/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.util;

import java.util.*;

/**
 *  Helper class to get over inadequacies of ResourceBundle Scheme.
 *  The inadequacy of the standard resource bundle is that only allows you
 *  to search two locales for the values, the specified locale and the default 
 *  locale. As when a web brower makes a request it can specify several locales
 *  (more than 2)
 *  in order of prefernce. This class allows that search to work.
 *  To work properly the default, top level resource bundle must be
 *  empty and the "en" locale must be complete.
 *
 * @author  jrm
 */
public class ResourceBundleHelper
{
    private static Hashtable instances = new Hashtable();
    
    private class AcceptableLocale
    {
        Locale locale;
        double q;
    }

    private class AcceptableLocaleComparator implements Comparator
    {
        public int compare(Object o1, Object o2)
        {
            if ( o1 == null )
                if ( o2 == null )
                    return 0;
                else
                    return 1;
            if ( o2 == null )
                return -1;
            
            if ( !(o1 instanceof AcceptableLocale) || !(o2 instanceof AcceptableLocale) )
                return 0;
            AcceptableLocale a1, a2;
            a1 = (AcceptableLocale)o1;
            a2 = (AcceptableLocale)o2;
            
            if ( a1.q < a2.q )
                return 1;
            if ( a1.q > a2.q )
                return -1;
            return 0;
        }
        
        public boolean equals( Object o )
        {
            return  o instanceof AcceptableLocaleComparator;
        }
    }

    String basename;
    private AcceptableLocale[] locales_in_order;
    
    
    /** Creates a new instance of ResourceBundleHelper */
    private ResourceBundleHelper( String basename, String accepts )
    {
        this.basename = basename;
        StringTokenizer tok = new StringTokenizer( accepts, "," );
        locales_in_order = new AcceptableLocale[ tok.countTokens() + 1 ];
        StringTokenizer spec, lspec;
        String qspec;
        int count, i;
        
        for ( i=0; tok.hasMoreTokens(); i++ )
        {
            locales_in_order[i] = new AcceptableLocale();
            
            spec = new StringTokenizer( tok.nextToken(), " ;" );
            lspec = new StringTokenizer( spec.nextToken(), "-" );
            count = lspec.countTokens();
            
            if ( count < 1 || count > 3 )
                throw new IllegalArgumentException( "Language spec must have one to three parts." );

            if ( count == 1 )
                locales_in_order[i].locale = new Locale( lspec.nextToken() );
            if ( count == 2 )
                locales_in_order[i].locale = new Locale( lspec.nextToken(), lspec.nextToken() );
            if ( count == 3 )
                locales_in_order[i].locale = new Locale( lspec.nextToken(), lspec.nextToken(), lspec.nextToken() );
            
            locales_in_order[i].q = 1.0;
            if ( spec.hasMoreTokens() )
            {
                qspec = spec.nextToken();
                if ( qspec.startsWith( "q=" ) )
                {
                    locales_in_order[i].q = Double.parseDouble( qspec.substring( 2 ) );
                }
            }
        }
        
        
        locales_in_order[i] = new AcceptableLocale();
        locales_in_order[i].locale = new Locale( "en" );
        locales_in_order[i].q = 0.0000000001;
        
        Arrays.sort( locales_in_order, new ResourceBundleHelper.AcceptableLocaleComparator() );
    }
    
    public static ResourceBundleHelper getResourceBundleHelper( String basename, String accepts )
    {
        synchronized ( instances )
        {
            if ( accepts == null )
                return getResourceBundleHelper( basename, "" );

            ResourceBundleHelper helper = (ResourceBundleHelper)instances.get( basename + ":" + accepts );
            if ( helper != null )
                return helper;

            try
            {
                helper = new ResourceBundleHelper( basename, accepts );
            }
            catch ( Exception e )
            {
                return null;
            }

            instances.put( basename + ":" + accepts, helper );
            
            return helper;
        }
    }
    

    public String getString( String key )
    {
        ResourceBundle bundle;
        String value;
        for ( int i=0; i<locales_in_order.length; i++ )
        {
            if ( locales_in_order[i] == null )
                continue;
            
            if ( locales_in_order[i].locale == null )
                continue;
            
            bundle = ResourceBundle.getBundle( basename, locales_in_order[i].locale );
            if ( bundle == null )
                continue;
            
            if ( !locales_in_order[i].locale.equals( bundle.getLocale() ) )
                continue;
            
            value = bundle.getString( key );
            if ( value == null )
                continue;
            
            return value;
        }
        
        return null;
    }
    
    public Locale getTopLocale()
    {
        return locales_in_order[0].locale;
    }
}
