#include <windows.h>
#include "../../include/org_maachang_rawio_NativeRawIO.h"
#include "cpu.h"

#pragma comment(lib,"jvm.lib")

// ハンドル格納オブジェクト.
typedef struct {
    HANDLE handle ;                 // ファイルハンドル.
    int sector ;                    // セクタ長.
} MHDL,*LPMHDL ;

// セクタ長を取得.
int GetSector( const char* path ) {
    unsigned long sector ;
    unsigned long t1,t2,t3 ;
    if( GetDiskFreeSpace( path,&t1,&sector,&t2,&t3 ) == FALSE ) {
        return -1 ;
    }
    return ( int )sector ;
}

// シークポイントを取得.
BOOL _GetPosRawIO( HANDLE handle,int sector,int pos ) {
    unsigned long low,high,res ;
    ULONGLONG p ;
    p = UInt32x32To64( ( unsigned long )pos,( unsigned long )sector ) ;
    low = ( unsigned long )( p & 0x00000000ffffffff ) ;
    high = ( unsigned long )( ( p & 0xffffffff00000000 ) >> 32 ) ;
    res = 0xffffffff ;
    if( high == 0 ){
        res = SetFilePointer( handle,low,NULL,FILE_BEGIN ) ;
    }else{
        res = SetFilePointer( handle,low,(long*)&high,FILE_BEGIN ) ;
    }
    if( res == 0xffffffff ){
        if( GetLastError() != NO_ERROR ) {
            return FALSE ;
        }
    }
    return TRUE ;
}

// rawioオープン.
HANDLE OpenRawIO( int* result,const char* path ) {
    HANDLE ret = CreateFile(
        path,
        ( GENERIC_READ | GENERIC_WRITE ),// 読み込み＆書込みを許可.
        0,
        NULL,
        OPEN_ALWAYS,// ファイルがなければ作成、あれば、そのまま利用する.
        ( FILE_ATTRIBUTE_NORMAL |                   // normal.
            //FILE_FLAG_WRITE_THROUGH |               // write through.
            FILE_FLAG_RANDOM_ACCESS |               // ランダムアクセス.
            FILE_FLAG_NO_BUFFERING |                // ファイルバッファを利用しない.
            FILE_FLAG_POSIX_SEMANTICS |             // POSIX仕様.
            FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ),   // Indexを利用しない.
        NULL ) ;
    if( ret == INVALID_HANDLE_VALUE ) {
        *result = 0 ;
        return NULL ;
    }
    *result = 1 ;
    return ret ;
}

// rawioクローズ.
void CloseRawIO( HANDLE handle ) {
    CloseHandle( handle ) ;
}

// 指定位置のセクタを書き込む.
int WriteRawIO( HANDLE handle,int sector,const char* data,int pos ) {
    int ret = 0 ;
    if( _GetPosRawIO( handle,sector,pos ) == TRUE ) {
        BOOL res = WriteFile( handle,data,sector,( unsigned long* )&ret,NULL ) ;
        if( res == FALSE ) {
            ret = -1 ;
        }
        return ret ;
    }
    return -1 ;
}

// 指定位置のセクタを読み込む.
int ReadRawIO( HANDLE handle,int sector,char* data,int pos ) {
    int ret = 0 ;
    if( _GetPosRawIO( handle,sector,pos ) == TRUE ) {
        BOOL res = ReadFile( handle,data,sector,( unsigned long* )&ret,NULL ) ;
        if( res == FALSE ) {
            ret = -1 ;
        }
        return ret ;
    }
    return -1 ;
}

// 現在のファイルサイズを取得.
BOOL GetLengthRawIO( jlong* out,HANDLE handle ) {
    unsigned long sizeLow = 0 ;
    unsigned long sizeHigh = 0 ;
    *out = 0 ;
    if( handle == INVALID_HANDLE_VALUE ) {
        return FALSE ;
    }
    sizeLow = GetFileSize( handle,&sizeHigh ) ;
    if( sizeLow == 0xffffffff ) {
        if( GetLastError() != ERROR_SUCCESS ) {
            return FALSE ;
        }
    }
    *out = ( jlong )( ( ( jlong )sizeLow & 0x00000000ffffffff ) |
        ( ( ( jlong )sizeHigh & 0x00000000ffffffff ) << 32 ) ) ; ;
    return TRUE ;
}

