#include <stdio.h>
#include "rawio.h"

///////////////////////////////////////////////////////////////////////////////
// prototype.
///////////////////////////////////////////////////////////////////////////////

// 基本I/O系.
LONG64 _GetPosition( int sector,int pos ) ;
int _GetSector( const char* path ) ;
int _FileRead( LPMHDL mh,char* buf,LONG64 pos,int length ) ;
int _FileWrite( LPMHDL mh,const char* buf,LONG64 pos,int length ) ;

// Mapping系.
void _Lock( LPMHDL mh ) ;
void _Unlock( LPMHDL mh ) ;
void _IoLock( LPMHDL mh ) ;
void _IoUnlock( LPMHDL mh ) ;
BOOL _GetPosRawIO( LPMHDL mh,LONG64 p ) ;
BOOL _GetFileLength( LPMHDL mh ) ;
BOOL _SetFileLenght( LPMHDL mh,LONG64 len ) ;
BOOL _CreateBuffer( LPMHDL out,HANDLE handle,int sector ) ;
void _DestroyBuffer( LPMHDL mh ) ;
BOOL _ClearWritePos( LPMHDL mh,int no ) ;
BOOL _DeleteOneBuffer( BOOL destroyFlag,LPMHDL mh,int no ) ;
BOOL _DeleteBuffer( LPMHDL mh ) ;
int _UseSpaceBuffer( LPMHDL mh ) ;
int _SearchMapping( LPMHDL mh,int addExp,LONG64 pos ) ;
BOOL _CreateMapping( LPMHDL mh,int mappingPos,LONG64 pos ) ;
int _ReadMapping( char* out,LPMHDL mh,LONG64 pos ) ;
int _WriteMapping( const char* in,LPMHDL mh,LONG64 pos ) ;


///////////////////////////////////////////////////////////////////////////////
// RawI/O内部実装.
///////////////////////////////////////////////////////////////////////////////

// クリティカルセクション(同期)開始.
void _Lock( LPMHDL mh ) {
    EnterCriticalSection( (CRITICAL_SECTION*)&mh->section ) ;
}

// クリティカルセクション(同期)終了.
void _Unlock( LPMHDL mh ) {
    LeaveCriticalSection( (CRITICAL_SECTION*)&mh->section ) ;
}

// クリティカルセクション(I/O同期)開始.
void _IoLock( LPMHDL mh ) {
    EnterCriticalSection( (CRITICAL_SECTION*)&mh->ioSection ) ;
}

// クリティカルセクション(I/O同期)終了.
void _IoUnlock( LPMHDL mh ) {
    LeaveCriticalSection( (CRITICAL_SECTION*)&mh->ioSection ) ;
}

// 指定項番からSeekポジションを取得.
LONG64 _GetPosition( int sector,int pos ) {
    return ( LONG64 )UInt32x32To64( ( unsigned long )pos,( unsigned long )sector ) ;
}

// セクタ長を取得.
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 ;
}

// ファイル読み込み.
int _FileRead( LPMHDL mh,char* buf,LONG64 pos,int length ) {
    int ret ;
    BOOL res ;
    ret = -1 ;
    _IoLock( mh ) ;
    if( _GetPosRawIO( mh,pos ) ) {
        res = ReadFile( mh->handle,buf,length,( unsigned long* )&ret,NULL ) ;
        if( res == FALSE ) {
            _IoUnlock( mh ) ;
            return -1 ;
        }
    }
    _IoUnlock( mh ) ;
    return ret ;
}

// ファイル書込み.
int _FileWrite( LPMHDL mh,const char* buf,LONG64 pos,int length ) {
    int ret ;
    BOOL res ;
    ret = -1 ;
    _IoLock( mh ) ;
    if( _GetPosRawIO( mh,pos ) ) {
        res = WriteFile( mh->handle,buf,length,&ret,NULL ) ;
        if( res == FALSE ) {
            _IoUnlock( mh ) ;
            return -1 ;
        }
    }
    _IoUnlock( mh ) ;
    return ret ;
}

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

