package org.maachang.comet.conf ;

import java.net.InetAddress;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.MaachangDef;
import org.maachang.comet.ServiceDef;
import org.maachang.comet.httpd.engine.auth.HttpdAuthManager;
import org.maachang.comet.httpd.engine.script.cache.CacheScriptManager;
import org.maachang.conf.ConvIniParam;
import org.maachang.dao.DaoSessionFactory;
import org.maachang.dao.InitDao;
import org.maachang.dao.dbms.RecordFactory;
import org.maachang.dbm.MDbmManager;
import org.maachang.dbm.service.MDbmServerService;
import org.maachang.manager.GlobalManager;
import org.maachang.util.FileUtil;

/**
 * コンフィグ読み込み開始オブジェクト.
 *
 * @version 2007/09/03
 * @author  masahito suzuki
 * @since   MaachangComet 1.00
 */
public class StartupConfig {
    
    /**
     * LOG.
     */
    private static final Log LOG = LogFactory.getLog( StartupConfig.class ) ;
    
    /**
     * レコードセクション名.
     */
    private static final String RECORD_SECTION = "dbms" ;
    
    /**
     * MDBMセクション名.
     */
    private static final String MDBM_SECTION = "mdbm" ;
    
    /**
     * コンフィグファイル拡張子.
     */
    private static final String CONF = ".conf" ;
    
    /**
     * コンストラクタ.
     */
    private StartupConfig() {
        
    }
    
