/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2007 Phex Development Group
 *
 *  Modified by (c) 2004-2008 heavy_baby
 *  heavy_baby@users.sourceforge.jp
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- SVN Information ---
 *  $Id: Ip2CountryManager.java 3859 2007-07-01 20:15:19Z gregork $
 */
package phex.common;

import java.io.BufferedReader;
/* cabos */
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
//import java.io.InputStream;
//import java.io.InputStreamReader;
//import java.util.ArrayList;
//import java.util.Collections;
//import java.util.List;
import com.sun.java.util.collections.*;

import phex.common.address.AddressUtils;
//import phex.common.address.IpAddress;
import phex.utils.IOUtil;
//import phex.utils.NLogger;
//import phex.utils.NLoggerNames;

/**
 * 
 */
//public class Ip2CountryManager extends AbstractManager
public class Ip2CountryManager
{   
    /**
     * Indicates if the database is fully loaded or not.
     */
//    private boolean isLoaded;
//    private List<IpCountryRange> ipCountryRangeList;
    private List ipCountryRangeList;
    
    

    private Ip2CountryManager()
    {
//        isLoaded = false;
//        ipCountryRangeList = new ArrayList<IpCountryRange>();
        ipCountryRangeList = new ArrayList();
/* cabos */
        loadIp2CountryDB();
    }

    static private class Holder
    {
       static protected final Ip2CountryManager manager = new Ip2CountryManager();
    }

    static public Ip2CountryManager getInstance()
    {
        return Ip2CountryManager.Holder.manager;
    }

    /* cabos */
	public String getIP2Language(String ip, String defaultLocale) {
        if (ip == null || ip.equals("")) return defaultLocale;

        byte[] address = AddressUtils.parseIP(ip);
        if (address == null) return defaultLocale;
        
        IpCountryRange range = binarySearch( address );
        return (range != null) ? range.countryCode : defaultLocale;
    }
    
    /**
     * This method is called in order to initialize the manager.  Inside
     * this method you can't rely on the availability of other managers.
     * @return true if initialization was successful, false otherwise.
     */
/*
    public boolean initialize()
    {
        return true;
    }
*/

    /**
     * This method is called in order to perform post initialization of the
     * manager. This method includes all tasks that must be done after initializing
     * all the several managers. Inside this method you can rely on the
     * availability of other managers.
     * @return true if initialization was successful, false otherwise.
     */
/*
    public boolean onPostInitialization()
    {
        return true;
    }
*/
    
    /**
     * This method is called after the complete application including GUI completed
     * its startup process. This notification must be used to activate runtime
     * processes that needs to be performed once the application has successfully
     * completed startup.
     */
/*
    public void startupCompletedNotify()
    {
        Runnable runnable = new Runnable()
        {
            public void run()
            {
                loadIp2CountryDB();
            }
        };
        ThreadPool.getInstance().addJob( runnable, "IP2CountryLoader" );
    }
*/

    /**
     * This method is called in order to cleanly shutdown the manager. It
     * should contain all cleanup operations to ensure a nice shutdown of Phex.
     */
/*
    public void shutdown(){}
*/
    
    /**
     * Returns the country code if found, empty string if not found, and null
     * if DB has not been loaded yet.
     * @param address
     * @return
     */
/*
    public String getCountryCode( IpAddress address )
    {
        if ( !isLoaded )
        {
            return null;
        }
        IpCountryRange range = binarySearch( address.getHostIP() );
        if ( range == null )
        {
            return "";
        }
        return range.countryCode;
    }
*/
    