// ファイルサイズを取得.
BOOL _GetFileLength( LPMHDL mh ) {
    BOOL ret ;
    unsigned long sizeLow = 0 ;
    unsigned long sizeHigh = 0 ;
    _Lock( mh ) ;// lock.
    ret = TRUE ;
    if( mh->handle == INVALID_HANDLE_VALUE ) {
        ret = FALSE ;
        goto L_END ;
    }
    sizeLow = GetFileSize( mh->handle,&sizeHigh ) ;
    if( sizeLow == 0xffffffff ) {
        if( GetLastError() != ERROR_SUCCESS ) {
            ret = FALSE ;
            goto L_END ;
        }
    }
    mh->fileLen = ( LONG64 )( ( ( LONG64 )sizeLow & 0x00000000ffffffff ) |
        ( ( ( LONG64 )sizeHigh & 0x00000000ffffffff ) << 32 ) ) ;
L_END :
    _Unlock( mh ) ;// unlock.
    return ret ;
}

// ファイルサイズをセット.
BOOL _SetFileLenght( LPMHDL mh,LONG64 len ) {
    BOOL ret ;
    long low,high ;
    unsigned long res ;
    ret = TRUE ;
    _Lock( mh ) ;// lock.
    low = ( unsigned long )( len & 0x00000000ffffffff ) ;
    high = ( unsigned long )( ( len & 0xffffffff00000000 ) >> 32 ) ;
    if( high == 0 ) {
        res = SetFilePointer( mh->handle,low,0,FILE_BEGIN ) ;
    }
    else {
        res = SetFilePointer( mh->handle,low,(long*)&high,FILE_BEGIN ) ;
    }
    if( res == 0xffffffff ) {
        if(  GetLastError() != NO_ERROR ) {
            ret = FALSE ;
            goto L_END ;
        }
    }
    if( SetEndOfFile( mh->handle ) == FALSE ) {
        ret = FALSE ;
        goto L_END ;
    }
    mh->fileLen = len ;
L_END :
    // ファイルサイズ変更の場合はすべてのバッファを破棄する.
    //ret = _DeleteBuffer( mh ) ;
    _Unlock( mh ) ;// unlock.
    return ret ;
}

// バッファ情報を生成.
BOOL _CreateBuffer( LPMHDL out,HANDLE handle,int sector ) {
    int i ;
    out->handle = handle ;
    out->sector = sector ;
    out->len = sector * MHDL_BUF ;
    InitializeCriticalSection( (CRITICAL_SECTION*)&out->section ) ;
    InitializeCriticalSection( (CRITICAL_SECTION*)&out->ioSection ) ;
    out->average = 0 ;
    out->maxNotRangeCount = 0 ;
    out->bufRange = ( LONG64 )( ( MHDL_BUF * BUF_SIZE * ( LONG64 )sector ) * BUFFER_RANGE ) ;
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        memset( &out->bufs[ i ],0,sizeof( MBUF ) ) ;
    }
    // ファイル長を取得.
    if( _GetFileLength( out ) == FALSE ) {
        return FALSE ;
    }
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        out->bufs[ i ].usePos = -1 ;
        out->bufs[ i ].useLen = -1 ;
        out->bufs[ i ].useWriteFlag = NOT_WRITE ;
        out->bufs[ i ].expire = 0 ;
        out->bufs[ i ].buf = ( char* )malloc( out->len ) ;
        _ClearWritePos( out,i ) ;
    }
    return TRUE ;
}

// すべてのバッファを破棄.
void _DestroyBuffer( LPMHDL mh ) {
    int i ;
    if( mh->handle != NULL ) {
        for( i = 0 ; i < BUF_SIZE ; i ++ ) {
            _DeleteOneBuffer( TRUE,mh,i ) ;
        }
    }
}

