#include <stdlib.h>
#include <memory.h>

#include "javaLeveldb.h"
#include "leveldb/db.h"
#include "leveldb/slice.h"
#include "leveldb/iterator.h"

/** オプション定義. **/
inline void setOption( leveldb::Options* op,jint write_buffer_size,
    jint max_open_files,jint block_size,jint block_restart_interval ) {
    
    if( write_buffer_size != -1 ) {
        op->write_buffer_size = (size_t)write_buffer_size ;
    }
    if( max_open_files != -1 ) {
        op->max_open_files = (int)max_open_files ;
    }
    if( block_size != -1 ) {
        op->block_size = (size_t)block_size ;
    }
    if( block_restart_interval != -1 ) {
        op->block_restart_interval = (int)block_restart_interval ;
    }
    op->create_if_missing = true ;
}

/** Leveldb破棄. **/
void java_leveldb_destroy( jlong name,jint write_buffer_size,
    jint max_open_files,jint block_size,jint block_restart_interval ) {
    
    std::string dbName((char*)name) ;
    
    leveldb::Options op ;
    setOption( &op,write_buffer_size,max_open_files,
        block_size,block_restart_interval ) ;
    
    leveldb::DestroyDB( dbName,op ) ;
}

/** Leveldb調整. **/
void java_leveldb_repair( jlong name,jint write_buffer_size,
    jint max_open_files,jint block_size,jint block_restart_interval ) {
    
    std::string dbName((char*)name) ;
    
    leveldb::Options op ;
    setOption( &op,write_buffer_size,max_open_files,
        block_size,block_restart_interval ) ;
    
    leveldb::RepairDB( dbName,op ) ;
}

/** Leveldbオープン. **/
jlong java_leveldb_open( jlong name,jint write_buffer_size,
    jint max_open_files,jint block_size,jint block_restart_interval ) {
    
    std::string dbName((char*)name) ;
    
    leveldb::Options op ;
    setOption( &op,write_buffer_size,max_open_files,
        block_size,block_restart_interval ) ;
    
    leveldb::DB* db ;
    
    leveldb::Status status = leveldb::DB::Open(op, dbName, &db);
    if( status.ok() ) {
        return (jlong)db ;
    }
    return 0 ;
}

/** Leveldbクローズ. **/
void java_leveldb_close( jlong db ) {
    leveldb::DB* n = (leveldb::DB*)db ;
    if( n ) {
        delete n ;
    }
}

/** Leveldb要素セット. **/
jint java_leveldb_put( jlong db, jlong key, jint kLen, jlong value , jint vLen ) {
    leveldb::DB* vdb = (leveldb::DB*)db ;
    if( vdb ) {
        leveldb::Status status = vdb->Put(leveldb::WriteOptions(),
            leveldb::Slice( (const char*)key,kLen ),
            leveldb::Slice( (const char*)value,vLen ) ) ;
        if( !status.ok() ) {
            return -1 ;
        }
        return 0 ;
    }
    return -1 ;
}

/** Leveldb要素取得. **/
jint java_leveldb_get( JNIEnv* env, jlong db , jlong key, jint len, jlongArray buf, jint bufLen ) {
    leveldb::DB* vdb = (leveldb::DB*)db ;
    if( vdb ) {
        std::string v ;
        
        leveldb::Status status = vdb->Get( leveldb::ReadOptions(),
            leveldb::Slice( (const char*)key,len ),&v ) ;
        if( !status.ok() ) {
            return -1 ;
        }
        else if( v.size() == 0 ) {
            return 0 ;
        }
        
        jlong n ;
        env->GetLongArrayRegion( buf,0,1,&n ) ;
        char* b = (char*)n ;
        
        if( v.size() > bufLen ) {
            b = (char*)malloc( v.size() ) ;
            n = (jlong)b ;
            env->SetLongArrayRegion( buf,0,1,&n ) ;
        }
        memcpy( b,v.c_str(),v.size() ) ;
        return v.size() ;
    }
    return -1 ;
}

/** Leveldb要素削除. **/
jint java_leveldb_remove( jlong db, jlong key, jint len ) {
    leveldb::DB* vdb = (leveldb::DB*)db ;
    if( vdb ) {
        leveldb::Status status = vdb->Delete( leveldb::WriteOptions(),
            leveldb::Slice( (const char*)key,len ) ) ;
        if( !status.ok() ) {
            return -1 ;
        }
        return 0 ;
    }
    return -1 ;
}

