package org.maachang.proxy.engine.mobile.carrier ;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;
import org.maachang.util.conf.Config;
import org.maachang.util.conf.ReadIni;

/**
 * Mobileコンフィグ.
 *
 * @version 2008/04/08
 * @author  masahito suzuki
 * @since   MaachangProxy 1.01
 */
public class MobileConfig {
    
    /**
     * Docomo:ユーザエージェント閾値.
     */
    private static final int DOCOMO_USER_AGENT_POS = 11 ;
    
    /**
     * コンフィグディレクトリ.
     */
    private static final String CONF_DIR = "./conf/carrier/" ;
    
    /**
     * コンフィグ拡張子.
     */
    private static final String CONF_PLUS = ".conf" ;
    
    /**
     * 再読み込み間隔.
     */
    private static final long RELOAD_TIME = 5000L ;
    
    /**
     * セクション名.
     */
    private String sectionName = null ;
    
    /**
     * ファイル日付.
     */
    private long lastTime = -1L ;
    
    /**
     * 前回アクセスタイム.
     */
    private long beforeTime = -1L ;
    
    /**
     * モバイルデータ群.
     */
    private HashMap<Integer,ArrayList<MobileData>> datas = null ;
    
    /**
     * Sha1.
     */
    private String sha1 = null ;
    
    /**
     * コンストラクタ.
     */
    private MobileConfig() {
        
    }
    
    /**
     * コンストラクタ.
     * @param sectionName 対象のセクション名を設定します.
     */
    public MobileConfig( String sectionName ) {
        this.sectionName = sectionName ;
        beforeTime = -1 ;
    }
    
    /**
     * 対象コンフィグファイルを読み込む.
     */
    private void read() {
        if( beforeTime + RELOAD_TIME <= System.currentTimeMillis() ) {
            try {
                String fileName = new StringBuilder().append( CONF_DIR ).
                    append( sectionName ).append( CONF_PLUS ).toString() ;
                boolean flg = false ;
                if( datas == null ) {
                    flg = true ;
                }
                else {
                    long t = FileUtil.getLastTime( fileName ) ;
                    if( t != lastTime ) {
                        lastTime = t ;
                        flg = true ;
                    }
                }
                if( flg == true ) {
                    BufferedReader br = new BufferedReader( new InputStreamReader(
                        new FileInputStream( fileName ),"UTF8" ) ) ;
                    Config conf = null ;
                    try {
                        conf = new Config() ;
                        ReadIni.analisys( conf,br ) ;
                        br.close() ;
                        br = null ;
                    } finally {
                        if( br != null ) {
                            try {
                                br.close() ;
                            } catch( Exception e ) {
                            }
                        }
                    }
                    if( lastTime <= -1L ) {
                        lastTime = FileUtil.getLastTime( fileName ) ;
                    }
                    analysisDatas( conf ) ;
                    beforeTime = System.currentTimeMillis() ;
                }
            } catch( Exception e ) {
                datas = null ;
                sha1 = null ;
                lastTime = -1L ;
                beforeTime = -1L ;
            }
        }
    }
    
