package org.maachang.rimdb.table ;

import org.maachang.rimdb.ColumnType;
import org.maachang.rimdb.RimDbException;
import org.maachang.rimdb.index.BetweenPointer;
import org.maachang.rimdb.index.IfPointer;
import org.maachang.rimdb.index.InPointer;
import org.maachang.rimdb.index.Index;
import org.maachang.rimdb.index.IndexUtil;
import org.maachang.rimdb.index.LikePointer;
import org.maachang.rimdb.index.bool.BoolEq;
import org.maachang.rimdb.index.bool.BoolIndex;
import org.maachang.rimdb.index.comparable.ComparableBetween;
import org.maachang.rimdb.index.comparable.ComparableBig;
import org.maachang.rimdb.index.comparable.ComparableEq;
import org.maachang.rimdb.index.comparable.ComparableIn;
import org.maachang.rimdb.index.comparable.ComparableIndex;
import org.maachang.rimdb.index.comparable.ComparableSmall;
import org.maachang.rimdb.index.number32.Number32Between;
import org.maachang.rimdb.index.number32.Number32Big;
import org.maachang.rimdb.index.number32.Number32Eq;
import org.maachang.rimdb.index.number32.Number32In;
import org.maachang.rimdb.index.number32.Number32Index;
import org.maachang.rimdb.index.number32.Number32Small;
import org.maachang.rimdb.index.number64.Number64Between;
import org.maachang.rimdb.index.number64.Number64Big;
import org.maachang.rimdb.index.number64.Number64Eq;
import org.maachang.rimdb.index.number64.Number64In;
import org.maachang.rimdb.index.number64.Number64Index;
import org.maachang.rimdb.index.number64.Number64Small;
import org.maachang.rimdb.index.numberFloat.NumberFloatBetween;
import org.maachang.rimdb.index.numberFloat.NumberFloatBig;
import org.maachang.rimdb.index.numberFloat.NumberFloatEq;
import org.maachang.rimdb.index.numberFloat.NumberFloatIn;
import org.maachang.rimdb.index.numberFloat.NumberFloatIndex;
import org.maachang.rimdb.index.numberFloat.NumberFloatSmall;
import org.maachang.rimdb.index.position.PositionIndex;
import org.maachang.rimdb.index.position.PositionIndexPointer;
import org.maachang.rimdb.index.string.StringBetween;
import org.maachang.rimdb.index.string.StringBig;
import org.maachang.rimdb.index.string.StringEq;
import org.maachang.rimdb.index.string.StringIn;
import org.maachang.rimdb.index.string.StringIndex;
import org.maachang.rimdb.index.string.StringLike;
import org.maachang.rimdb.index.string.StringSmall;

