package org.maachang.rimdb ;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.maachang.rimdb.search.CacheSearch;
import org.maachang.rimdb.table.ConfTable;
import org.maachang.rimdb.table.TableImpl;
import org.maachang.rimdb.table.TableManager;
import org.maachang.rimdb.table.TableUtil;
import org.maachang.rimdb.util.Config;
import org.maachang.rimdb.util.FileUtil;

/**
 * テーブルファクトリ.
 * 
 * @version 2014/07/11
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
public final class TableFactory {
    
    /** バージョン. **/
    public static final int VERSION = 1 ;
    
    /** マイナーバージョン. **/
    public static final int MINOR_VERSION = 1 ;
    
    protected TableFactory() {}
    private static final TableFactory SNGL = new TableFactory() ;
    
    /**
     * ファクトリオブジェクトを取得.
     * @return TableFactory ファクトリオブジェクトが返却されます.
     */
    public static final TableFactory getInstance() {
        return SNGL ;
    }
    
    /** デフォルトrimdbコンフィグファイル名. **/
    private static final String DEF_CONF = "rimdb.conf" ;
    
    /** セクション名. **/
    private static final String SECTION = "rimdb" ;
    
    /** ベースコンフィグファイル名. **/
    private volatile String baseFileName ;
    
    /** ベースフォルダ. **/
    private volatile String baseFolder ;
    
    /** テーブル管理オブジェクト. **/
    private volatile TableManager manager ;
    
    /** テーブルコンフィグ管理オブジェクト. **/
    private volatile Map<String,ConfTable> confMap ;
    
    /** 検索キャッシュオブジェクト. **/
    private final CacheSearch cache = new CacheSearch() ;
    
    /**
     * 初期処理.
     * @param fileName ベースコンフィグファイル名を設定します.
     */
    public final void init( String fileName ) {
        if( fileName.endsWith( "\\" ) || fileName.endsWith( "/" ) ) {
            fileName += DEF_CONF ;
        }
        if( !FileUtil.isFile( fileName ) ) {
            throw new RimDbException( "指定されたファイル[" + fileName + "]は存在しません" ) ;
        }
        try {
            fileName = FileUtil.getFullPath( fileName ) ;
        } catch( Exception e ) {
            throw new RimDbException( e ) ;
        }
        this.baseFileName = fileName; 
        reload() ;
    }
    
    /**
     * 全データの再読み込み.
     */
    public final void reload() {
        if( baseFileName == null ) {
            return ;
        }
        Config conf ;
        try {
            conf = Config.read( baseFileName ) ;
        } catch( Exception e ) {
            throw new RimDbException( e ) ;
        }
        
        if( !conf.isSection( SECTION ) ) {
            throw new RimDbException( "指定ファイル[" + baseFileName +
                "]のConfigファイルは、RIMDB定義ではありません" ) ;
        }
        
        // テーブル定義が有効かチェック.
        Boolean useFlag = conf.getBoolean( SECTION,"use",0 ) ;
        if( useFlag != null && useFlag == false ) {
            
            // 無効の場合は、テーブル定義はクリア.
            confMap.clear() ;
            manager = null ;
            baseFileName = null ;
            baseFolder = null ;
            cache.clear() ;
            return ;
        }
        
        // ベースフォルダを取得.
        String folder = conf.getString( SECTION,"folder",0 ) ;
        if( folder == null || ( folder = folder.trim() ).length() <= 0 ) {
            int p = baseFileName.lastIndexOf( "/" ) ;
            if( p == -1 ) {
                folder = "/" ;
            }
            else {
                folder = baseFileName.substring( 0,p+1 ) ;
            }
        }
        else {
            try {
                folder = FileUtil.getFullPath( folder ) ;
            } catch( Exception e ) {
                throw new RimDbException( e ) ;
            }
        }
        
        // テーブルファイル名を取得.
        int len ;
        String[] names = conf.getAll( SECTION,"name" ) ;
        if( names == null || names.length == 0 ) {
            
            // 1件も存在しない場合は、存在しない条件として、セット.
            TableManager m = new TableManager() ;
            Map<String,ConfTable> mm = new ConcurrentHashMap<String,ConfTable>() ;
            baseFolder = folder ;
            confMap = mm ;
            manager = m ;
            return ;
        }
        else {
            
            // ファイル名として整形.
            len = names.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( !names[ i ].endsWith( ".conf" ) ) {
                    names[ i ] += ".conf" ;
                }
            }
        }
        
        // テーブルデータ読み込み.
        len = names.length ;
        ConfTable ct ;
        TableImpl t ;
        TableManager m = new TableManager() ;
        Map<String,ConfTable> mm = new ConcurrentHashMap<String,ConfTable>() ;
        for( int i = 0 ; i < len ; i ++ ) {
            ct = new ConfTable( folder,names[ i ] ) ;
            t = ct.createTable() ;
            m.set( t ) ;
            mm.put( t.getName(),ct ) ;
        }
        
        // 情報更新.
        baseFolder = folder ;
        confMap = mm ;
        manager = m ;
        
        // キャッシュクリア.
        cache.clear() ;
    }
    
    /**
     * 指定テーブルを再読み込み.
     * @param name 対象のテーブル名を設定します.
     */
    public final void reload( final String name ) {
        if( !isInit() ) {
            throw new RimDbException( "TableFactoryは初期化されていません" ) ;
        }
        ConfTable t = confMap.get( TableUtil.convertJavaNameByDBName( name ) ) ;
        if( t == null ) {
            throw new RimDbException( "指定テーブル名[" + name + "]は存在しません" ) ;
        }
        ConfTable cn = new ConfTable( t.baseFolder,t.confName ) ;
        TableImpl nt = cn.createTable() ;
        confMap.put( nt.getName(),cn ) ;
        manager.set( nt ) ;
        
        // キャッシュクリア.
        cache.clear() ;
    }
    
    /**
     * 初期処理が行われたかチェック.
     * @return boolean [true]の場合、初期処理は行われています.
     */
    public final boolean isInit() {
        return baseFileName != null ;
    }
    
    /**
     * 指定テーブルを取得.
     * @param name 対象のテーブル名を設定します.
     * @return Table 指定テーブルが返却されます.
     */
    public final Table get( final String name ) {
        TableManager m = manager ;
        if( !isInit() ) {
            throw new RimDbException( "TableFactoryは初期化されていません" ) ;
        }
        return m.get( TableUtil.convertJavaNameByDBName( name ) ) ;
    }
    
    /**
     * 指定テーブルが存在するかチェック.
     * @param name 対象のテーブル名を設定します.
     * @return boolean [true]の場合、存在します.
     */
    public final boolean isTable( final String name ) {
        if( !isInit() ) {
            throw new RimDbException( "TableFactoryは初期化されていません" ) ;
        }
        return confMap.containsKey( TableUtil.convertJavaNameByDBName( name ) ) ;
    }
    
    /**
     * 登録テーブル数を取得.
     * @return int 登録されているテーブル数が返却されます.
     */
    public final int size() {
        return manager.size() ;
    }
    
    /**
     * 登録テーブル名一覧を取得.
     * @return String[] 登録されているテーブル名一覧が返却されます.
     */
    public final String[] getNames() {
        return manager.getNames() ;
    }
    
    /**
     * コンフィグファイル名を取得.
     * @return String コンフィグファイル名が返却されます.
     */
    public final String getConfigFileName() {
        return baseFileName ;
    }
    
    /**
     * ベースフォルダ名を取得.
     * @return String ベースフォルダ名が返却されます.
     */
    public final String getBaseFolder() {
        return baseFolder ;
    }
    
    /**
     * 検索キャッシュオブジェクトを取得.
     * @return CacheSearch 検索キャッシュオブジェクトが返却されます.
     */
    public final CacheSearch getCacheSearch() {
        return cache ;
    }
}