// 書込みフラグをクリア.
BOOL _ClearWritePos( LPMHDL mh,int no ) {
    BOOL ret ;
    ret = TRUE ;
    // 書込みが存在する場合は、書込み処理を行う.
    if( mh->bufs[ no ].buf != NULL ) {
        memset( mh->bufs[ no ].sectorWriteFlag,NOT_WRITE,MHDL_BUF ) ;
        return TRUE ;
    }
    return FALSE ;
}

// １つのバッファを無効に設定.
BOOL _DeleteOneBuffer( BOOL destroyFlag,LPMHDL mh,int no ) {
    int i ;
    BOOL ret ;
    ret = TRUE ;
    // 書込みが存在する場合は、書込み処理を行う.
    if( mh->bufs[ no ].buf != NULL ) {
        // 書込み処理が行われている場合.
        if( mh->bufs[ no ].usePos != -1 && mh->bufs[ no ].useWriteFlag == USE_WRITE ) {
            int p ;
            LONG64 seek ;
            char* flg ;
            char* buf ;
            p = 0 ;
            seek = mh->bufs[ no ].usePos ;
            flg = ( char* )malloc( MHDL_BUF ) ;
            memcpy( flg,mh->bufs[ no ].sectorWriteFlag,MHDL_BUF ) ;
            buf = ( char* )malloc( mh->len ) ;
            memcpy( buf,mh->bufs[ no ].buf,mh->len ) ;
            // 先にバッファをクリア.
            _ClearWritePos( mh,no ) ;
            mh->bufs[ no ].usePos = -1 ;
            mh->bufs[ no ].useLen = -1 ;
            mh->bufs[ no ].useWriteFlag = NOT_WRITE ;
            mh->bufs[ no ].expire = 0 ;
            // 破棄フラグがOFFの場合.
            if( destroyFlag == FALSE ) {
                _Unlock( mh ) ;
                for( i = 0 ; i < MHDL_BUF ; i ++ ) {
                    if( flg[ i ] == USE_WRITE ) {
                        _FileWrite( mh,( char* )&(buf[p]),seek,mh->sector ) ;
                    }
                    p += mh->sector ;
                    seek += ( LONG64 )mh->sector ;
                }
                _Lock( mh ) ;
            }
            // 破棄フラグがONの場合.
            else {
                for( i = 0 ; i < MHDL_BUF ; i ++ ) {
                    if( flg[ i ] == USE_WRITE ) {
                        _FileWrite( mh,( char* )&(buf[p]),seek,mh->sector ) ;
                    }
                    p += mh->sector ;
                    seek += ( LONG64 )mh->sector ;
                }
            }
            free( flg ) ;
            flg = NULL ;
            free( buf ) ;
            buf = NULL ;
        }
        // 書込み処理が行われていない場合は無視.
        else {
            // バッファクリア.
            _ClearWritePos( mh,no ) ;
            mh->bufs[ no ].usePos = -1 ;
            mh->bufs[ no ].useLen = -1 ;
            mh->bufs[ no ].useWriteFlag = NOT_WRITE ;
            mh->bufs[ no ].expire = 0 ;
        }
        // バッファ破棄条件の場合.
        if( destroyFlag == TRUE ) {
            if( mh->bufs[ no ].buf != NULL ) {
                free( mh->bufs[ no ].buf ) ;
                mh->bufs[ no ].buf = NULL ;
            }
        }
        return ret ;
    }
    return FALSE ;
}

// バッファを無効に設定.
BOOL _DeleteBuffer( LPMHDL mh ) {
    BOOL ret ;
    int i ;
    ret = TRUE ;
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        BOOL res = _DeleteOneBuffer( FALSE,mh,i ) ;
        if( res == FALSE ) {
            ret = FALSE ;
        }
    }
    return ret ;
}

// Mappingに空きが存在するかチェック.
int _UseSpaceBuffer( LPMHDL mh ) {
    int i ;
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        if( mh->bufs[ i ].usePos == -1 ) {
            return i ;
        }
    }
    return -1 ;
}

