package org.maachang.rawio.mapping ;

import java.io.IOException;

/**
 * １つのMapping操作実装.
 *  
 * @version 2008/06/15
 * @author  masahito suzuki
 * @since   Rawio 1.00
 */
class MappingOpImpl implements MappingOp {
    
    /**
     * MappingioImpl.
     */
    private MappingioImpl impl = null ;
    
    /**
     * １つのMapping要素.
     */
    private MappingChild child = null ;
    
    /**
     * 同期オブジェクト.
     */
    private Object sync = null ;
    
    /**
     * マスク値.
     */
    private int mask = -1 ;
    
    /**
     * シフト値.
     */
    private int shift = -1 ;
    
    /**
     * セクタ位置マスク値.
     */
    private int sectorMask = -1 ;
    
    /**
     * コンストラクタ.
     */
    private MappingOpImpl() {
        
    }
    
    /**
     * コンストラクタ.
     * @param impl MappingioImplを設定します.
     * @param child 対象のMapping要素を設定します.
     * @param sync 対象の同期オブジェクトを設定します.
     * @exception Exception 例外.
     */
    public MappingOpImpl( MappingioImpl impl,MappingChild child,Object sync )
        throws Exception {
        int sector = child.getSector() ;
        int sh = getShift( sector ) ;
        if( sh == -1 ) {
            throw new IOException( "セクタ長("+sector+")は不正です" ) ;
        }
        this.impl = impl ;
        this.child = child ;
        this.sync = sync ;
        this.mask = sector - 1 ;
        this.shift = sh ;
        this.sectorMask = ~mask ;
    }
    