/**
 * インデックステーブル.
 * 
 * @version 2014/07/07
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public final class Indexs {
    
    protected final int[] columns ;
    protected final Index[] list ;
    protected final PositionIndex position ;
    protected final int[] positionColumns ;
    protected Columns base ;
    protected final int length ;
    
    /**
     * コンストラクタ.
     * @param base カラムテーブルオブジェクトを設定します.
     * @param columns 対象のインデックス列項番を設定します.
     * @param list 対象のインデックス情報を設定します.
     * @param positionColumns 対象の空間インデックスカラム位置を設定します.
     * @param position 対象の空間インデックス情報を設定します.
     */
    public Indexs( Columns base,int[] columns,Index[] list,int[] positionColumns,PositionIndex position ) {
        this.base = base ;
        this.columns = columns ;
        this.list = list ;
        this.positionColumns = positionColumns ;
        this.position = position ;
        
        final int len = list.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            list[ i ].setColumnNoAndName( columns[ i ],base.getColumnName( columns[ i ] ) ) ;
        }
        this.length = len ;
    }
    
    /**
     * インデックス数を取得.
     * @return int インデックス数が返却されます.
     */
    public final int length() {
        return length ;
    }
    
    /**
     * 空間インデックス情報を取得.
     * @return PositionIndex 空間インデックス情報が返却されます.
     */
    public final PositionIndex getPositionIndex() {
        return position ;
    }
    
    /**
     * 空間インデックス情報が利用可能かチェック.
     * @return boolean [true]の場合、利用可能です.
     */
    public final boolean isPositionIndex() {
        return position != null ;
    }
    
    /**
     * 指定カラム項番にインデックスが存在するかチェック.
     * @param no 対象のカラム項番を設定します.
     * @return boolean [true]の場合、インデックス情報は存在します.
     */
    public final boolean contains( final int no ) {
        return getColumnNoByNo( no ) != -1 ;
    }
    
    /**
     * 指定カラム名がインデックスかチェック.
     * @param name 対象のカラム名を設定します.
     */
    public final boolean contains( final String name ) {
        return getColumnNameByNo( name ) != -1 ;
    }
    
    /**
     * 対象項番のインデックス情報を取得.
     * @param no 対象のカラム項番を設定します.
     * @return Index 対象のインデックスが返却されます.
     */
    public final Index getIndex( final int no ) {
        final int p = getColumnNoByNo( no ) ;
        if( p == -1 ) {
            return null ;
        }
        return list[ p ] ;
    }
    
    /**
     * 対象カラム名のインデックス情報を取得.
     * @param name 対象のカラム名を設定します.
     * @return Index 対象のインデックスが返却されます.
     */
    public final Index getIndex( final String name ) {
        final int p = getColumnNameByNo( name ) ;
        if( p == -1 ) {
            return null ;
        }
        return list[ p ] ;
    }
    
    /**
     * 完全一致[=]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer eq( final int no ) {
        return _eq( getColumnNoByNo( no ),true ) ;
    }
    
    /**
     * 完全一致[=]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer eq( final String name ) {
        return _eq( getColumnNameByNo( name ),true ) ;
    }
    
    /**
     * 完全不一致[!=]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer not( final int no ) {
        return _eq( getColumnNoByNo( no ),false ) ;
    }
    
    /**
     * 完全不一致[!=]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer not( final String name ) {
        return _eq( getColumnNameByNo( name ),false ) ;
    }
    
    /**
     * 大なり[>]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer big( final int no ) {
        return _big( getColumnNoByNo( no ),false ) ;
    }
    
    /**
     * 大なり[>]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer big( final String name ) {
        return _big( getColumnNameByNo( name ),false ) ;
    }
    
    /**
     * 以上[>=]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer bigEq( final int no ) {
        return _big( getColumnNoByNo( no ),true ) ;
    }
    
    /**
     * 以上[>=]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer bigEq( final String name ) {
        return _big( getColumnNameByNo( name ),true ) ;
    }
    
    /**
     * 小なり[<]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer small( final int no ) {
        return _small( getColumnNoByNo( no ),false ) ;
    }
    
    /**
     * 小なり[<]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer small( final String name ) {
        return _small( getColumnNameByNo( name ),false ) ;
    }
    
    /**
     * 以下[<=]検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer smallEq( final int no ) {
        return _small( getColumnNoByNo( no ),true ) ;
    }
    
    /**
     * 以下[<=]検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return IfPointer 検索ポインターが返却されます.
     */
    public final IfPointer smallEq( final String name ) {
        return _small( getColumnNameByNo( name ),true ) ;
    }
    
    /**
     * IN検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return InPointer 検索ポインターが返却されます.
     */
    public final InPointer in( final int no ) {
        return _in( getColumnNoByNo( no ) ) ;
    }
    
    /**
     * IN検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return InPointer 検索ポインターが返却されます.
     */
    public final InPointer in( final String name ) {
        return _in( getColumnNameByNo( name ) ) ;
    }
    
    /**
     * BETWEEN検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return InPointer 検索ポインターが返却されます.
     */
    public final BetweenPointer between( final int no ) {
        return _between( getColumnNoByNo( no ) ) ;
    }
    
    /**
     * BETWEEN検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return InPointer 検索ポインターが返却されます.
     */
    public final BetweenPointer between( final String name ) {
        return _between( getColumnNameByNo( name ) ) ;
    }
    
    /**
     * Like検索ポインタを生成.
     * @param no 対象のカラム項番を設定します.
     * @return LikePointer 検索ポインターが返却されます.
     */
    public final LikePointer like( final int no ) {
        return _like( getColumnNoByNo( no ) ) ;
    }
    
    /**
     * Like検索ポインタを生成.
     * @param name 対象のカラム名を設定します.
     * @return LikePointer 検索ポインターが返却されます.
     */
    public final LikePointer like( final String name ) {
        return _like( getColumnNameByNo( name ) ) ;
    }
    
    /**
     * 空間インデックス検索ポインタを生成.
     * @return PositionIndexPointer 空間インデックス検索ポインタが返却されます.
     */
    public final PositionIndexPointer position() {
        if( position == null ) {
            throw new RimDbException( "空間インデックスは対応していません" ) ;
        }
        return new PositionIndexPointer() ;
    }
    
    /** 指定カラム項番のインデックス項番を取得. **/
    protected final int getColumnNoByNo( final int no ) {
        return IndexUtil.searchInt( columns,no ) ;
    }
    
    /** 指定カラム名のインデックス項番を取得. **/
    protected final int getColumnNameByNo( final String name ) {
        int p = base.getColumnNo( name ) ;
        if( p == -1 || ( p = IndexUtil.searchInt( columns,p ) ) == -1 ) {
            return -1 ;
        }
        return p ;
    }
    
    /** 一致、不一致ポインター取得. **/
    protected final IfPointer _eq( final int no,final boolean eq ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件ではありません:" + no ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                return new BoolEq( (BoolIndex)idx,eq ) ;
            case ColumnType.TYPE_INT :
                return new Number32Eq( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_LONG :
                return new Number64Eq( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_FLOAT :
                return new NumberFloatEq( (NumberFloatIndex)idx,eq ) ;
            case ColumnType.TYPE_DATE :
                return new Number64Eq( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_TIME :
                return new Number32Eq( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_TIMESTAMP :
                return new Number64Eq( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_STRING :
                return new StringEq( (StringIndex)idx,eq ) ;
            case ColumnType.TYPE_OBJECT :
                return new ComparableEq( (ComparableIndex)idx,eq ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
    /** 大なり(>)以上(>=)ポインター取得. **/
    protected final IfPointer _big( final int no,final boolean eq ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                throw new RimDbException( "Boolean型では[> or >=]処理は対応していません" ) ;
            case ColumnType.TYPE_INT :
                return new Number32Big( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_LONG :
                return new Number64Big( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_FLOAT :
                return new NumberFloatBig( (NumberFloatIndex)idx,eq ) ;
            case ColumnType.TYPE_DATE :
                return new Number64Big( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_TIME :
                return new Number32Big( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_TIMESTAMP :
                return new Number64Big( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_STRING :
                return new StringBig( (StringIndex)idx,eq ) ;
            case ColumnType.TYPE_OBJECT :
                return new ComparableBig( (ComparableIndex)idx,eq ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
    /** 小なり(<)以下(<=)ポインター取得. **/
    protected final IfPointer _small( final int no,final boolean eq ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                throw new RimDbException( "Boolean型では[< or <=]処理は対応していません" ) ;
            case ColumnType.TYPE_INT :
                return new Number32Small( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_LONG :
                return new Number64Small( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_FLOAT :
                return new NumberFloatSmall( (NumberFloatIndex)idx,eq ) ;
            case ColumnType.TYPE_DATE :
                return new Number64Small( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_TIME :
                return new Number32Small( (Number32Index)idx,eq ) ;
            case ColumnType.TYPE_TIMESTAMP :
                return new Number64Small( (Number64Index)idx,eq ) ;
            case ColumnType.TYPE_STRING :
                return new StringSmall( (StringIndex)idx,eq ) ;
            case ColumnType.TYPE_OBJECT :
                return new ComparableSmall( (ComparableIndex)idx,eq ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
    /** INポインター取得. **/
    protected final InPointer _in( final int no ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                throw new RimDbException( "Boolean型ではIN処理は対応していません" ) ;
            case ColumnType.TYPE_INT :
                return new Number32In( (Number32Index)idx ) ;
            case ColumnType.TYPE_LONG :
                return new Number64In( (Number64Index)idx ) ;
            case ColumnType.TYPE_FLOAT :
                return new NumberFloatIn( (NumberFloatIndex)idx ) ;
            case ColumnType.TYPE_DATE :
                return new Number64In( (Number64Index)idx ) ;
            case ColumnType.TYPE_TIME :
                return new Number32In( (Number32Index)idx ) ;
            case ColumnType.TYPE_TIMESTAMP :
                return new Number64In( (Number64Index)idx ) ;
            case ColumnType.TYPE_STRING :
                return new StringIn( (StringIndex)idx ) ;
            case ColumnType.TYPE_OBJECT :
                return new ComparableIn( (ComparableIndex)idx ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
    /** BETWEENポインター取得. **/
    protected final BetweenPointer _between( final int no ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                throw new RimDbException( "Boolean型ではIN処理は対応していません" ) ;
            case ColumnType.TYPE_INT :
                return new Number32Between( (Number32Index)idx ) ;
            case ColumnType.TYPE_LONG :
                return new Number64Between( (Number64Index)idx ) ;
            case ColumnType.TYPE_FLOAT :
                return new NumberFloatBetween( (NumberFloatIndex)idx ) ;
            case ColumnType.TYPE_DATE :
                return new Number64Between( (Number64Index)idx ) ;
            case ColumnType.TYPE_TIME :
                return new Number32Between( (Number32Index)idx ) ;
            case ColumnType.TYPE_TIMESTAMP :
                return new Number64Between( (Number64Index)idx ) ;
            case ColumnType.TYPE_STRING :
                return new StringBetween( (StringIndex)idx ) ;
            case ColumnType.TYPE_OBJECT :
                return new ComparableBetween( (ComparableIndex)idx ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
    /** LIKEポインター取得. **/
    protected final LikePointer _like( final int no ) {
        if( no < 0 || no >= length ) {
            throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
        }
        final Index idx = list[ no ] ;
        final int type = base.list[ columns[ no ] ].getType() ;
        switch( type ) {
            case ColumnType.TYPE_BOOL :
                throw new RimDbException( "Boolean型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_INT :
                throw new RimDbException( "Integer型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_LONG :
                throw new RimDbException( "Long型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_FLOAT :
                throw new RimDbException( "Float型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_DATE :
                throw new RimDbException( "Date型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_TIME :
                throw new RimDbException( "Time型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_TIMESTAMP :
                throw new RimDbException( "Timestamp型では[Like]処理は対応していません" ) ;
            case ColumnType.TYPE_STRING :
                return new StringLike( (StringIndex)idx ) ;
            case ColumnType.TYPE_OBJECT :
                throw new RimDbException( "Object型では[Like]処理は対応していません" ) ;
        }
        throw new RimDbException( "インデックス条件["+no+"]ではありません" ) ;
    }
    
}
