package org.maachang.dbm.engine ;

import java.io.IOException;
import java.util.Arrays;

import org.maachang.rawio.Baseio;
import org.maachang.rawio.mapping.MappingOp;
import org.maachang.rawio.mapping.Mappingio;

/**
 * フラグ管理用.
 * 
 * @version 2008/06/05
 * @author masahito suzuki
 * @since MaachangDBM 1.12
 */
class M2RawFlag {
    
    /**
     * 残りのファイルスペースマスクを判別する条件.
     */
    protected static final int ETC_MASK = 0x0000001f ;
    
    /**
     * ファイルスペースマスク.
     */
    protected static final int FSS_MASK = ~ETC_MASK ;
    
    /**
     * ビット係数.
     */
    protected static final int RIGHT_SHIFT = 5 ;
    
    /**
     * データオフセット値.
     */
    protected static final int OFFSET = 12 ;
    
    /**
     * 空き領域が存在する開始ポジション.
     */
    private int useStartPos = 0 ;
    
    /**
     * 最大サイズを保持.
     */
    private int maxBuf = -1 ;
    
    /**
     * 現在のサイズを保持.
     */
    private int sizeBuf = -1 ;
    
    /**
     * 最大配列を保持.
     */
    private int maxAryBuf = -1 ;
    
    /**
     * MappingOp.
     */
    private MappingOp mappingOp = null ;
    
    /**
     * コンストラクタ.
     */
    private M2RawFlag() {
        
    }
    
    /**
     * コンストラクタ.
     * @param fp ランダムアクセスファイルオブジェクトを設定します.
     * @param startPos メモリーマップ開始ポジションを設定します.
     * @param size フラグ管理数を設定します.
     * @param mappingLength マッピング長を設定します.
     * @exception Exception 例外.
     */
    public M2RawFlag( Baseio fp,int startPos,int size,int mappingLength )
        throws Exception {
        this( false,fp,startPos,size,mappingLength ) ;
    }
    