    /**
     * コンフィグを解析してデータ群を生成.
     */
    private void analysisDatas( Config conf )
        throws Exception {
        datas = new HashMap<Integer,ArrayList<MobileData>>() ;
        int len = conf.size( sectionName,"agent" ) ;
        if( len > 0 ) {
            for( int i = 0 ; i < len ; i ++ ) {
                String code = conf.get( sectionName,"code",i ) ;
                if( code.indexOf( "/" ) > 0 ) {
                    ArrayList<String> cds = StringUtil.cutString( code,"/" ) ;
                    int lenJ = cds.size() ;
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        String target = cds.get( j ) ;
                        String agent = conf.get( sectionName,"agent",i ) ;
                        agent = StringUtil.changeString( agent,code,target ) ;
                        agent = smartAgentName( sectionName,agent ) ;
                        int pos = getIndex( sectionName,agent ) ;
                        MobileData data = new MobileData() ;
                        data.setCarrier( sectionName ) ;
                        data.setUserAgent( agent ) ;
                        data.setCode( target ) ;
                        data.setType( conf.get( sectionName,code+".type",0 ) ) ;
                        data.setX( conf.getInt( sectionName,code+".x",0 ) ) ;
                        data.setY( conf.getInt( sectionName,code+".y",0 ) ) ;
                        data.setJpegFlag( conf.getBoolean( sectionName,code+".jpg",0 ) ) ;
                        data.setGifFlag( conf.getBoolean( sectionName,code+".gif",0 ) ) ;
                        data.setPngFlag( conf.getBoolean( sectionName,code+".png",0 ) ) ;
                        data.setFlashFlag( conf.getBoolean( sectionName,code+".flash",0 ) ) ;
                        Integer key = new Integer( pos ) ;
                        ArrayList<MobileData> lst = datas.get( key ) ;
                        if( lst == null ) {
                            lst = new ArrayList<MobileData>() ;
                            datas.put( key,lst ) ;
                        }
                        lst.add( data ) ;
                    }
                }
                else {
                    String agent = conf.get( sectionName,"agent",i ) ;
                    agent = smartAgentName( sectionName,agent ) ;
                    int pos = getIndex( sectionName,agent ) ;
                    MobileData data = new MobileData() ;
                    data.setCarrier( sectionName ) ;
                    data.setUserAgent( agent ) ;
                    data.setCode( code ) ;
                    data.setType( conf.get( sectionName,code+".type",0 ) ) ;
                    data.setX( conf.getInt( sectionName,code+".x",0 ) ) ;
                    data.setY( conf.getInt( sectionName,code+".y",0 ) ) ;
                    data.setJpegFlag( conf.getBoolean( sectionName,code+".jpg",0 ) ) ;
                    data.setGifFlag( conf.getBoolean( sectionName,code+".gif",0 ) ) ;
                    data.setPngFlag( conf.getBoolean( sectionName,code+".png",0 ) ) ;
                    data.setFlashFlag( conf.getBoolean( sectionName,code+".flash",0 ) ) ;
                    Integer key = new Integer( pos ) ;
                    ArrayList<MobileData> lst = datas.get( key ) ;
                    if( lst == null ) {
                        lst = new ArrayList<MobileData>() ;
                        datas.put( key,lst ) ;
                    }
                    lst.add( data ) ;
                }
            }
            sha1 = conf.get( sectionName,"sha1",0 ) ;
        }
    }
    
    /**
     * ユーザエージェントを最適化.
     */
    private String smartAgentName( String carrier,String agent ) {
        if( "softbank".equals( carrier ) ) {
            int pos = agent.indexOf( "/",0 ) ;
            if( pos <= -1 ) {
                return agent ;
            }
            pos = agent.indexOf( "/",pos+1 ) ;
            if( pos <= -1 ) {
                return agent ;
            }
            pos = agent.indexOf( "/",pos+1 ) ;
            if( pos <= -1 ) {
                return agent ;
            }
            return agent.substring( 0,pos ) ;
        }
        int index = 0 ;
        int p = -1 ;
        for( ;; ) {
            p = agent.indexOf( " ",index ) ;
            if( p <= -1 ) {
                return agent ;
            }
            if( "docomo".equals( carrier ) && p < DOCOMO_USER_AGENT_POS ) {
                index = p + 1 ;
                continue ;
            }
            break ;
        }
        return agent.substring( 0,p ) ;
    }
    
    /**
     * 最短のコード値を取得.
     */
    private int getIndex( String carrier,String userAgent ) {
        if( "softbank".equals( carrier ) ) {
            int pos = userAgent.indexOf( "/",0 ) ;
            if( pos <= -1 ) {
                return userAgent.length() ;
            }
            pos = userAgent.indexOf( "/",pos+1 ) ;
            if( pos <= -1 ) {
                return userAgent.length() ;
            }
            return pos ;
        }
        else {
            int index = 0 ;
            int pos = -1 ;
            pos = userAgent.indexOf( " ",index ) ;
            int pos2 = userAgent.indexOf( "/",index ) ;
            if( pos <= -1 || pos2 <= -1 ) {
                if( pos <= -1 ) {
                    pos = pos2 ;
                }
            }
            else if( pos > pos2 ) {
                pos = pos2 ;
            }
            if( pos <= -1 ) {
                pos = userAgent.length() ;
            }
            return pos ;
        }
    }
    
    /**
     * Sha1を取得.
     * @return String Sha1が返されます.
     */
    public synchronized String getSha1() {
        read() ;
        if( datas == null ) {
            return null ;
        }
        return sha1 ;
    }
    
    /**
     * 指定ユーザエージェントから、機種データを取得.
     * @param carrier 対象のキャリア情報を設定します.
     * @param userAgent 対象のユーザエージェントを設定します.
     * @return String 対象のキャリアが返されます.<BR>
     *                [null]の場合は、PCです.
     */
    public synchronized MobileData getMobileData( String carrier,String userAgent ) {
        if( userAgent == null || ( userAgent = userAgent.trim() ).length() <= 0 ) {
            return null ;
        }
        read() ;
        if( datas == null ) {
            return null ;
        }
        int pos = getIndex( carrier,userAgent ) ;
        ArrayList<MobileData> lst = datas.get( new Integer( pos ) ) ;
        if( lst == null ) {
            return null ;
        }
        int len = lst.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            MobileData data = lst.get( i ) ;
            if( userAgent.startsWith( data.getUserAgent() ) ) {
                return data ;
            }
        }
        return null ;
    }
    
    /**
     * キー一覧を取得.
     * @return Object[] キー一覧が返されます.
     */
    public synchronized Object[] getKeys() {
        read() ;
        if( datas == null ) {
            return null ;
        }
        return datas.keySet().toArray() ;
    }
    
    /**
     * データ群一覧を取得.
     * @param key 対象のキーを設定します.
     * @return ArrayList<MobileData> 内容一覧が返されます.
     */
    public synchronized ArrayList<MobileData> getList( Object key ) {
        read() ;
        if( datas == null ) {
            return null ;
        }
        return datas.get( key ) ;
    }
}