    /**
     * コンフィグ情報を初期化.
     * <BR><BR>
     * コンフィグ情報を初期化します.
     * <BR>
     * @param path 対象のパスを設定します.
     * @exception Exception 例外.
     */
    public static final void init( String path )
        throws Exception {
        // 初期化内容.
        // IniFile.
        // Mime.
        // packages.
        // Record.
        // Users(DB/File).
        // Auth.
        // CacheScriptManager.
        if( path == null || ( path = path.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        String basePath = getPath( path ) ;
        path = basePath+MaachangDef.DIRECTORY_CONFIG ;
        path = FileUtil.getFullPath( path )+FileUtil.FILE_SPACE ;
        
        BaseConfig conf = getBaseConfig( path ) ;
        GlobalManager nm = GlobalManager.getInstance() ;
        
        // MimeTypeを取得.
        LOG.info( "@@@ MimeType ... [START]" ) ;
        String mime = path+conf.getMimes() ;
        if( mime.endsWith( CONF ) == false ) {
            mime += CONF ;
        }
        // Mimeを登録.
        MimeConfig mimeType = new MimeConfig() ;
        mimeType.open( mime ) ;
        nm.put( ServiceDef.MANAGER_BY_MIME_TYPE,mimeType ) ;// Mime.
        LOG.info( "@@@ MimeType ... [END]" ) ;
        
        // Mailを取得.
        LOG.info( "@@@ Mail-Config ... [START]" ) ;
        String mail = path+conf.getMail() ;
        if( mail.endsWith( CONF ) == false ) {
            mail += CONF ;
        }
        // Mailを登録.
        MailConfig mailConfig = new MailConfig() ;
        mailConfig.open( mail ) ;
        nm.put( ServiceDef.MANAGER_BY_MAIL_CONFIG,mailConfig ) ;// Mail.
        LOG.info( "@@@ Mail-Config ... [END]" ) ;
        
        // packages情報を取得.
        LOG.info( "@@@ packages ... [START]" ) ;
        String packages = path+conf.getPackages() ;
        if( packages.endsWith( CONF ) == false ) {
            packages += CONF ;
        }
        // packagesを登録.
        PackagesConfig packagesConf = new PackagesConfig() ;
        packagesConf.open( packages ) ;
        nm.put( ServiceDef.MANAGER_BY_PACKAGES,packagesConf ) ;// packages.
        LOG.info( "@@@ packages ... [END]" ) ;
        
        // 拡張コンフィグファイルを取得.
        LOG.info( "@@@ read-Conf ... [START]" ) ;
        IniFile config = getEnhancingConfigs( conf,path ) ;
        //int len = conf.size() ;
        //IniFile config = new IniFile() ;
        //for( int i = 0 ; i < len ; i ++ ) {
        //    String name = conf.getConfig( i ) ;
        //    if( name == null || ( name = name.trim() ).length() <= 0 ) {
        //        continue ;
        //    }
        //    LOG.info( "--- read-Conf ... ["+name+"]" ) ;
        //    readConfig( config,path,name ) ;
        //}
        // 拡張コンフィグを登録.
        nm.put( ServiceDef.MANAGER_BY_CONFIG,config ) ;// IniFile.
        LOG.info( "@@@ read-Conf ... [END]" ) ;
        
        // レコードファクトリを登録.
        LOG.info( "@@@ Record ... [START]" ) ;
        RecordFactory recordFactory = getRecordFactory( true,config ) ;
        
        // MDBMを生成.
        LOG.info( "@@@ mdbm ... [START]" ) ;
        createMDbm( true,config ) ;
        LOG.info( "@@@ mdbm ... [END]" ) ;
        
        // レコードファクトリを登録.
        if( recordFactory != null ) {
            nm.put( ServiceDef.MANAGER_BY_DBMS_POOL,recordFactory ) ;// Record.
        }
        LOG.info( "@@@ Record ... [END]" ) ;
        
        // キャッシュスクリプトマネージャを登録.
        LOG.info( "@@@ cacheScript ... [START]" ) ;
        CacheScriptManager cacheScriptManager = getCacheScriptManager( config ) ;
        
        // キャッシュスクリプトマネージャを登録.
        if( cacheScriptManager != null ) {
            nm.put( ServiceDef.SCRIPT_CACHE_MANAGER,cacheScriptManager ) ;// CacheScriptManager.
        }
        LOG.info( "@@@ cacheScript ... [END]" ) ;
        
        // 認証情報を取得.
        LOG.info( "@@@ AuthUsers ... [START]" ) ;
        String auth = conf.getUsers() ;
        if( auth != null && ( auth = auth.trim() ).length() > 0 ) {
            if( auth.endsWith( CONF ) == false ) {
                auth += CONF ;
            }
            AuthUsersByConfig authUser = new AuthUsersByConfig() ;
            authUser.open( path+auth ) ;
            // 認証コンフィグを登録.
            nm.put( ServiceDef.MANAGER_BY_USERS,authUser ) ;// Users(file).
            LOG.info( "@@@ AuthUsers ... [ByFile]" ) ;
        }
        // DBでの認証コンフィグを生成.
        else {
            auth = conf.getDbUsers() ;
            if( auth != null && ( auth = auth.trim() ).length() > 0 ) {
                if( auth.endsWith( CONF ) == false ) {
                    auth += CONF ;
                }
                DbAuthUsers dbauth = new DbAuthUsers() ;
                dbauth.open( path+auth ) ;
                // DB認証用オブジェクトを生成.
                AuthUserByDatabase authUser = new AuthUserByDatabase(
                    recordFactory,
                    dbauth.getTable(),
                    dbauth.getUserColumn(),
                    dbauth.getPasswdColumn(),
                    dbauth.getReloadTime() ) ;
                
                // 認証コンフィグを登録.
                nm.put( ServiceDef.MANAGER_BY_USERS,authUser ) ;// Users(DB).
                LOG.info( "@@@ AuthUsers ... [ByDB]" ) ;
            }
        }
        if( nm.get( ServiceDef.MANAGER_BY_USERS ) == null ) {
            LOG.info( "@@@ AuthUsers ... [NOT]" ) ;
        }
        LOG.info( "@@@ AuthUsers ... [END]" ) ;
        // 認証コンフィグが登録されている場合.
        LOG.info( "@@@ AuthManager ... [START]" ) ;
        if( nm.get( ServiceDef.MANAGER_BY_USERS ) != null ) {
            // 認証マネージャを生成.
            //String publicPath = basePath + MaachangDef.DIRECTORY_PUBLIC ;
            String publicPath = basePath + MaachangDef.DIRECTORY_APPLICATION ;// application直下に設定.
            publicPath = FileUtil.getFullPath( publicPath )+FileUtil.FILE_SPACE ;
            HttpdAuthManager authManager = new HttpdAuthManager() ;
            authManager.create(  publicPath  ) ;
            nm.put( ServiceDef.MANAGER_BY_AUTH,authManager ) ;// Auth.
            LOG.info( "@@@ AuthManager ... [END]" ) ;
        }
        else {
            LOG.info( "@@@ AuthManager ... [NOT]" ) ;
        }
    }
    
    /**
     * コンフィグ情報を破棄.
     * <BR><BR>
     * コンフィグ情報を破棄します.
     */
    public static final void destroy() {
        // GlobalManagerを取得.
        GlobalManager nm = GlobalManager.getInstance() ;
        
        // MDBMを破棄.
        LOG.info( "@@@ (Destroy) mdbm ... [START]" ) ;
        MDbmManager.getInstance().close() ;
        LOG.info( "@@@ (Destroy) mdbm ... [END]" ) ;
        
        // レコードファクトリを破棄.
        LOG.info( "@@@ (Destroy) Record ... [START]" ) ;
        RecordFactory recordFactory = ( RecordFactory )nm.get( ServiceDef.MANAGER_BY_DBMS_POOL ) ;
        if( recordFactory != null ) {
            try {
                recordFactory.clear() ;
            } catch( Exception e ) {
                LOG.error( "error",e ) ;
            }
        }
        LOG.info( "@@@ (Destroy) Record ... [END]" ) ;
    }
    
    /**
     * パス情報を変換.
     * <BR><BR>
     * パス情報を変換します.
     * <BR>
     * @param path 対象のパスを設定します.
     * @return String パス情報が返されます.
     * @exception Exception 例外.
     */
    public static final String getPath( String path )
        throws Exception {
        if( path.endsWith( "\\" ) == false && path.endsWith( "/" ) == false ) {
            path += "/" ;
        }
        path = FileUtil.getFullPath( path )+FileUtil.FILE_SPACE ;
        return path ;
    }
    
    /**
     * ベースコンフィグをオープン.
     * <BR><BR>
     * ベースコンフィグをオープンします.
     * <BR>
     * @param path 対象のパスを設定します.
     * @return BaseConfig ベースコンフィグが返されます.
     * @exception Exception 例外.
     */
    public static final BaseConfig getBaseConfig( String path )
        throws Exception {
        BaseConfig conf = new BaseConfig() ;
        conf.open( path+"base.conf" ) ;
        return conf ;
    }
    
    /**
     * 指定コンフィグ情報を取得.
     * <BR><BR>
     * 指定コンフィグ情報を取得します.
     * <BR>
     * @param out コンフィグを格納するオブジェクトを設定します.
     * @param path 対象のパスを設定します.
     * @param name オープンコンフィグ情報を設定します.
     * @return boolean [true]の場合、読み込み成功です.
     * @exception Exception 例外.
     */
    public static final boolean readConfig( IniFile out,String path,String name )
        throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            return false ;
        }
        if( name.endsWith( CONF ) == false ) {
            name += CONF ;
        }
        out.addOpen( path+name ) ;
        return true ;
    }
    