// 指定シークポジションに対する、Mappingを検索.
int _SearchMapping( LPMHDL mh,int addExp,LONG64 pos ) {
    int i ;
    int ret = -1 ;
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        if( mh->bufs[ i ].usePos != -1 ) {
            // Mappingの範囲内.
            if( pos >= mh->bufs[ i ].usePos && pos < mh->bufs[ i ].usePos + mh->bufs[ i ].useLen ) {
                // Mapping範囲内のI/Oの場合は、Expire値を初期値にする.
                mh->bufs[ i ].expire = 0 ;
                ret = i ;
                break ;
            }
            // Mapping範囲外.
            //else {
            //    mh->bufs[ i ].expire += addExp ;
            //    if( mh->bufs[ i ].expire >= EXPIRE_BUF ) {
            //        _DeleteOneBuffer( FALSE,mh,i ) ;
            //    }
            //}
        }
    }
    // 一致するBufferが存在しない場合.
    // すべてのMappingに対してExpire値をインクリメント.
    if( ret == -1 ) {
        // Mapping範囲内で平均値範囲内の場合.
        if( mh->average < mh->bufRange + pos && mh->average + mh->bufRange > pos ) {
            for( i = 0 ; i < BUF_SIZE ; i ++ ) {
                if( mh->bufs[ i ].usePos != -1 ) {
                    mh->bufs[ i ].expire += addExp ;
                    if( mh->bufs[ i ].expire >= EXPIRE_BUF ) {
                        _DeleteOneBuffer( FALSE,mh,i ) ;
                    }
                }
            }
        }
    }
    return ret ;
}

// 指定項番に対してMapping.
BOOL _CreateMapping( LPMHDL mh,int mappingPos,LONG64 pos ) {
    int i ;
    LONG64 len ;
    int res ;
    // Mapping条件が不正な場合.
    if( mh->fileLen <= 0 || mh->fileLen <= pos ||
        mh->bufs[ mappingPos ].usePos != -1 ) {
        return FALSE ;
    }
    // バッファ可能範囲外の場合は、Mappingを作成しない.
    if( ( mh->average < mh->bufRange + pos && mh->average + mh->bufRange > pos ) == FALSE ) {
        return FALSE ;
    }
    // Mapping範囲を計算.
    len = mh->len ;
    if( len + pos >= mh->fileLen ) {
        len = mh->fileLen - pos ;
    }
    // 指定位置が他のMappingと重なる可能性があるかチェック.
    for( i = 0 ; i < BUF_SIZE ; i ++ ) {
        if( mh->bufs[ i ].usePos != -1 ) {
            // 他のMappingと開始ポイントが重なった場合.
            if( pos >= mh->bufs[ i ].usePos && pos < mh->bufs[ i ].usePos + mh->bufs[ i ].useLen ) {
                // Mapping処理は行わない.
                return FALSE ;
            }
            // 他のMappingポイントと、今回対象のMappingの長さが重なる場合.
            else if( pos < mh->bufs[ i ].usePos && pos + len >= mh->bufs[ i ].usePos ) {
                return FALSE ;
            }
        }
    }
    // Mapping領域を取得.
    res = _FileRead( mh,mh->bufs[ mappingPos ].buf,pos,( int )len ) ;
    // Mapping処理を行う.
    if( res == -1 ) {
        // Mapping失敗.
        mh->bufs[ mappingPos ].usePos = -1 ;
        mh->bufs[ mappingPos ].useLen = -1 ;
        mh->bufs[ mappingPos ].useWriteFlag = NOT_WRITE ;
        mh->bufs[ mappingPos ].expire = 0 ;
        _ClearWritePos( mh,mappingPos ) ;
        return FALSE ;
    }
    // Mapping成功.
    mh->bufs[ mappingPos ].usePos = pos ;
    mh->bufs[ mappingPos ].useLen = len ;
    mh->bufs[ mappingPos ].useWriteFlag = NOT_WRITE ;
    mh->bufs[ mappingPos ].expire = 0 ;
    _ClearWritePos( mh,mappingPos ) ;
    return TRUE ;
}

