package org.maachang.rimdb.table ;

import org.maachang.rimdb.ColumnType;
import org.maachang.rimdb.RimDbException;
import org.maachang.rimdb.util.Config;
import org.maachang.rimdb.util.ConvertUtil;
import org.maachang.rimdb.util.FileUtil;
import org.maachang.rimdb.util.OList;

/**
 * Confファイルから、テーブル作成.
 * 
 * @version 2014/07/11
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
public final class ConfTable {
    
    /** テーブルセクション名. **/
    protected static final String SECTION = "table" ;
    
    /** ベースフォルダ. **/
    public String baseFolder ;
    
    /** コンフィグファイル名. **/
    public String confName ;
    
    /** フルパス名. **/
    public String name ;
    
    /**
     * コンストラクタ.
     * @param baseFolder ベースフォルダを設定します.
     * @param confName Confファイル名を設定します.
     */
    public ConfTable( String baseFolder,String confName ) {
        baseFolder = FileUtil.getFullPath( baseFolder ) ;
        if( !baseFolder.endsWith( "/" ) ) {
            baseFolder += "/" ;
        }
        String file = getPathName( baseFolder,confName ) ;
        
        if( !FileUtil.isFile( file ) ) {
            throw new RimDbException( "指定コンフィグファイル[" + file + "]は存在しません" ) ;
        }
        this.baseFolder = baseFolder ;
        this.confName = confName ;
        this.name = FileUtil.getFullPath( file ) ;
    }
    
    /**
     * コンフィグ情報を読み込みテーブル情報を生成.
     * @return Table テーブルオブジェクトが返却されます.
     */
    public final TableImpl createTable() {
        Config conf = getConf() ;
        String type = conf.getString( SECTION,"readType",0 ) ;
        if( ConvertUtil.eqEng( "csv",type ) ){
            return readCsv( conf ) ;
        }
        else if( ConvertUtil.eqEng( "jdbc",type ) ) {
            return readJDBC( conf ) ;
        }
        throw new RimDbException( "指定コンフィグファイル[" + name +
            "]の属性[" + type + "]が不明です" ) ;
    }
    
    /** パス名を取得. **/
    private final String getPathName( String base,String file ) {
        if( file.indexOf( "/" ) == -1 && file.indexOf( "\\" ) == -1 ) {
            return FileUtil.getFullPath( base + file ) ;
        }
        return FileUtil.getFullPath( file ) ;
    }
    
    /** confオブジェクトを生成. **/
    private final Config getConf() {
        Config ret ;
        try {
            ret = Config.read( name ) ;
        } catch( Exception e ) {
            throw new RimDbException( e ) ;
        }
        if( !ret.isSection( SECTION ) ) {
            throw new RimDbException( "指定コンフィグファイル[" + name +
                "]はrimDBコンフィグ定義ファイルではありません" ) ;
        }
        return ret ;
    }
    
    /** CSV定義を読み込んでテーブル作成. **/
    private final TableImpl readCsv( Config conf ) {
        
        // CSV生成オブジェクトを生成.
        String csvFile = conf.getString( SECTION,"csv",0 ) ;
        String file = getPathName( baseFolder,csvFile ) ;
        
        if( !FileUtil.isFile( file ) ) {
            throw new RimDbException( "指定CSVファイル[" + file + "]は存在しません" ) ;
        }
        
        String charset = conf.getString( SECTION,"charset",0 ) ;
        if( charset == null || charset.length() == 0 ) {
            charset = "Windows-31J" ;
        }
        
        boolean header = conf.getBoolean( SECTION,"header",0 ) ;
        CsvTable createTable = new CsvTable( header,charset,file ) ;
        
        // テーブル定義情報を取得.
        readTableInfo( createTable,conf ) ;
        
        // テーブル作成.
        return createTable.create() ;
   }
    
    /** JDBC定義を読み込んでテーブル作成. **/
    private final TableImpl readJDBC( Config conf ) {
        
        // JDBC生成オブジェクトを生成.
        String driver = conf.getString( SECTION,"driver",0 ) ;
        String url = conf.getString( SECTION,"url",0 ) ;
        String user = conf.getString( SECTION,"user",0 ) ;
        String passwd = conf.getString( SECTION,"passwd",0 ) ;
        String sql = conf.getString( SECTION,"sql",0 ) ;
        
        if( driver == null || ( driver = driver.trim() ).length() <= 0 ) {
            throw new RimDbException( "JDBCドライバー名が設定されていません" ) ;
        }
        
        if( url == null || ( url = url.trim() ).length() <= 0 ) {
            throw new RimDbException( "JDBC接続先が設定されていません" ) ;
        }
        
        if( sql == null || ( sql = sql.trim() ).length() <= 0 ) {
            throw new RimDbException( "JDBC接続SQL条件が設定されていません" ) ;
        }
        
        JDBCTable createTable = new JDBCTable( driver,url,user,passwd,sql ) ;
        
        // テーブル定義情報を取得.
        readTableInfo( createTable,conf ) ;
        
        // テーブル作成.
        return createTable.create() ;
    }
    
    /** テーブル定義条件を取得. **/
    private final void readTableInfo( LoadTable createTable,Config conf ) {
        String tableName = conf.getString( SECTION,"table",0 ) ;
        if( tableName == null || ( tableName = tableName.trim() ).length() == 0 ) {
            throw new RimDbException( "指定ファイル[" + conf.getSrcName() +
                "]のテーブル定義にテーブル名が存在しません" ) ;
        }
        
        String[] columnNames = getColumnNames( conf,SECTION ) ;
        int[] types = getColumnTypes( conf,SECTION,tableName,columnNames ) ;
        if( columnNames == null || columnNames.length == 0 || types == null || types.length == 0 ) {
            throw new RimDbException( "指定ファイル[" + conf.getSrcName() +
                "]のテーブル[" + tableName + "]定義にカラム定義条件が存在しません" ) ;
        }
        
        String[] uniqueNames = getStringArray( conf,SECTION,"unique" ) ;
        if( uniqueNames == null || uniqueNames.length == 0 ) {
            uniqueNames = null ;
        }
        
        String[] index = getStringArray( conf,SECTION,"index" ) ;
        if( index == null || index.length == 0 ) {
            index = null ;
        }
        String[] ngram = getStringArray( conf,SECTION,"ngram" ) ;
        if( ngram == null || ngram.length == 0 ) {
            ngram = null ;
        }
        
        String positionX = conf.getString( SECTION,"positionX",0 ) ;
        String positionY = conf.getString( SECTION,"positionY",0 ) ;
        Integer accuracy = conf.getInt( SECTION,"accuracy",0 ) ;
        if( positionX == null || ( positionX = positionX.trim() ).length() == 0 ||
            positionY == null || ( positionY = positionY.trim() ).length() == 0 ) {
            positionX = null ;
            positionY = null ;
            accuracy = -1 ;
        }
        else if( accuracy == null || accuracy <= 0 ) {
            accuracy = 1 ;
        }
        
        createTable.define( tableName,columnNames,types,uniqueNames,index,ngram,
            positionX,positionY,accuracy ) ;
    }
    
    /** 連続文字定義を取得. **/
    private static final String[] getStringArray( final Config conf,final String section,final String name ) {
        int len = conf.size( section,name ) ;
        if( len <= 0 ) {
            return null ;
        }
        String[] ret = new String[ len ] ;
        for( int i = 0 ; i < len ; i ++ ) {
            ret[ i ] = conf.getString( section,name,i ).trim() ;
        }
        return ret ;
    }
    
    /** カラム定義名群を取得. **/
    private static final String[] getColumnNames( final Config conf,final String section ) {
        final String[] list = conf.getKeys( section ) ;
        OList<String> res = new OList<String>() ;
        int len = list.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( list[ i ].startsWith( "." ) ) {
                res.add( list[ i ].substring( 1 ).trim() ) ;
            }
        }
        len = res.size() ;
        String[] ret = new String[ len ] ;
        System.arraycopy( res.toArray(),0,ret,0,len ) ;
        return ret ;
    }
    
    /** カラム定義を取得. **/
    private static final int[] getColumnTypes( final Config conf,final String section,final String tableName,final String[] columns ) {
        String val ;
        final int len = columns.length ;
        final int[] ret = new int[ len ] ;
        for( int i = 0 ; i < len ; i ++ ) {
            val = conf.getString( section,"." + columns[ i ],0 ) ;
            if( val == null || ( val = val.trim() ).length() <= 0 ) {
                throw new RimDbException( "テーブル[" + tableName +
                    "]定義のカラム[" + columns[ i ] + "]に対する型定義は不正です" ) ;
            }
            if( ConvertUtil.eqEng( "bool",val ) || ConvertUtil.eqEng( "boolean",val ) ) {
                ret[ i ] = ColumnType.TYPE_BOOL ;
            }
            else if( ConvertUtil.eqEng( "int",val ) || ConvertUtil.eqEng( "integer",val )  ) {
                ret[ i ] = ColumnType.TYPE_INT ;
            }
            else if( "long".equals( val ) || ConvertUtil.eqEng( "bigint",val ) ||
                ConvertUtil.eqEng( "biginteger",val )   ) {
                ret[ i ] = ColumnType.TYPE_LONG ;
            }
            else if( ConvertUtil.eqEng( "float",val ) || ConvertUtil.eqEng( "double",val ) ||
                ConvertUtil.eqEng( "real",val ) || ConvertUtil.eqEng( "decimal",val ) ||
                ConvertUtil.eqEng( "numeric",val ) ) {
                ret[ i ] = ColumnType.TYPE_FLOAT ;
            }
            else if( ConvertUtil.eqEng( "string",val ) || ConvertUtil.eqEng( "char",val ) ||
                ConvertUtil.eqEng( "varchar",val ) || ConvertUtil.eqEng( "text",val )  ) {
                ret[ i ] = ColumnType.TYPE_STRING ;
            }
            else if( ConvertUtil.eqEng( "date",val ) ) {
                ret[ i ] = ColumnType.TYPE_DATE ;
            }
            else if( ConvertUtil.eqEng( "time",val ) ) {
                ret[ i ] = ColumnType.TYPE_TIME ;
            }
            else if( ConvertUtil.eqEng( "timestamp",val ) || ConvertUtil.eqEng( "datetime",val ) ) {
                ret[ i ] = ColumnType.TYPE_TIMESTAMP ;
            }
            else if( ConvertUtil.eqEng( "object",val ) ) {
                ret[ i ] = ColumnType.TYPE_OBJECT ;
            }            
            else {
                throw new RimDbException( "テーブル[" + tableName +
                    "]定義のカラム[" + columns[ i ] + "]に対する型定義[" + val + "]は不正です" ) ;
            }
        }
        return ret ;
    }
    
}