    /**
     * 拡張コンフィグ情報を読み込み.
     * <BR><BR>
     * 基本コンフィグを設定して拡張コンフィグを取得します.
     * <BR>
     * @param base 基本コンフィグ情報を設定します.
     * @param path 対象のパスを設定します.
     * @return IniFile 拡張コンフィグ内容が返されます.
     * @exception Exception 例外.
     */
    public static final IniFile getEnhancingConfigs( BaseConfig base,String path )
        throws Exception {
        int len = base.size() ;
        IniFile ret = new IniFile() ;
        for( int i = 0 ; i < len ; i ++ ) {
            String name = base.getConfig( i ) ;
            if( name == null || ( name = name.trim() ).length() <= 0 ) {
                continue ;
            }
            readConfig( ret,path,name ) ;
        }
        return ret ;
    }
    
    /**
     * レコードファクトリを取得.
     * <BR><BR>
     * レコードファクトリを取得します.
     * <BR>
     * @param config 対象のコンフィグ情報を設定します.
     * @return RecordFactory レコードファクトリが返されます.
     * @exception Exception 例外.
     */
    public static final RecordFactory getRecordFactory( boolean mode,IniFile config )
        throws Exception {
        if( ConvIniParam.getBoolean( config.get( RECORD_SECTION,"flag",0 ) ) == true ) {
            boolean debug = ConvIniParam.getBoolean( config.get( RECORD_SECTION,"debug",0 ) ) ;
            String adapter = config.get( RECORD_SECTION,"adapter",0 ) ;
            String driver = config.get( RECORD_SECTION,"driver",0 ) ;
            String url = config.get( RECORD_SECTION,"url",0 ) ;
            String max = config.get( RECORD_SECTION,"max",0 ) ;
            String checksql = config.get( RECORD_SECTION,"checksql",0 ) ;
            String user = config.get( RECORD_SECTION,"user",0 ) ;
            //String passwd = config.get( RECORD_SECTION,"passwd",0 ) ;
            String autoCommit = config.get( RECORD_SECTION,"autoCommit",0 ) ;
            if( mode == true ) {
                if( LOG.isInfoEnabled() ) {
                    LOG.info( "*** connect database [START]" ) ;
                    LOG.info( "debug:" + debug ) ;
                    LOG.info( "adapter:" + adapter ) ;
                    LOG.info( "driver:" + driver ) ;
                    LOG.info( "url:" + url ) ;
                    LOG.info( "max:" + max ) ;
                    LOG.info( "checksql:" + checksql ) ;
                    if( user != null && user.trim().length() > 0 ) {
                        LOG.info( "user:" + user ) ;
                    }
                    //LOG.info( "passwd:" + passwd ) ;
                    LOG.info( "autoCommit:" + autoCommit ) ;
                    LOG.info( "*** connect database [END]" ) ;
                }
            }
            else {
                System.out.println( "*** connect database [START]" ) ;
                System.out.println( "debug:" + debug ) ;
                System.out.println( "adapter:" + adapter ) ;
                System.out.println( "driver:" + driver ) ;
                System.out.println( "url:" + url ) ;
                System.out.println( "max:" + max ) ;
                System.out.println( "checksql:" + checksql ) ;
                if( user != null && user.trim().length() > 0 ) {
                    System.out.println( "user:" + user ) ;
                }
                //System.out.println( "passwd:" + passwd ) ;
                System.out.println( "autoCommit:" + autoCommit ) ;
                System.out.println( "*** connect database [END]" ) ;
            }
            InitDao.init( config.getConfig() ) ;
            return DaoSessionFactory.getInstance().getRecordFactory() ;
        }
        if( mode == true ) {
            LOG.info( "*** RDBMSコネクションは無効設定です..." ) ;
        }
        else {
            System.out.println( "*** RDBMSコネクションは無効設定です..." ) ;
        }
        return null ;
    }
    