// Mappingを含んだRead処理.
int _ReadMapping( char* out,LPMHDL mh,LONG64 pos ) {
    int ret ;
    int mappingPos ;
    ret = -1 ;
    _Lock( mh ) ;// lock.
    // アクセス平均値を求める.
    if( mh->average <= 0 ) {
        mh->average = pos ;
    }
    else {
        mh->average = ( mh->average + pos ) / 2 ;
    }
    // Mappingが見つかった場合.
    if( ( mappingPos = _SearchMapping( mh,READ_EXPIRE,pos ) ) >= 0 ) {
        int p = ( int )( pos - mh->bufs[ mappingPos ].usePos ) ;
        memcpy( out,( char* )&(mh->bufs[ mappingPos ].buf[p]),mh->sector ) ;
        ret = mh->sector ;
        //mh->maxNotRangeCount = 0 ;
        goto L_SUCCESS ;
    }
    // Mapping領域が見つからない場合は、空き領域を検索し、存在する場合は、
    // そこに対して今回のMapping領域を展開させる.
    if( ( mappingPos = _UseSpaceBuffer( mh ) ) >= 0 ) {
        // 空きMappingが見つかったので、そこに今回の領域に対するMappingを行う.
        if( _CreateMapping( mh,mappingPos,pos ) ) {
            // Mapping成功.
            int p = ( int )( pos - mh->bufs[ mappingPos ].usePos ) ;
            memcpy( out,( char* )&(mh->bufs[ mappingPos ].buf[p]),mh->sector ) ;
            ret = mh->sector ;
            //mh->maxNotRangeCount = 0 ;
            goto L_SUCCESS ;
        }
    }
    // 範囲外である場合.
    //mh->maxNotRangeCount ++ ;
    //if( mh->maxNotRangeCount >= MAX_NOT_RANGE ) {
    //    mh->maxNotRangeCount = 0 ;
    //    mh->average = 0 ;
    //}
    // Mapping対象が存在せず、Mappingできなかった場合は、直接ファイルから取得.
    ret = _FileRead( mh,out,pos,mh->sector ) ; ;
    
// Mapping成功の場合.
L_SUCCESS :
    _Unlock( mh ) ;// unlock.
    return ret ;
}

// Mappingを含んだWrite処理.
int _WriteMapping( const char* in,LPMHDL mh,LONG64 pos ) {
    int ret ;
    int mappingPos ;
    ret = -1 ;
    _Lock( mh ) ;// lock.
    // アクセス平均値を求める.
    if( mh->average <= 0 ) {
        mh->average = pos ;
    }
    else {
        mh->average = ( mh->average + pos ) / 2 ;
    }
    // Mappingが見つかった場合.
    if( ( mappingPos = _SearchMapping( mh,WRITE_EXPIRE,pos ) ) >= 0 ) {
        int p = ( int )( pos - mh->bufs[ mappingPos ].usePos ) ;
        memcpy( ( char* )&(mh->bufs[ mappingPos ].buf[p]),in,mh->sector ) ;
        mh->bufs[ mappingPos ].useWriteFlag = USE_WRITE ;
        mh->bufs[ mappingPos ].sectorWriteFlag[ p / mh->sector ] = USE_WRITE ;
        ret = mh->sector ;
        //mh->maxNotRangeCount = 0 ;
        goto L_SUCCESS ;
    }
    // Mapping領域が見つからない場合は、空き領域を検索し、存在する場合は、
    // そこに対して今回のMapping領域を展開させる.
    if( ( mappingPos = _UseSpaceBuffer( mh ) ) >= 0 ) {
        // 空きMappingが見つかったので、そこに今回の領域に対するMappingを行う.
        if( _CreateMapping( mh,mappingPos,pos ) ) {
            // Mapping成功.
            int p = ( int )( pos - mh->bufs[ mappingPos ].usePos ) ;
            memcpy( ( char* )&(mh->bufs[ mappingPos ].buf[p]),in,mh->sector ) ;
            mh->bufs[ mappingPos ].useWriteFlag = USE_WRITE ;
            mh->bufs[ mappingPos ].sectorWriteFlag[ p / mh->sector ] = USE_WRITE ;
            ret = mh->sector ;
            //mh->maxNotRangeCount = 0 ;
            goto L_SUCCESS ;
        }
    }
    // 範囲外である場合.
    //mh->maxNotRangeCount ++ ;
    //if( mh->maxNotRangeCount >= MAX_NOT_RANGE ) {
    //    mh->maxNotRangeCount = 0 ;
    //    mh->average = 0 ;
    //}
    // Mapping対象が存在せず、Mappingできなかった場合は、直接ファイルに書き込む.
    ret = _FileWrite( mh,in,pos,mh->sector ) ;
    
// Mapping成功の場合.
L_SUCCESS :
    _Unlock( mh ) ;// unlock.
    return ret ;
}

