package org.maachang.jni.io ;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;

/**
 * sun.misc.Unsafeを呼び出し.
 * 
 * @version 2010/06/04
 * @author  masahito suzuki
 * @since   SeabassNativeIO-1.0.0
 */
public final class Unsafe {
    private static final String UNSAFE_FIELD = "theUnsafe" ;
    public static final boolean UNSAFE_MODE ;
    public static final sun.misc.Unsafe unsafe ;
    public static final boolean BIG_ENDIAN ;
    
    private static final Method directByteBufferAddress ;
    
    static {
        ////////////////
        // unsafe取得.
        ////////////////
        sun.misc.Unsafe u = null ;
        try {
            Field f = sun.misc.Unsafe.class.getDeclaredField( UNSAFE_FIELD ) ;
            f.setAccessible(true) ;
            u = (sun.misc.Unsafe) f.get(null) ;
        } catch (Exception e) {
        } finally {
            unsafe = u ;
            UNSAFE_MODE = ( u != null ) ;
        }
        u = null ;
        /////////////////////
        // エンディアン取得.
        /////////////////////
        boolean md = false ;
        // unsafe対応の場合は、これを利用.
        if( unsafe != null ) {
            long a = unsafe.allocateMemory(8);
            try {
                unsafe.putLong(a, 0x0102030405060708L);
                byte b = unsafe.getByte(a);
                switch (b) {
                case 0x01: md = true ; break;
                case 0x08: md = false ; break ;
                default:
                    md = false ;
                }
            } finally {
                unsafe.freeMemory(a);
            }
        }
        // 非対応の場合は、NativeIOを利用.
        else {
            long a = NativeIO.malloc(8);
            try {
                NativeIO.putLong(a, 0x0102030405060708L);
                byte b = NativeIO.getByte(a);
                switch (b) {
                case 0x01: md = true ; break;
                case 0x08: md = false ; break ;
                default:
                    md = false ;
                }
            } finally {
                NativeIO.free(a);
            }
        }
        BIG_ENDIAN = md ;
        /////////////////////////////////////////////////
        // DirectBufferのNativeアドレスReflectionを取得.
        /////////////////////////////////////////////////
        Method dbAddrMethod = null ;
        try {
            Class<?> directByteBuffer = Class.forName( "java.nio.DirectByteBuffer" ) ;
            dbAddrMethod = directByteBuffer.getDeclaredMethod( "address" ) ;
            dbAddrMethod.setAccessible(true) ;
        } catch( Exception e ) {
        } finally {
            directByteBufferAddress = dbAddrMethod ;
        }
    }
    
    /**
     * sun.misc.Unsafeオブジェクトを取得.
     * @return sun.misc.Unsafe オブジェクトが返されます.
     */
    public static sun.misc.Unsafe get() {
        return unsafe;
    }
    
    /**
     * DirectByteBufferのNativeアドレスを取得.
     * @param buf 対象のByteBufferオブジェクトを設定します.
     * @return long Nativeアドレスが返されます.
     * @exception Exception 例外.
     */
    public static long getDirectByteBufferNativeAddress( ByteBuffer buf )
        throws Exception {
        if( directByteBufferAddress == null ) {
            throw new IllegalStateException( "このメソッドはサポートされていません" ) ;
        }
        if( buf == null || !buf.isDirect() ) {
            if( buf == null ) {
                throw new IllegalArgumentException( "引数は不正です" ) ;
            }
            throw new IllegalArgumentException( "指定ByteBufferはDirectByteBufferではありません" ) ;
        }
        return (Long)directByteBufferAddress.invoke(buf) ;
    }
    
    /**
     * shortSwap.
     */
    public static short swap(short x) {
        return (short)((x << 8) | ((x >> 8) & 0x000000ff));
    }
    
    /**
     * charSwap.
     */
    public static char swap(char x) {
        return (char)((x << 8) | ((x >> 8) & 0x000000ff));
    }
    
    /**
     * intSwap.
     */
    public static int swap(int x) {
        return (
            ( ( x & 0x000000ff ) << 24 ) |
            ( ( x & 0x0000ff00 ) << 8 ) |
            ( ( x & 0x00ff0000 ) >> 8 ) |
          ( ( ( x & 0xff000000 ) >> 24 ) & 0x000000ff )
        ) ;
    }
    
    /**
     * longSwap.
     */
    public static long swap(long x) {
        return (
            ( ( x & 0x00000000000000ffL ) << 56L ) |
            ( ( x & 0x000000000000ff00L ) << 40L ) |
            ( ( x & 0x0000000000ff0000L ) << 24L ) |
            ( ( x & 0x00000000ff000000L ) << 8L )  |
            ( ( x & 0x000000ff00000000L ) >> 8L )  |
            ( ( x & 0x0000ff0000000000L ) >> 24L ) |
            ( ( x & 0x00ff000000000000L ) >> 40L ) |
          ( ( ( x & 0xff00000000000000L ) >> 56L ) & 0x00000000000000ff )
        ) ;
    }
    
    //private static int swapInt( int x ) {
    //    return (int)((swap((short)x) << 16) | (swap((short)(x >> 16)) & 0xffff));
    //}
    
    //private static long swapLong( long x ) {
    //    return (long)(((long)swapInt((int)(x)) << 32) | ((long)swapInt((int)(x >> 32)) & 0xffffffffL));
    //}
}