// ファイルサイズをセット.
BOOL SetLengthRawIO( HANDLE handle,jlong len ) {
    long low,high ;
    unsigned long res ;
    if( handle == INVALID_HANDLE_VALUE ) {
        return FALSE ;
    }
    low = ( unsigned long )( len & 0x00000000ffffffff ) ;
    high = ( unsigned long )( ( len & 0xffffffff00000000 ) >> 32 ) ;
    res = SetFilePointer( handle,low,&high,FILE_BEGIN ) ;
    if( res == 0xffffffff ) {
        if(  GetLastError() != NO_ERROR ) {
            return FALSE ;
        }
    }
    if( SetEndOfFile( handle ) == FALSE ) {
        return FALSE ;
    }
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// JNI実装.
///////////////////////////////////////////////////////////////////////////////

// セクタサイズを取得.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_getSector
  (JNIEnv* env, jclass c, jbyteArray a1) {
    jint ret ;
    jbyte* path ;
    path = (*env)->GetPrimitiveArrayCritical(env, a1, NULL);
    ret = GetSector( ( char* )path ) ;
    (*env)->ReleasePrimitiveArrayCritical(env, a1, path, JNI_ABORT);
    return ret ;
}

// ハンドルからセクタ長を取得.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_getSectorByHandle
  (JNIEnv* env, jclass c, jlong a1) {
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    return h->sector ;
}

// Rawファイルオープン.
JNIEXPORT jlong JNICALL Java_org_maachang_rawio_NativeRawIO_rawOpen
  (JNIEnv* env, jclass c, jbyteArray a1, jbyteArray a2) {
    int result ;
    jbyte* path ;
    jbyte* drv ;
    HANDLE handle ;
    int sector ;
    LPMHDL ret ;
    path = (*env)->GetPrimitiveArrayCritical(env, a1, NULL);
    drv = (*env)->GetPrimitiveArrayCritical(env, a2, NULL);
    handle = OpenRawIO( &result,( const char* )path ) ;
    sector = GetSector( ( char* )drv ) ;
    (*env)->ReleasePrimitiveArrayCritical(env, a1, path, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, a2, drv, JNI_ABORT);
    if( result == 0 ) {
        return ( jlong )-1 ;
    }
    ret = ( LPMHDL )malloc( sizeof( MHDL ) ) ;
    ret->handle = handle ;
    ret->sector = sector ;
    return ( jlong )ret ;
}

// Rawファイルクローズ.
JNIEXPORT void JNICALL Java_org_maachang_rawio_NativeRawIO_rawClose
  (JNIEnv* env, jclass c, jlong a1) {
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    CloseRawIO( h->handle ) ;
    free( h ) ;
}

// Rawファイル書込み.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_rawWrite
  (JNIEnv* env, jclass c, jlong a1, jint a2, jbyteArray a3) {
    jbyte* data ;
    jint ret ;
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    data = (*env)->GetPrimitiveArrayCritical(env, a3, NULL);
    ret = WriteRawIO( h->handle,h->sector,( char* )data,a2 ) ;
    (*env)->ReleasePrimitiveArrayCritical(env, a3, data, 0);
    return ret ;

}

// Rawファイル読み込み.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_rawRead
  (JNIEnv* env, jclass c, jlong a1, jint a2, jbyteArray a3) {
    jbyte* data ;
    jint ret ;
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    data = (*env)->GetPrimitiveArrayCritical(env, a3, NULL);
    ret = ReadRawIO( h->handle,h->sector,( char* )data,a2 ) ;
    (*env)->ReleasePrimitiveArrayCritical(env, a3, data, 0);
    return ret ;
}

// Rawファイル長を取得.
JNIEXPORT jlong JNICALL Java_org_maachang_rawio_NativeRawIO_getRawLength
  (JNIEnv* env, jclass c, jlong a1) {
    jlong ret ;
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    if( GetLengthRawIO( &ret,h->handle ) == FALSE ) {
        return ( jlong )-1 ;
    }
    return ret ;
}

// Rawファイル長をセット.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_setRawLength
  (JNIEnv* env, jclass c, jlong a1, jlong a2) {
    LPMHDL h ;
    h = ( LPMHDL )a1 ;
    if( SetLengthRawIO( h->handle,a2 ) == FALSE ) {
        return -1 ;
    }
    return 0 ;
}

// CPU使用率を取得.
JNIEXPORT jdouble JNICALL Java_org_maachang_rawio_NativeRawIO_getUseCpu
  (JNIEnv* env, jclass c) {
    return useCpu() ;
}

// プロセスIDを取得.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_getProcessId
  (JNIEnv* env, jclass c) {
    return getProcessId() ;
}