    private void loadIp2CountryDB()
    {
/*
        InputStream inStream = ClassLoader.getSystemResourceAsStream(
            "phex/resources/ip2country.csv" );
        if ( inStream == null )
        {
        	NLogger.debug( Ip2CountryManager.class,
                "Default GWebCache file not found." );
            return;
        }
        BufferedReader reader = new BufferedReader( new InputStreamReader( inStream ) );
*/
        BufferedReader reader;
        try {
            reader = new BufferedReader( new FileReader( "ip2language.csv" ) );
        } catch (Exception e) {
            return;
        }
        
//        ArrayList<IpCountryRange> initialList = new ArrayList<IpCountryRange>( 5000 );
        ArrayList initialList = new ArrayList( 5000 );
        IpCountryRange range;
        String line;
        try
        {
            line = reader.readLine();
            while( line != null )
            {
                range = new IpCountryRange( line );
                initialList.add( range );
                line = reader.readLine();
            }
        }
        catch (IOException exp)
        {
//            NLogger.error( NLoggerNames.GLOBAL, exp, exp );
        }
        finally
        {
            IOUtil.closeQuietly(reader);
        }
        initialList.trimToSize();
        Collections.sort( initialList );
        ipCountryRangeList = Collections.unmodifiableList( initialList );
//        isLoaded = true;
    }
    
    private IpCountryRange binarySearch( byte[] hostIp )
    {
        int low = 0;
        int high = ipCountryRangeList.size() - 1;
    
        while (low <= high)
        {
            int mid = (low + high) >> 1;
//            IpCountryRange midVal = ipCountryRangeList.get( mid );
            IpCountryRange midVal = (IpCountryRange)ipCountryRangeList.get( mid );
            int cmp = midVal.compareHostAddress( hostIp );
            if (cmp < 0)
            {
                low = mid + 1;
            }
            else if (cmp > 0)
            {
                high = mid - 1;
            }
            else
            {
                return midVal; // key found
            }
        }
        return null;  // key not found
    }
    
//    private static class IpCountryRange implements Comparable<IpCountryRange>
    private static class IpCountryRange implements com.sun.java.util.collections.Comparable
    {
        byte[] from;
        byte[] to;
        String countryCode;
        
        public IpCountryRange( String line )
        {
            // "33996344","33996351","GB"
            int startIdx, endIdx;
            startIdx = 0;
            
            endIdx = line.indexOf( ',', startIdx );
            from = AddressUtils.parseIntIP( line.substring( startIdx, endIdx ) );
            
            startIdx = endIdx + 1;
            endIdx = line.indexOf( ',', startIdx );
            to = AddressUtils.parseIntIP( line.substring( startIdx, endIdx ) );
            
            startIdx = endIdx + 1;
            String subCode = line.substring( startIdx );
            // take the internal string representation of the country code to 
            // save memory...
            countryCode = subCode.intern();
        }
        
        public int compareHostAddress( byte[] hostIp )
        {
            long hostIpL;
            hostIpL = IOUtil.unsignedInt2Long(
                IOUtil.deserializeInt( hostIp, 0));
            long fromIpL = IOUtil.unsignedInt2Long( IOUtil.deserializeInt( from, 0 ) );
            long cmp = hostIpL - fromIpL;
            if ( cmp == 0 )
            {
                return 0;
            }
            if ( cmp < 0 )
            {// host Ip is lower..
                return 1;
            }
            
            // validate to range..
            long toIpL = IOUtil.unsignedInt2Long( IOUtil.deserializeInt( to, 0 ) );
            cmp = hostIpL - toIpL;
            if ( cmp == 0 || cmp < 0)
            {// we are between from and to
                return 0;
            }
            else
            {// host Ip is higher..
                return -1;
            }
        }
        
//        public int compareTo(IpCountryRange range)
        public int compareTo(Object range)
        {
            if ( range == this )
            {
                return 0;
            }            
            byte[] ip1 = from;
//            byte[] ip2 = range.from;
            byte[] ip2 = ((IpCountryRange)range).from;

            long ip1l = IOUtil.unsignedInt2Long( IOUtil.deserializeInt( ip1, 0 ) );
            long ip2l = IOUtil.unsignedInt2Long( IOUtil.deserializeInt( ip2, 0 ) );

            if ( ip1l < ip2l )
            {
                return -1;
            }
            // only if rate and object is equal return 0
            else
            {
                return 1;
            }
        }
    }
}