    /**
     * MDBMオブジェクトを作成.
     * <BR><BR>
     * MDBMオブジェクトを作成します.
     * <BR>
     * @param mode [true]の場合、MDBMを外部接続可能にします.
     * @param config 対象のコンフィグ情報を設定します.
     * @exception Exception 例外.
     */
    public static final void createMDbm( boolean mode,IniFile config )
        throws Exception {
        String name = config.get( MDBM_SECTION,"directory",0 ) ;
        boolean flag = false ;
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            name = "mdbm" ;
            flag = false ;
        }
        else {
            flag = ConvIniParam.getBoolean( config.get( MDBM_SECTION,"flag",0 ) ) ;
        }
        MDbmManager.getInstance().open( false,name ) ;
        if( mode == true && flag == true ) {
            InetAddress addr = ConvIniParam.getInetAddress( config.get( MDBM_SECTION,"bind-addr",0 ) ) ;
            int port = ConvIniParam.getInt( config.get( MDBM_SECTION,"bind-port",0 ) ) ;
            int max = ConvIniParam.getInt( config.get( MDBM_SECTION,"max",0 ) ) ;
            int pool = ConvIniParam.getInt( config.get( MDBM_SECTION,"pool",0 ) ) ;
            if( port <= -1 ) {
                port = -1 ;
            }
            MDbmServerService server = new MDbmServerService( addr,port,max,pool ) ;
            MDbmManager.getInstance().setServer( server ) ;
            if( mode == true ) {
                LOG.info( "*** MDBM Option ***" ) ;
                LOG.info( " bindAddr :" + server.getInetAddress().getHostAddress() ) ;
                LOG.info( " bindPort :" + server.getLocalPort() ) ;
            }
            else {
                System.out.println( "*** MDBM Option ***" ) ;
                System.out.println( " bindAddr :" + server.getInetAddress().getHostAddress() ) ;
                System.out.println( " bindPort :" + server.getLocalPort() ) ;
            }
        }
        else {
            if( mode == true ) {
                LOG.info( "*** MDBMサーバ接続は無効設定です..." ) ;
            }
            else {
                System.out.println( "*** MDBMサーバ接続は無効設定です..." ) ;
            }
        }
    }
    
    /**
     * キャッシュスクリプト情報を生成.
     * <BR><BR>
     * キャッシュスクリプト情報を生成します.
     * <BR>
     * @param config 対象のコンフィグ情報を設定します.
     * @return CacheScriptManager キャッシュスクリプトマネージャが返されます.
     * @exception Exception 例外.
     */
    public static final CacheScriptManager getCacheScriptManager( IniFile config )
        throws Exception {
        CacheScriptManager cman = new CacheScriptManager() ;
        String[] names = config.getAll( "cacheScript","script" ) ;
        if( names != null && names.length > 0 ) {
            int len = names.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                String name = names[ i ] ;
                if( name != null && ( name = name.trim() ).length() > 0 ) {
                    cman.add( name ) ;
                }
            }
        }
        return cman ;
    }
}
