#define _GNU_SOURCE
#define _XOPEN_SOURCE 600

#define _LARGEFILE64_SOURCE 1
#include <errno.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
//#include <stdio.h>
//#include <sys/uio.h>

#include "../../include/org_maachang_rawio_NativeRawIO.h"

// ファイルオープンパーミッション.
#define PERMISSION 0666

// セクタサイズ.
#define SECTOR_SIZE 512

// I/Oポインタ.
typedef struct {
    int handle ;
    char* readBuf ;
    char* writeBuf ;
} HRAW,*LPHRAW ;

// セクタ長を取得.
int GetSector( const char* path ) {
    return SECTOR_SIZE ;
    //return -1 ;
}

// シークポイントを取得.
off64_t _GetPosRawIO( int sector,int pos ) {
    return ( off64_t )( ( off64_t )sector * ( off64_t )pos ) ;
}

// rawioオープン.
jlong OpenRawIO( int* result,char* path ) {
    LPHRAW ret ;
    int fp ;
    fp = open64( path,
        ( O_RDWR | O_DIRECT | O_NOATIME | O_CREAT | O_LARGEFILE | O_SYNC ),
        PERMISSION ) ;
    if( fp <= -1 ) {
        *result = 0 ;
        return ( jlong )-1 ;
    }
    posix_fadvise64( fp, 0, 0, POSIX_FADV_RANDOM ) ;
    ret = ( LPHRAW )malloc( sizeof( HRAW ) ) ;
    ret->handle = fp ;
    ret->readBuf = ( char* )valloc( SECTOR_SIZE ) ;
    ret->writeBuf = ( char* )valloc( SECTOR_SIZE ) ;
    *result = 1 ;
    return ( jlong )ret ;
}

// rawioクローズ.
void CloseRawIO( jlong handle ) {
    LPHRAW hraw ;
    hraw = ( LPHRAW )handle ;
    if( hraw->handle > -1 ) {
        close( hraw->handle ) ;
        free( hraw->readBuf ) ;
        free( hraw->writeBuf ) ;
    }
    hraw->readBuf = NULL ;
    hraw->writeBuf = NULL ;
    hraw->handle = -1 ;
}

// 指定位置のセクタを書き込む.
int WriteRawIO( jlong handle,int sector,char* data,int pos ) {
    LPHRAW hraw ;
    off64_t off;
    int ret;
    hraw = ( LPHRAW )handle ;
    if( hraw->handle > -1 ) {
        memcpy( hraw->writeBuf,data,sector ) ;
        off = _GetPosRawIO( sector,pos ) ;
        ret = pwrite64( hraw->handle,hraw->writeBuf,sector,off ) ;
        if( ret <= -1 ) {
            return -1 ;
        }
        return ret ;
    }
    return -1 ;
}

// 指定位置のセクタを読み込む.
int ReadRawIO( jlong handle,int sector,char* data,int pos ) {
    LPHRAW hraw ;
    off64_t off;
    int ret;
    hraw = ( LPHRAW )handle ;
    if( hraw->handle > -1 ) {
        off = _GetPosRawIO( sector,pos ) ;
        ret = pread64( hraw->handle,hraw->readBuf,sector,off ) ;
        if( ret <= -1 ) {
            return -1 ;
        }
        memcpy( data,hraw->readBuf,sector ) ;
        return ret ;
    }
    return -1 ;
}

// 現在のファイルサイズを取得.
int GetLengthRawIO( jlong* out,jlong handle ) {
    LPHRAW hraw ;
    struct stat64 buf64;
    int ret;
    hraw = ( LPHRAW )handle ;
    if( hraw->handle > -1 ) {
        ret = fstat64( hraw->handle,&buf64 );
        if( ret >= 0 ) {
            *out = buf64.st_size ;
        }
        else {
            *out = 0 ;
            ret = -1 ;
        }
        return ret ;
    }
    return -1 ;
}

// ファイルサイズをセット.
int SetLengthRawIO( jlong handle,jlong len ) {
    int ret = 0 ;
    LPHRAW hraw ;
    hraw = ( LPHRAW )handle ;
    if( hraw->handle > -1 ) {
         ret = ftruncate64( hraw->handle,len ) ;
         //if( ret > 0 ) {
         //   posix_fadvise64( hraw->handle, 0, len, POSIX_FADV_RANDOM ) ;
         //}
         return ret ;
    }
    return -1 ;
}

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

// セクタサイズを取得.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_getSector
  (JNIEnv* env, jclass c, jbyteArray a1) {
    return GetSector( NULL ) ;
}

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

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

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

// Rawファイル書込み.
JNIEXPORT jint JNICALL Java_org_maachang_rawio_NativeRawIO_rawWrite
  (JNIEnv* env, jclass c, jlong a1, jint a2, jbyteArray a3) {
    jbyte* dt;
    dt = (*env)->GetPrimitiveArrayCritical(env, a3, NULL);
    jint ret = WriteRawIO( a1,SECTOR_SIZE,dt,a2 ) ;
    (*env)->ReleasePrimitiveArrayCritical(env, a3, dt, JNI_ABORT);
    return ret ;
}

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

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

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