///////////////////////////////////////////////////////////////////////////////
// RawI/O実装.
///////////////////////////////////////////////////////////////////////////////

// rawioクローズ.
void CloseRawIO( LPMHDL mh ) {
    if( mh->handle != NULL ) {
        _DestroyBuffer( mh ) ;
        DeleteCriticalSection( (CRITICAL_SECTION*)&mh->section ) ;
        DeleteCriticalSection( (CRITICAL_SECTION*)&mh->ioSection ) ;
        CloseHandle( mh->handle ) ;
        memset( mh,0,sizeof( MHDL ) ) ;
        free( mh ) ;
    }
}

// rawioオープン.
LPMHDL OpenRawIO( int* result,const char* path,const char* drv ) {
    LPMHDL ret ;
    int sectorSize ;
    HANDLE handle ;
    // File初期化.
    handle = 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( handle == INVALID_HANDLE_VALUE ) {
        *result = 0 ;
        return NULL ;
    }
    // MHDL初期化.
    sectorSize = _GetSector( drv ) ;
    *result = 1 ;
    ret = ( LPMHDL )malloc( sizeof( MHDL ) ) ;
    // ファイル長を取得.
    if( _CreateBuffer( ret,handle,sectorSize ) == FALSE ) {
        // ファイル長の取得に失敗した場合は、生成内容を破棄.
        CloseRawIO( ret ) ;
        *result = 0 ;
        ret = NULL ;
    }
    return ret ;
}

// 指定位置のセクタを書き込む.
int WriteRawIO( LPMHDL mh,const char* data,int pos ) {
    return _WriteMapping( data,mh,_GetPosition( mh->sector,pos ) ) ;
}

// 指定位置のセクタを読み込む.
int ReadRawIO( LPMHDL mh,char* data,int pos ) {
    return _ReadMapping( data,mh,_GetPosition( mh->sector,pos ) ) ;
}

// 現在のファイルサイズを取得.
BOOL GetLengthRawIO( LONG64* out,LPMHDL mh ) {
    _Lock( mh ) ;// lock.
    *out = mh->fileLen ;
    _Unlock( mh ) ;// unlock.
    return TRUE ;
}

// ファイルサイズをセット.
BOOL SetLengthRawIO( LPMHDL mh,LONG64 len ) {
    return _SetFileLenght( mh,len ) ;
}

// セクタ数を取得.
int GetSector( LPMHDL mh ) {
    int ret ;
    _Lock( mh ) ;// lock.
    ret = mh->sector ;
    _Unlock( mh ) ;// unlock.
    return ret ;
}

// セクタ数を取得.
int GetSectorByPath( const char* path ) {
    return _GetSector( path ) ;
}