    /**
     * コンストラクタ.
     * @param mode 初期化する場合は[true]を設定します.
     * @param fp ランダムアクセスファイルオブジェクトを設定します.
     * @param startPos メモリーマップ開始ポジションを設定します.
     * @param size フラグ管理数を設定します.
     * @param mappingLength マッピング長を設定します.
     * @exception Exception 例外.
     */
    public M2RawFlag( boolean mode,Baseio fp,int startPos,int size,int mappingLength )
        throws Exception {
        int max = ( ( size & FSS_MASK ) >> RIGHT_SHIFT ) + ( ( ( size & ETC_MASK ) != 0 ) ? 1 : 0 ) ;
        int maxPos = max * 32 ;
        Mappingio mappingio = ( Mappingio )fp ;
        MappingOp op = mappingio.addMapping( startPos,mappingLength ) ;
        // 初期化.
        if( mode == true ) {
            int mappingFlags = mappingLength * fp.getSector() ;
            byte[] b = new byte[ mappingFlags ] ;
            Arrays.fill( b,( byte )0 ) ;
            op.writeBytes( b,0,0,mappingFlags ) ;
        }
        this.mappingOp = op ;
        // 初期設定値を設定.
        mfMaxSize( maxPos ) ;
        mfMaxArray( max ) ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public synchronized void destroy() {
        this.mappingOp = null ;
        this.useStartPos = 0 ;
        this.maxBuf = -1 ;
        this.sizeBuf = -1 ;
        this.maxAryBuf = -1 ;
    }
    
    /**
     * 指定データサイズに対する領域を計算.
     * @param sector 対象のセクタ長を設定します.
     * @param size 対象のサイズを設定します.
     * @return int サイズが返されます.
     */
    public static final int convertRect( int sector,int size ) {
        int x = ( ( ( ( size & FSS_MASK ) >> RIGHT_SHIFT ) + ( ( ( size & ETC_MASK ) != 0 ) ? 1 : 0 ) ) * 4 ) + OFFSET ;
        int ret = x / sector ;
        if( x % sector != 0 ) {
            ret ++ ;
        }
        return ret ;
    }
    
    /**
     * 指定位置のポジションを利用中に設定.
     * @param pos 対象のポジションを設定します.
     * @exception Exception 例外.
     */
    public synchronized void setPos( int pos ) throws Exception {
        int maxPos = mfMaxSize() ;
        if( pos < 0 || pos >= maxPos ) {
            throw new IllegalArgumentException( "指定ポジションは不正です" ) ;
        }
        int thisPos = ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ;
        int innerPos = ( pos & ETC_MASK ) ;
        int target = mfArray( thisPos ) ;
        if( ( ( ( 1 << innerPos ) ) & target ) != 0 ) {
            throw new IOException( "指定項番["+pos+"]は既にONです" ) ;
        }
        target = ( 1 << innerPos ) | target ;
        mfArray( thisPos,target ) ;
        mfSize( mfSize() + 1 ) ;
        
        useStartPos = pos+1 ;
    }
    
    /**
     * 指定位置のポジションを空ける.
     * @param pos 対象のポジションを設定します.
     * @exception Exception 例外.
     */
    public synchronized void removePos( int pos ) throws Exception {
        int maxPos = mfMaxSize() ;
        if( pos < 0 || pos >= maxPos ) {
            throw new IllegalArgumentException( "指定ポジションは不正です" ) ;
        }
        int thisPos = ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ;
        int innerPos = ( pos & ETC_MASK ) ;
        int target = mfArray( thisPos ) ;
        if( ( ( ( 1 << innerPos ) ) & target ) == 0 ) {
            throw new IOException( "指定項番["+pos+"]は既にOFFです" ) ;
        }
        target = ( ~( 1 << innerPos ) ) & target ;
        mfArray( thisPos,target ) ;
        mfSize( mfSize() - 1 ) ;
        
        useStartPos = 0 ;
    }
    
    /**
     * 指定位置のポジションが空いているか取得.
     * @param pos 対象のポジションを設定します.
     * @return boolean [false]の場合は、空いています.
     * @exception Exception 例外.
     */
    public synchronized boolean getPos( int pos ) throws Exception {
        int maxPos = mfMaxSize() ;
        if( pos < 0 || pos >= maxPos ) {
            throw new IllegalArgumentException( "指定ポジション["+pos+"]は不正です" ) ;
        }
        return targetPos( pos ) ;
    }
    
    private boolean targetPos( int pos ) throws Exception {
        return ( ( ( 1 << ( pos & ETC_MASK ) ) ) & mfArray( ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ) ) != 0 ;
    }
    
    private boolean targetPos( int pos,int one ) throws Exception {
        return ( ( ( 1 << ( pos & ETC_MASK ) ) ) & one ) != 0 ;
    }
    
    /**
     * 指定ポジションの次の有効な項番を取得.
     * @param pos 対象のポジションを設定します.
     * @return int 次に有効なポジションが返されます.
     * @exception Exception 例外.
     */
    public synchronized int useNextPos( int pos ) throws Exception {
        int maxPos = mfMaxSize() ;
        if( pos < -1 || pos >= maxPos ) {
            throw new IllegalArgumentException( "指定ポジションは不正です" ) ;
        }
        int one = 0 ;
        pos ++ ;
        if( ( pos & ETC_MASK ) != 0 ) {
            one = mfArray( ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ) ;
        }
        for( ;; ) {
            if( pos >= maxPos ) {
                return -1 ;
            }
            if( ( pos & ETC_MASK ) == 0 ) {
                one = mfArray( ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ) ;
                if( one == 0 ) {
                    pos += 32 ;
                    continue ;
                }
            }
            if( targetPos( pos,one ) == true ) {
                return pos ;
            }
            pos ++ ;
        }
    }
    
    /**
     * 空いているポジション位置を取得.
     * @param pos 検索開始ポジションを設定します.
     * @return int 空いているポジション位置が返されます.<BR>
     *             [-1]が返された場合、空き位置は存在しません.
     * @exception Exception 例外.
     */
    public synchronized int usePos( int pos ) throws Exception {
        if( pos < 0 ) {
            throw new IllegalArgumentException( "指定ポジションは不正です" ) ;
        }
        else if( pos == 0 ) {
            pos = useStartPos ;
        }
        int len = mfMaxArray() ;
        for( int i = ( ( pos & FSS_MASK ) >> RIGHT_SHIFT ) ; i < len ; i ++ ) {
            int target = mfArray( i ) ;
            if( target != 0xffffffff ) {
                for( int j = 0 ; j < 32 ; j ++ ) {
                    if( ( target & ( 1 << j ) ) == 0 ) {
                        return ( i * 32 ) + j ;
                    }
                }
            }
        }
        //useStartPos = 0 ;
        return -1 ;
    }
    
    /**
     * 空いているポジション位置を予約して取得.
     * @param pos 検索開始ポジションを設定します.
     * @return int 空いているポジション位置が返されます.<BR>
     *             [-1]が返された場合、空き位置は存在しません.
     * @exception Exception 例外.
     */
    public synchronized int usePosBySet( int pos ) throws Exception {
        int u = usePos( pos ) ;
        if( u == -1 ) {
            return -1 ;
        }
        setPos( u ) ;
        return u ;
    }
    
    /**
     * 現在有効件数を取得.
     * @return int 現在の有効件数が返されます.
     * @exception Exception 例外.
     */
    public synchronized int size() throws Exception {
        return mfSize() ;
    }
    
    /**
     * 現在の最大有効件数を取得.
     * @return int 現在の最大有効件数が返されます.
     * @exception Exception 例外.
     */
    public synchronized int maxSize() throws Exception {
        return mfMaxSize() ;
    }
    
    private int mfMaxSize() throws Exception {
        int ret ;
        if( this.maxBuf <= -1 ) {
            ret = mappingOp.readInt( 0 ) ;
            this.maxBuf = ret ;
        }
        else {
            ret = this.maxBuf ;
        }
        return ret ;
    }
    
    private void mfMaxSize( int n ) throws Exception {
        mappingOp.writeInt( n,0 ) ;
        this.maxBuf = n ;
    }
    
    private int mfSize() throws Exception {
        int ret ;
        if( this.sizeBuf <= -1 ) {
            ret = mappingOp.readInt( 4 ) ;
            this.sizeBuf = ret ;
        }
        else {
            ret = this.sizeBuf ;
        }
        return ret ;
    }
    
    private void mfSize( int n ) throws Exception {
        mappingOp.writeInt( n,4 ) ;
        this.sizeBuf = n ;
    }
    
    private int mfMaxArray() throws Exception {
        int ret ;
        if( maxAryBuf <= -1 ) {
            ret = mappingOp.readInt( 8 ) ;
            this.maxAryBuf = ret ;
        }
        else {
            ret = this.maxAryBuf ;
        }
        return ret ;
    }
    
    private void mfMaxArray( int n ) throws Exception {
        mappingOp.writeInt( n,8 ) ;
        this.maxAryBuf = n ;
    }
    
    private int mfArray( int no ) throws Exception {
        return mappingOp.readInt( OFFSET + ( no * 4 ) ) ;
    }
    
    private void mfArray( int no,int n ) throws Exception {
        mappingOp.writeInt( n,OFFSET + ( no * 4 ) ) ;
    }

}