/** Leveldb状態取得. **/
jint java_leveldb_property( JNIEnv* env, jlong db , jlong cmd, jint len, jlongArray buf, jint bufLen ) {
    leveldb::DB* vdb = (leveldb::DB*)db ;
    if( vdb ) {
        std::string v ;
        
        if( !vdb->GetProperty( leveldb::Slice( (const char*)cmd,len ),&v ) ) {
            return -1 ;
        }
        else if( v.size() == 0 ) {
            return 0 ;
        }
        
        jlong n ;
        env->GetLongArrayRegion( buf,0,1,&n ) ;
        char* b = (char*)n ;
        
        if( v.size() > bufLen ) {
            b = (char*)malloc( v.size() ) ;
            n = (jlong)b ;
            env->SetLongArrayRegion( buf,0,1,&n ) ;
        }
        memcpy( b,v.c_str(),v.size() ) ;
        return v.size() ;
    }
    return -1 ;
}

/** Iterator作成. **/
jlong java_leveldb_iterator( jlong db ) {
    leveldb::DB* vdb = (leveldb::DB*)db ;
    if( vdb ) {
        return (jlong)vdb->NewIterator( leveldb::ReadOptions() ) ;
    }
    return 0 ;
}

/** Iteratorクローズ. **/
void java_leveldb_itr_delete( jlong itr ) {
    leveldb::Iterator* n = (leveldb::Iterator*)itr ;
    if( n ) {
        delete n ;
    }
}

/** Iterator先頭に移動. **/
void java_leveldb_itr_first( jlong itr ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    if( it ) {
        it->SeekToFirst() ;
    }
}

/** Iterator最後に移動. **/
void java_leveldb_itr_last( jlong itr ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    if( it ) {
        it->SeekToLast() ;
    }
}

/** Iteratorシーク位置に移動. **/
void java_leveldb_itr_seek( jlong itr, jlong key,jint len ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    if( it ) {
        it->Seek( leveldb::Slice( (const char*)key,len ) ) ;
    }
}

/** Iterator現在位置カーソルの情報存在確認. **/
jint java_leveldb_itr_valid( jlong itr ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    if( it ) {
        if( it->Valid() ) {
            return 1 ;
        }
        return 0 ;
    }
    return -1 ;
}

/** Iteratorカーソルを次に移動. **/
void java_leveldb_itr_next( jlong itr ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    if( it && it->Valid() ) {
        it->Next() ;
    }
}

/** Iteratorカーソル位置のKeyを取得. **/
jint java_leveldb_itr_key( JNIEnv* env, jlong itr , jlongArray out, jint bufLen ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    
    if( !it || !it->Valid() ) {
        return -1 ;
    }

    leveldb::Slice ret = it->key() ;
    if( !it->status().ok() ) {
        return -1 ;
    }
    else if( ret.size() <= 0 ) {
        return 0 ;
    }
    
    jlong n ;
    env->GetLongArrayRegion( out,0,1,&n ) ;
    char* b = (char*)n ;
    
    if( ret.size() > bufLen ) {
        b = (char*)malloc( ret.size() ) ;
        n = (jlong)b ;
        env->SetLongArrayRegion( out,0,1,&n ) ;
    }
    memcpy( b,ret.data(),ret.size() ) ;
    return ret.size() ;
}

/** Iteratorカーソル位置のValueを取得. **/
jint java_leveldb_itr_value( JNIEnv* env, jlong itr, jlongArray out, jint bufLen ) {
    leveldb::Iterator* it = (leveldb::Iterator*)itr ;
    
    if( !it || !it->Valid() ) {
        return -1 ;
    }
    
    leveldb::Slice ret = it->value() ;
    if( !it->status().ok() ) {
        return -1 ;
    }
    else if( ret.size() <= 0 ) {
        return 0 ;
    }
    
    jlong n ;
    env->GetLongArrayRegion( out,0,1,&n ) ;
    char* b = (char*)n ;
    
    if( ret.size() > bufLen ) {
        b = (char*)malloc( ret.size() ) ;
        n = (jlong)b ;
        env->SetLongArrayRegion( out,0,1,&n ) ;
    }
    memcpy( b,ret.data(),ret.size() ) ;
    return ret.size() ;
}

