package org.maachang.mimdb.core.impl;

/**
 * 文字インデックスキー.
 *
 * @version 2013/10/12
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public final class MStringIndexKey implements Comparable<MStringIndexKey> {
    protected final char[] value ;
    protected final int hash ;
    protected final int length ;
    
    /** コンストラクタ **/
    public MStringIndexKey() {
        value = null ;
        hash = -1 ;
        length = -1 ;
    }
    
    /** コンストラクタ. **/
    public MStringIndexKey( final char[] v,final int h,final int l ) {
        value = v ;
        hash = h ;
        length = l ;
    }
    
    /** コンストラクタ. **/
    public MStringIndexKey( final String v ) {
        if( v == null ) {
            value = null ;
            hash = -1 ;
            length = -1 ;// nullは-1
        }
        else {
            final int len = v.length() ;
            final char[] vv = new char[ len ] ;
            for( int i = 0 ; i < len ; i ++ ) {
                vv[ i ] = v.charAt( i ) ;
            }
            value = vv ;
            if( len == 0 ) {
                hash = 0 ;
                length = 0 ;// 空は0
            }
            else {
                hash = MStringIndexKey.fnv32a( vv,len ) ;
                length = len ;
            }
        }
    }
    
    /** コンストラクタ. **/
    public MStringIndexKey( final char[] v ) {
        if( v == null ) {
            value = null ;
            hash = -1 ;
            length = -1 ;// nullは-1
        }
        else {
            value = v ;
            length = v.length ;
            if( length == 0 ) {
                hash = 0 ;// 空は0
            }
            else {
                hash = MStringIndexKey.fnv32a( v,length ) ;
            }
        }
    }
    
    /** ハッシュ生成. **/
    protected static final int fnv32a( final char[] key,final int len) {
        int ret = 0x811C9DC5 ;
        for ( int i = 0 ; i < len ; i++ ) {
            ret = ( ret = ret ^ key[i] ) + (ret << 1L) + (ret << 4L) +
                (ret << 7L) + (ret << 8L) + (ret << 24L);
        }
        return (ret & 0x7ffffffe) + 1 ;
    }
    
    /** nullチェック. **/
    public final boolean isNull() {
        return length == -1 ;
    }
    
    /** 空チェック. **/
    public final boolean isEmpty() {
        return length == 0 ;
    }
    
    /** 同一チェック. **/
    public final boolean equals( final Object o ) {
        if( o == this ) return true ;
        if( o instanceof MStringIndexKey ) {
            final MStringIndexKey oo = ( MStringIndexKey )o ;
            if( oo.length == length && oo.hash == hash && oo.value[ 0 ] == value[ 0 ] ) {
                int i = 0 ;
                final int len = length ;
                final char[] ooo = oo.value ;
                final char[] v = value ;
                while( ++ i < len && ooo[ i ] == v[ i ] ) ;
                return ( i == len ) ;
            }
        }
        return false ;
    }
    
    /** 同一チェック. **/
    public final boolean equals( final int off,MStringIndexKey o,final int oOff,final int len ) {
        if( o.value[ oOff ] == value[ off ] ) {
            int i = 0 ;
            final char[] oval = o.value ;
            final char[] v = value ;
            while( ++ i < len && oval[ oOff + i ] == v[ off + i ] ) ;
            return i == len ;
        }
        return false ;
    }
    
    /** オブジェクトの大きさチェック. **/
    public final int compareTo( final MStringIndexKey n ) {
        if( n == this ) return 0 ;
        final int len = ( length < n.length ) ? length : n.length ;
        if( n.value[ 0 ] == value[ 0 ] ) {
            int i = 0 ;
            final char[] ooo = n.value ;
            final char[] v = value ;
            while( ++ i < len && ooo[ i ] == v[ i ] ) ;
            return ( i == len ) ? length - n.length : (int)v[ i ] - (int)ooo[ i ] ;
        }
        return (int)value[ 0 ] - (int)n.value[ 0 ] ;
    }
    
    /** このオブジェクトの方が大きいかチェック **/
    /** このオブジェクトの方が大きい場合は、>0. **/
    /** このオブジェクトの方が小さい場合は、<0. **/
    /** このオブジェクトと等しい場合は、=0. **/
    public final int toBig( final MStringIndexKey n ) {
        if( n == this ) return 0 ;
        final int len = ( length < n.length ) ? length : n.length ;
        if( n.value[ 0 ] == value[ 0 ] ) {
            int i = 0 ;
            char[] ooo = n.value ;
            char[] v = value ;
            while( ++ i < len && ooo[ i ] == v[ i ] ) ;
            return ( i == len ) ? 0 : (int)v[ i ] - (int)ooo[ i ] ;
        }
        return (int)value[ 0 ] - (int)n.value[ 0 ] ;
    }
    
    /** indexOf. **/
    public final  int indexOf( final String n,final int off ) {
        final int len = n.length() ;
        if( len == 0 || off + len > length ) {
            return -1 ;
        }
        if( len == 1 ) {
            char[] v = value ;
            char nn = n.charAt( 0 ) ;
            int vLen = length ;
            for( int i = off ; i < vLen ; i ++ ) {
                if( nn != v[ i ] ) {
                    while( ++ i < vLen && nn != v[ i ] ) ;
                    if( vLen != i ) {
                        return i ;
                    }
                }
                else {
                    return i ;
                }
            }
        }
        else {
            final char[] v = value ;
            final int vLen = length - (len-1) ;
            final char first = n.charAt( 0 ) ;
            int j,k,next ;
            for( int i = off ; i < vLen ; i ++ ) {
                if( v[ i ] != first ) {
                    while( ++ i < vLen && v[ i ] != first ) ;
                }
                if( i < vLen ) {
                    for( next = i + len , j = i + 1 , k = 1 ;
                        j < next && v[ j ] == n.charAt( k ) ;
                        j ++ , k ++ ) ;
                    if( j == next ) {
                        return i ;
                    }
                }
            }
        }
        return -1 ;
    }
    
    /** indexOf. **/
    public final int indexOf( final char[] n,final int off ) {
        final int len = n.length ;
        if( len == 0 || off + len > length ) {
            return -1 ;
        }
        if( len == 1 ) {
            final char[] v = value ;
            final char nn = n[ 0 ] ;
            final int vLen = length ;
            for( int i = off ; i < vLen ; i ++ ) {
                if( nn != v[ i ] ) {
                    while( ++ i < vLen && nn != v[ i ] ) ;
                    if( vLen != i ) {
                        return i ;
                    }
                }
                else {
                    return i ;
                }
            }
        }
        else {
            final char[] v = value ;
            final int vLen = length - (len-1) ;
            final char first = n[ 0 ] ;
            int j,k,next ;
            for( int i = off ; i < vLen ; i ++ ) {
                if( first != v[ i ] ) {
                    while( ++ i < vLen && v[ i ] != first ) ;
                }
                if( i < vLen ) {
                    for( next = i + len , j = i + 1 , k = 1 ;
                        j < next && v[ j ] == n[ k ] ;
                        j ++ , k ++ ) ;
                    if( j == next ) {
                        return i ;
                    }
                }
            }
        }
        return -1 ;
    }
    
    /** indexOf. **/
    public final int indexOf( final MStringIndexKey n,final int off ) {
        if( n.isNull() || n.isEmpty() ) {
            return -1 ;
        }
        return indexOf( n.value,off ) ;
    }
    
    /** 開始一致チェック. **/
    public final boolean startsWith( String n ) {
        if( n.length() > length ) {
            return false ;
        }
        if( n.charAt( 0 ) == value[ 0 ] ) {
            int i = 0 ;
            final int len = n.length() ;
            final char[] v = value ;
            while( ++ i < len && n.charAt( i ) == v[ i ] ) ;
            return ( i == len ) ;
        }
        return false ;
    }
    
    /** 開始一致チェック. **/
    public final boolean startsWith( final char[] n ) {
        if( n.length > length ) {
            return false ;
        }
        if( n[ 0 ] == value[ 0 ] ) {
            int i = 0 ;
            final int len = n.length ;
            final char[] v = value ;
            while( ++ i < len && n[ i ] == v[ i ] ) ;
            return ( i == len ) ;
        }
        return false ;
    }
    
    /** 開始一致チェック. **/
    public final boolean startsWith( final MStringIndexKey n ) {
        if( n.isNull() || n.isEmpty() ) {
            return false ;
        }
        return startsWith( n.value ) ;
    }
    
    /** 終端一致チェック. **/
    public final boolean endsWith( final String n ) {
        if( n.length() > length ) {
            return false ;
        }
        final int off = length - n.length() ;
        if( n.charAt( 0 ) == value[ off ] ) {
            int i = 0 ;
            final int len = length - off ;
            final char[] v = value ;
            while( ++ i < len && n.charAt( i ) == v[ off + i ] ) ;
            return ( i == len ) ;
        }
        return false ;
    }
    
    /** 終端一致チェック. **/
    public final boolean endsWith( final char[] n ) {
        if( n.length > length ) {
            return false ;
        }
        final int off = length - n.length ;
        if( n[ 0 ] == value[ off ] ) {
            int i = 0 ;
            final int len = length - off ;
            final char[] v = value ;
            while( ++ i < len && n[ i ] == v[ off + i ] ) ;
            return ( i == len ) ;
        }
        return false ;
    }
    
    /** 終端一致チェック. **/
    public final boolean endsWith( final MStringIndexKey n ) {
        if( n.isNull() || n.isEmpty() ) {
            return false ;
        }
        return endsWith( n.value ) ;
    }
    
    /** ハッシュ値取得. **/
    public final int hashCode() {
        return hash ;
    }
    
    /** 長さを取得. **/
    public final int length() {
        return length ;
    }
    
    /** char配列を取得. **/
    public final char[] getChars() {
        return value ;
    }
    
    /** substring. **/
    public final String substring( final int s,final int e ) {
        return String.copyValueOf( value,s,e-s ) ;
    }
    
    /** substring. **/
    public final String substring( final int s ) {
        return String.copyValueOf( value,s,value.length-s ) ;
    }
    
    /** subMString **/
    public final MStringIndexKey subMString( final int s,final int e ) {
        final int ln = e-s ;
        final char[] c = new char[ ln ] ;
        System.arraycopy( value,s,c,0,ln ) ;
        return new MStringIndexKey( c ) ;
    }
    
    /** subMString **/
    public final MStringIndexKey subMString( final int s ) {
        final int ln = value.length-s ;
        final char[] c = new char[ ln ] ;
        System.arraycopy( value,s,c,0,ln ) ;
        return new MStringIndexKey( c ) ;
    }
    
    /** 文字列化. **/
    public final String toString() {
        return String.copyValueOf( value ) ;
    }
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    protected static final int searchMStringIndexKey( final MStringIndexKey[] a, final MStringIndexKey key) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            if ((cmp = a[mid].compareTo(key)) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        return -1 ;
    }
    
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    protected static final int searchMStringIndexKeyBS( final boolean big,final MStringIndexKey[] a,final MStringIndexKey key ) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        mid = 0 ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            if ((cmp = a[mid].compareTo(key)) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        if( big ) {
            if( ( (MStringIndexKey)a[mid] ).compareTo( key ) > 0 ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( ( (MStringIndexKey)a[mid] ).compareTo( key ) > 0 ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
}
