package org.maachang.jni.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * NativeI/O.
 * 
 * @version 2008/11/29
 * @author  masahito suzuki
 * @since   SeabassNativeIO-1.0.0
 */
class NativeIO {
    private static final _atomicBOOL initFlag = new _atomicBOOL( false ) ;
    static {
        int bit = IsOs.getInstance().getBit() ;
        if( bit == -1 ) {
            System.err.println( "JavaVM条件の取得に失敗" ) ;
            System.exit( -9 ) ;
        }
        String name = NativeIODefine.LIB_NAME ;
        StringBuilder libBuf = new StringBuilder().append( name ).append( "-" ) ;
        if( bit == 32 ) {
            libBuf.append( NativeIODefine.LIB_BIT_32 ) ;
        }
        else if( bit == 64 ) {
            libBuf.append( NativeIODefine.LIB_BIT_64 ) ;
        }
        String lib = libBuf.append( NativeIODefine.VERSION ).toString() ;
        libBuf = null ;
        switch( IsOs.getInstance().getOS() ) {
            case IsOs.OS_WINNT :
                lib += NativeIODefine.WINDOWS_LIB_PLUS ;
                System.load( targetDynamincLib( true,lib ) ) ;
                if( init() != 0 ) {
                    System.err.println( "NativeIO初期化処理失敗" ) ;
                    System.exit( -1 ) ;
                }
                initFlag.set( true ) ;
                break ;
            case IsOs.OS_UNIX :
                lib += NativeIODefine.LINUX_LIB_PLUS ;
                System.load( targetDynamincLib( true,lib ) ) ;
                initFlag.set( true ) ;
                if( init() != 0 ) {
                    System.err.println( "NativeIO初期化処理失敗" ) ;
                    System.exit( -1 ) ;
                }
                break ;
            default :
                initFlag.set( false ) ;
                break ;
        }
    }
    
    private static final String targetDynamincLib( boolean mode,String lib ) {
        String sp = System.getProperty( "file.separator" ) ;
        File targetDir = null ;
        targetDir = new File( new StringBuilder().
            append( System.getProperty( "user.home" ) ).
            append( sp ).
            append( NativeIODefine.DEFAULT_DIR ).
            toString() );
        if( targetDir.exists() == false ) {
            targetDir.mkdirs() ;
        }
        File outFile = new File( targetDir,lib );
        if( mode ) {
            String ntvDir = NativeIODefine.NATIVE_PACKAGE ;
            ntvDir = ntvDir.trim() ;
            if( ntvDir.endsWith( "/" ) == false ) {
                ntvDir += "/" ;
            }
            if( ntvDir.startsWith( "/" ) ) {
                ntvDir = ntvDir.substring( 1 ) ;
            }
            InputStream is = new BufferedInputStream(
                Thread.currentThread().getContextClassLoader().getResourceAsStream(
                ntvDir + lib ) ) ;
            if( isLibFile( outFile,is ) ) {
                try{
                    OutputStream os = new BufferedOutputStream( new FileOutputStream( outFile ) ) ;
                    try{
                        try{
                            int n ;
                            byte[] b = new byte[ 512 ] ;
                            while( true ) {
                                if( ( n = is.read( b ) ) <= 0 ) {
                                    break;
                                }
                                os.write( b,0,n );
                            }
                            os.flush() ;
                            os.close() ;
                            os = null ;
                            is.close() ;
                            is = null ;
                        }
                        finally{
                            if( is != null ) {
                                is.close();
                                is = null ;
                            }
                        }
                    }
                    catch( Exception e ) {
                        e.printStackTrace() ;
                        throw e ;
                    }
                    finally{
                        if( os != null ) {
                            os.close();
                            os = null ;
                        }
                    }
                }
                catch( Exception e ) {
                    outFile = null ;
                }
            }
            if( is != null ) {
                try {
                    is.close() ;
                } catch( Exception e ) {
                }
                is = null ;
            }
        }
        if( outFile != null ) {
            return outFile.getAbsolutePath() ;
        }
        return null ;
    }
    
    private static final boolean isLibFile( File f,InputStream in ) {
        boolean ret = false ;
        try {
            if( f.exists() == false ) {
                ret = true ;
            }
            else {
                int len = ( int )f.length() ;
                ret = ( len <= 0 || len != in.available() ) ;
            }
        } catch( Exception e ) {
            ret = false ;
        }
        return ret ;
    }
    
    protected static final boolean useInit() {
        return initFlag.get() ;
    }
    
    protected NativeIO() {
        
    }
    
    /** i/o系初期化処理. **/
    private static native int init() ;
    
    /** memory-i/o. **/
    protected static native long malloc( int size ) ;
    protected static native long realloc( long address,int size ) ;
    protected static native void free( long address ) ;
    protected static native void memset( long address,byte code,int size ) ;
    protected static native void memcpy( long destAddr,long srcAddr,int size ) ;
    protected static native byte getByte( long address ) ;
    protected static native void putByte( long address,byte value ) ;
    protected static native void getBinary( long address,byte[] binary,int off,int len ) ;
    protected static native void putBinary( long address,byte[] binary,int off,int len ) ;
    protected static native void putChar( long address,char value ) ;
    protected static native char getChar( long address ) ;
    protected static native void putShort( long address,short value ) ;
    protected static native short getShort( long address ) ;
    protected static native void putInt( long address,int value ) ;
    protected static native int getInt( long address ) ;
    protected static native void putLong( long address,long value ) ;
    protected static native long getLong( long address ) ;
    protected static native int equalsBinary( long c1addr,long c2addr,int len ) ;
    protected static native long fnv64( long address,int len ) ;
    protected static native int indexOf( long address,int length,byte[] binary,int binaryLength ) ;
    protected static native int lastIndexOf( long startAddress,int offset,int length,byte[] binary,int binaryLength ) ;
    
    /** file-i/o. **/
    protected static native long open( int mode,int rw,byte[] file ) ;
    protected static native void close( long handle ) ;
    protected static native int fsync( long handle ) ;
    protected static native int read( long handle,long seek,long address,int off,int len ) ;
    protected static native int write( long handle,long seek,long address,int off,int len ) ;
    protected static native long getLength( long handle ) ;
    protected static native int setLength( long handle,long len ) ;
    protected static native long getSeek( long handle ) ;
    
    // mmap関連.
    protected static native int createMmap( long[] out,long handle,int opt,long offset,int length ) ;
    protected static native int closeMmap( long handle,long address,int length ) ;
    protected static native int flushMmap( long address,int length ) ;
    protected static native int pageFileLength() ;
    protected static native int pageFileShift() ;
    protected static native int mmapLength( int length ) ;
    
}

/** atomicBOOL **/
class _atomicBOOL {
    private final AtomicInteger ato = new AtomicInteger( 0 ) ;
    public _atomicBOOL() {
        
    }
    public _atomicBOOL( boolean f ) {
        setting( ( f ) ? 1 : 0 ) ;
    }
    public boolean get() {
        return ato.get() != 0 ;
    }
    
    public void set( boolean f ) {
        setting( ( f ) ? 1 : 0 ) ;
    }
    
    private void setting( int no ) {
        while( !ato.compareAndSet( ato.get(),no ) ) {}
    }
}
