package org.maachang.jni.io;

import java.io.IOException;
import java.io.InputStream;

/**
 * [Mmap]NativeInputStream.
 * 
 * @version 2010/06/04
 * @author  masahito suzuki
 * @since   SeabassNativeIO-1.0.0
 */
public class NativeMmapInputStream extends InputStream {
    private static final int ACCESS_MODE = NativeIODefine.MODE_SEQ ;
    private static final int IO_OPT = NativeIODefine.OPT_READ ;
    private long address = 0L ;
    private long mmapHandle = -1L ;
    private int mmapLength = 0 ;
    private int position = 0 ;
    private long handle = -1L ;
    private long fileSize = 0L ;
    
    private NativeMmapInputStream() {
        
    }
    
    /**
     * コンストラクタ.
     * @param name 対象のファイル名を設定します.
     * @exception Exception 例外.
     */
    public NativeMmapInputStream( String name ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( NativeIODefine.useFile( name ) == false ) {
            throw new NotFileException( "指定ファイル[" + name + "]は存在しません" ) ;
        }
        name = NativeIODefine.fullPath( name ) ;
        this.handle = NativeIO.open( ACCESS_MODE,NativeIODefine.OPEN_ALWAYS,IO_OPT,NativeIODefine.nativeString( name ) ) ;
        if( this.handle == -1L ) {
            throw new IOException( "指定ファイル[" + name + "]のオープンに失敗しました" ) ;
        }
        this.fileSize = NativeIO.getLength( this.handle ) ;
        if( this.fileSize >= 0x000000007fffffffL ) {
            this.close() ;
            throw new IOException( "ファイルサイズが大きすぎるので、mmapできません" ) ;
        }
        createMmap() ;
    }
    
    protected void finalize() throws Exception {
        try {
            close() ;
        } catch( Exception e ) {
        }
    }
    
    /**
     * ファイルクローズ.
     */
    public void close() {
        if( this.handle > -1L ) {
            clearMmap() ;
            NativeIO.close( this.handle ) ;
            this.handle = -1L ;
            this.position = 0 ;
            this.fileSize = -1L ;
        }
    }
    
    /**
     * skip処理.
     * @param off Skip位置を設定します.
     * @return long Skipされた位置数が返却されます.
     * @exception Exception 例外.
     */
    public long skip( long off ) throws IOException {
        if( handle <= -1L ) {
            throw new IOException( "ファイルは既にクローズしています" ) ;
        }
        long oldPos = position ;
        long pos = position + off ;
        if( pos > fileSize ) {
            pos = fileSize ;
        }
        else if( pos <= -1 ) {
            pos = 0 ;
        }
        position = (int)pos ;
        return pos - oldPos ;
    }
    
    /**
     * 残り容量を取得.
     * @return int 残り容量が返されます.
     * @exception IOException I/O例外.
     */
    public int available() throws IOException {
        if( handle <= -1L ) {
            throw new IOException( "ファイルは既にクローズしています" ) ;
        }
        return ( int )( ( fileSize - position ) & 0x000000007fffffffL ) ;
    }
    
    /**
     * 読み込み処理.
     * @return int 読み込まれた情報が返されます.
     * @exception IOException I/O例外.
     */
    public int read() throws IOException {
        if( handle <= -1L ) {
            throw new IOException( "ファイルは既にクローズしています" ) ;
        }
        if( fileSize <= position ) {
            return -1 ;
        }
        int ret = ( int )( DirectMemoryIO.get( address,position ) & 0x000000ff ) ;
        position ++ ;
        return ret ;
    }
    
    /**
     * アドレス情報を取得.
     * @return long Mmapアドレス情報が返されます.
     * @exception IOException I/O例外.
     */
    public long getAddress() throws IOException {
        if( handle <= -1L ) {
            throw new IOException( "ファイルは既にクローズしています" ) ;
        }
        return address ;
    }
    
    /**
     * 読み込みファイル長を取得.
     * @return int ファイル長が返されます.
     */
    public int getLength() throws IOException {
        if( handle <= -1L ) {
            throw new IOException( "ファイルは既にクローズしています" ) ;
        }
        return (int)fileSize ;
    }
    
    /**
     * markサポート.
     * @return boolean [false]が返されます.
     */
    public boolean markSupported() {
        return false ;
    }
    
    /**
     * mmap生成.
     */
    protected void createMmap() throws IOException {
        int len = MmapBufferImpl.getLengthToPageSize( (int)this.fileSize ) ;
        long[] out = new long[ 2 ] ;
        if( NativeIO.createMmap( out,this.handle,MmapBuffer.OPT_READ,0,len ) != 0 ) {
            throw new IOException( "Mmapの生成に失敗しました" ) ;
        }
        this.address = out[ 0 ] ;
        this.mmapHandle = out[ 1 ] ;
        this.mmapLength = len ;
    }
    
    /**
     * mmap破棄.
     */
    protected void clearMmap() {
        if( address != 0L ) {
            NativeIO.closeMmap( mmapHandle,address,mmapLength ) ;
            address = 0L ;
            mmapHandle = -1L ;
            mmapLength = 0 ;
        }
    }
    
    /**
     * 現在のファイルサイズを取得.
     * @return long ファイルサイズが返されます.
     * @exception Exception 例外.
     */
    public long length() throws Exception {
        if( handle == -1L ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        return fileSize ;
    }
}