    /**
     * セクタ長を取得.
     * @param sector セクタ長が返されます.
     */
    public int getSector() {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                return -1 ;
            }
            return child.getSector() ;
        }
    }
    
    /**
     * Mapping開始ポジションを取得.
     * @return int Mapping開始ポジションが返されます.
     */
    public int getStartPos() {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                return -1 ;
            }
            return child.getStartPos() ;
        }
    }
    
    /**
     * Mapping長を取得.
     * @return int Mapping長が返されます.
     */
    public int getLength() {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                return -1 ;
            }
            return child.length() ;
        }
    }
    
    /**
     * Mappingバイナリ長を取得.
     * @return int Mappingバイナリ長が返されます.
     */
    public int getBinaryLength() {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                return -1 ;
            }
            return child.getStartPos() * child.length() ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return boolean 対象の要素が返されます.
     */
    public boolean readBoolean( int pos ) {
        boolean ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos ) ;
            int res = readEx( pos ) ;
            ret = ( res == 0 ) ? false : true ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeBoolean( boolean value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos ) ;
            writeEx( ( ( value == true ) ? ( byte )1 : ( byte )0 ),pos ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return byte 対象の要素が返されます.
     */
    public byte readByte( int pos ) {
        byte ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos ) ;
            int res = readEx( pos ) ;
            ret = ( byte )res ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeByte( byte value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos ) ;
            writeEx( value,pos ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return char 対象の要素が返されます.
     */
    public char readChar( int pos ) {
        char ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+1 ) ;
            ret = ( char )( readEx( pos ) & 0x000000ff ) ;
            ret |= ( char )( ( readEx( pos+1 ) & 0x000000ff ) << 8 ) ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeChar( char value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+1 ) ;
            writeEx( ( byte )( value & 0x000000ff ),pos ) ;
            writeEx( ( byte )( ( value & 0x0000ff00 ) >> 8 ),pos+1 ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return short 対象の要素が返されます.
     */
    public short readShort( int pos ) {
        short ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+1 ) ;
            ret = ( short )( readEx( pos ) & 0x000000ff ) ;
            ret |= ( short )( ( readEx( pos+1 ) & 0x000000ff ) << 8 ) ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeShort( short value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+1 ) ;
            writeEx( ( byte )( value & 0x000000ff ),pos ) ;
            writeEx( ( byte )( ( value & 0x0000ff00 ) >> 8 ),pos+1 ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return int 対象の要素が返されます.
     */
    public int readInt( int pos ) {
        int ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+3 ) ;
            ret = ( int )( readEx( pos ) & 0x000000ff ) ;
            ret |= ( int )( ( readEx( pos+1 ) & 0x000000ff ) << 8 ) ;
            ret |= ( int )( ( readEx( pos+2 ) & 0x000000ff ) << 16 ) ;
            ret |= ( int )( ( readEx( pos+3 ) & 0x000000ff ) << 24 ) ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeInt( int value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+3 ) ;
            writeEx( ( byte )( value & 0x000000ff ),pos ) ;
            writeEx( ( byte )( ( value & 0x0000ff00 ) >> 8 ),pos+1 ) ;
            writeEx( ( byte )( ( value & 0x00ff0000 ) >> 16 ),pos+2 ) ;
            writeEx( ( byte )( ( value & 0xff000000 ) >> 24 ),pos+3 ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @return long 対象の要素が返されます.
     */
    public long readLong( int pos ) {
        long ret ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+7 ) ;
            ret = ( long )( readEx( pos ) & 0x00000000000000ffL ) ;
            ret |= ( long )( ( readEx( pos+1 ) & 0x00000000000000ffL ) << 8L ) ;
            ret |= ( long )( ( readEx( pos+2 ) & 0x00000000000000ffL ) << 16L ) ;
            ret |= ( long )( ( readEx( pos+3 ) & 0x00000000000000ffL ) << 24L ) ;
            ret |= ( long )( ( readEx( pos+4 ) & 0x00000000000000ffL ) << 32L ) ;
            ret |= ( long )( ( readEx( pos+5 ) & 0x00000000000000ffL ) << 40L ) ;
            ret |= ( long )( ( readEx( pos+6 ) & 0x00000000000000ffL ) << 48L ) ;
            ret |= ( long )( ( readEx( pos+7 ) & 0x00000000000000ffL ) << 56L ) ;
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     */
    public void writeLong( long value,int pos ) {
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            checkSpace( pos,pos+7 ) ;
            writeEx( ( byte )( value & 0x00000000000000ffL ),pos ) ;
            writeEx( ( byte )( ( value & 0x000000000000ff00L ) >> 8L ),pos+1 ) ;
            writeEx( ( byte )( ( value & 0x0000000000ff0000L ) >> 16L ),pos+2 ) ;
            writeEx( ( byte )( ( value & 0x00000000ff000000L ) >> 24L ),pos+3 ) ;
            writeEx( ( byte )( ( value & 0x000000ff00000000L ) >> 32L ),pos+4 ) ;
            writeEx( ( byte )( ( value & 0x0000ff0000000000L ) >> 40L ),pos+5 ) ;
            writeEx( ( byte )( ( value & 0x00ff000000000000L ) >> 48L ),pos+6 ) ;
            writeEx( ( byte )( ( value & 0xff00000000000000L ) >> 56L ),pos+7 ) ;
        }
    }
    
    /**
     * 指定位置内容を取得します.
     * @param pos 対象のポジションを設定します.
     * @param length 対象の長さを設定します.
     * @return byte[] 対象の要素が返されます.
     */
    public byte[] readBytes( int pos,int length ) {
        if( length <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        byte[] ret = new byte[ length ] ;
        readBytes( ret,pos,0,length ) ;
        return ret ;
    }
    
    /**
     * 指定位置内容を取得します.
     * @param out データ格納対象のバイナリを設定します.
     * @param pos 対象のポジションを設定します.
     * @param off 対象のオフセットを設定します.
     * @param length 対象の長さを設定します.
     * @return int 取得された要素長が返されます.
     */
    public int readBytes( byte[] out,int pos,int off,int length ) {
        if( out == null || off <= -1 || length <= 0 ||
            out.length < off + length ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        int ret = 0 ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            for( int i = 0 ; i < length ; i ++ ) {
                int n = read( pos+i ) ;
                if( n <= -1 ) {
                    break ;
                }
                out[ off+i ] = ( byte )n ;
                ret ++ ;
            }
        }
        return ret ;
    }
    
    /**
     * 指定位置内容を設定します.
     * @param value 対象の要素を設定します.
     * @param pos 対象のポジションを設定します.
     * @param off 対象のオフセットを設定します.
     * @param length 対象の長さを設定します.
     * @return int 書き込まれたバイナリ長を設定します.
     */
    public int writeBytes( byte[] value,int pos,int off,int length ) {
        if( value == null || off <= -1 || length <= 0 ||
            value.length < off + length ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        int ret = 0 ;
        synchronized( sync ) {
            if( impl.isUse() == false ) {
                throw new IllegalArgumentException( "オブジェクトは破棄されています" ) ;
            }
            for( int i = 0 ; i < length ; i ++ ) {
                if( write( value[ off+i ],pos+i ) == false ) {
                    break ;
                }
                ret ++ ;
            }
        }
        return ret ;
    }
    
    private int getShift( int sector ) {
        switch( sector ) {
            case 512 : return 9 ;
            case 1024 : return 10 ;
            case 2048 : return 11 ;
            case 4096 : return 12 ;
            case 8192 : return 13 ;
            case 16384 : return 14 ;
            case 32768 : return 15 ;
            case 65536 : return 16 ;
        }
        return -1 ;
    }
    
    private int getSector( int pos ) {
        return ( ( pos & sectorMask ) >> shift ) ;
    }
    
    private int getNo( int pos ) {
        return ( pos & mask ) ;
    }
    
    private int read( int pos ) {
        int sno = getSector( pos ) ;
        int no = getNo( pos ) ;
        if( sno >= child.length() ) {
            return -1 ;
        }
        byte[] b = child.get( sno ) ;
        return ( ( int )b[ no ] & 0x000000ff ) ;
    }
    
    private boolean write( byte bin,int pos ) {
        int sno = getSector( pos ) ;
        int no = getNo( pos ) ;
        if( sno >= child.length() ) {
            return false ;
        }
        byte[] b = child.get( sno ) ;
        b[ no ] = bin ;
        child.updatePos( sno ) ;
        //try{ impl.updateFlush(); } catch( Exception e ) {}
        return true ;
    }
    
    private byte readEx( int pos ) {
        byte[] b = child.get( getSector( pos ) ) ;
        return b[ getNo( pos ) ] ;
    }
    
    private void writeEx( byte bin,int pos ) {
        int sno = getSector( pos ) ;
        byte[] b = child.get( sno ) ;
        b[ getNo( pos ) ] = bin ;
        child.updatePos( sno ) ;
        //try{ impl.updateFlush(); } catch( Exception e ) {}
    }
    
    private void checkSpace( int base,int pos ) {
        if( getSector( pos ) >= child.length() ) {
            throw new IllegalArgumentException( "指定ポジション[" + base +
                "]はMapping範囲外です" ) ;
        }
    }
}
