package org.maachang.dbm ;

import java.io.IOException;
import java.util.Enumeration;

import org.maachang.dbm.engine.M2Engine;
import org.maachang.dbm.engine.M2Key;
import org.maachang.dbm.engine.M2RawHash;
import org.maachang.dbm.engine.M2Sector;
import org.maachang.util.FileUtil;

/**
 * MaachangDbm実装.
 * 
 * @version 2008/01/18
 * @author masahito suzuki
 * @since MaachangDBM 1.01
 */
class MDbmImpl implements MDbm,MDbmIO {
    
    /**
     * Hash基本管理ファイル名.
     */
    private static final String BASE_FILE = "base.hash" ;
    
    /**
     * DbmEngine.
     */
    private M2Engine engine = null ;
    
    /**
     * データ展開用ディレクトリ.
     */
    private String directory = null ;
    
    /**
     * コンストラクタ.
     */
    private MDbmImpl() {
        
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * 指定条件から、MaachangDbmを生成します.
     * <BR>
     * @param directory MaachangDbm展開先のディレクトリ名を設定します.
     * @exception Exception 例外.
     */
    public MDbmImpl( String directory ) throws Exception {
        this( true,directory ) ;
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * 指定条件から、MaachangDbmを生成します.
     * <BR>
     * @param shutdown [true]の場合、シャットダウンモードをＯＮにします.
     * @param directory MaachangDbm展開先のディレクトリ名を設定します.
     * @exception Exception 例外.
     */
    public MDbmImpl( boolean shutdown,String directory ) throws Exception {
        if( directory == null || ( directory = directory.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        directory = FileUtil.getFullPath( directory ) ;
        if( directory.endsWith( "\\" ) || directory.endsWith( "/" ) ) {
            directory = directory.substring( 0,directory.length() - 1 ) ;
        }
        if( FileUtil.isDirExists( directory ) == false ) {
            FileUtil.mkdirs( directory ) ;
        }
        M2Sector sector = new M2Sector( directory ) ;
        M2RawHash hash = new M2RawHash( new StringBuilder().
            append( directory ).append( FileUtil.FILE_SPACE ).append( BASE_FILE ).toString() ) ;
        M2Key key = new M2Key( hash,sector ) ;
        M2Engine engine = new M2Engine( key,sector ) ;
        if( shutdown == true ) {
            engine.setShutdown() ;
        }
        this.engine = engine ;
        this.directory = directory ;
    }
    
    /**
     * デストラクタ.
     * <BR>
     * @exception Exception 例外.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    protected void destroy() {
        if( engine != null ) {
            engine.destroy() ;
        }
        engine = null ;
        directory = null ;
    }
    
    /**
     * MDbmオブジェクトを取得.
     * <BR>
     * @return MDbm オブジェクトが返されます.
     */
    public MDbm getParent() {
        return null ;
    }
    
    /**
     * クリアー処理.
     * <BR>
     * @exception Exception 例外.
     */
    public void close() throws Exception {
        this.destroy() ;
    }
    
    /**
     * コミット処理.
     * <BR>
     * @exception Exception 例外.
     */
    public void commit() throws Exception {
        
    }
    
    /**
     * ロールバック処理.
     * <BR>
     * @exception Exception 例外.
     */
    public void rollback() throws Exception {
        
    }
    
    /**
     * データ登録が可能かチェック.
     * <BR>
     * @exception Exception 例外.
     */
    public void check() throws Exception {
        
    }
    
    /**
     * 情報を設定.
     * <BR>
     * @param key 対象のキー情報を設定します.
     * @param value 対象の情報を設定します.
     * @exception Exception 例外.
     */
    public void put( byte[] key,byte[] value ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        engine.put( key,value ) ;
    }
    
    /**
     * 情報を削除.
     * <BR>
     * @param key 対象のキー情報を設定します.
     * @exception Exception 例外.
     */
    public void remove( byte[] key ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        engine.remove( key ) ;
    }
    
    /**
     * 情報を取得.
     * <BR>
     * @param key 対象のキー情報を設定します.
     * @return byte[] 対象の情報が返されます.
     * @exception Exception 例外.
     */
    public byte[] get( byte[] key ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        return engine.get( key ) ;
    }
    
    /**
     * 指定位置の内容を書き込む.
     * <BR>
     * @param key 対象のキー情報を設定します.
     * @param value 対象の情報を設定します.
     * @param pos 対象のポジションを設定します.
     * @param off 対象のオフセット値を設定します.
     * @param length 対象のデータ長を設定します.
     * @return int 書き込まれたデータ長が返されます.
     * @exception Exception 例外.
     */
    public int write( byte[] key,byte[] value,int pos,int off,int length )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        return engine.write( key,value,pos,off,length ) ;
    }
    
    /**
     * 指定位置の内容を読み込む.
     * <BR>
     * @param key 対象のキー情報を設定します.
     * @param value 対象の情報を設定します.
     * @param pos 対象のポジションを設定します.
     * @param off 対象のオフセット値を設定します.
     * @param length 対象のデータ長を設定します.
     * @return int 取得されたデータ長が返されます.
     * @exception Exception 例外.
     */
    public int read( byte[] key,byte[] value,int pos,int off,int length )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        return engine.read( key,value,pos,off,length ) ;
    }
    
    /**
     * 指定キーが存在するかチェック.
     * <BR>
     * @param key チェック対象のキー内容を設定します.
     * @return boolean [true]の場合、情報が存在します.
     * @exception Exception 例外.
     */
    public boolean containsKey( byte[] key) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        return engine.containsKey( key ) ;
    }
    
    /**
     * キー内容を列挙.
     * <BR><BR>
     * @return  Enumeration<byte[]> 列挙オブジェクトが返されます.
     */
    public Enumeration<byte[]> elements() {
        if( isUse() == false ) {
            return null ;
        }
        return engine.elements() ;
    }
    
    /**
     * 新しいシーケンスIDを取得.
     * <BR><BR>
     * 新しいシーケンスIDを取得します.<BR>
     * このメソッドはトランザクションに対応しません.
     * <BR>
     * @param no シーケンスNoを設定します.<BR>
     *           [0-63]まで利用可能です.
     * @return long 新しいシーケンスIDが返されます.
     * @exception Exception 例外.
     */
    public long sequenceId( int no ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "既にオブジェクトは破棄されています" ) ;
        }
        return engine.sequenceId( no ) ;
    }
    
    /**
     * 格納情報数を取得.
     * <BR>
     * @return int 格納情報数が返されます.<BR>
     *             [-1]が返された場合、オブジェクトは既に破棄されています.
     */
    public int size() {
        if( isUse() == false ) {
            return -1 ;
        }
        return engine.size() ;
    }
    
    /**
     * MaachangDbm展開ディレクトリを取得.
     * <BR>
     * @return String MaachangDbm展開ディレクトリ名が返されます.
     */
    public String getDirectory() {
        if( isUse() == false ) {
            return null ;
        }
        return directory ;
    }
    
    /**
     * MaachangDbmエンジンを取得.
     * <BR>
     * @return M2Engine MaachangDbmエンジンが返されます.
     */
    protected M2Engine getEngine() {
        if( isUse() == false ) {
            return null ;
        }
        return engine ;
    }
    
    /**
     * このオブジェクトが有効かチェック.
     * <BR>
     * @return boolean [true]の場合、有効です.
     */
    public boolean isUse() {
        if( engine == null ) {
            return false ;
        }
        return engine.isUse() ;
    }
    
    /**
     * 同期オブジェクトを取得.
     * <BR>
     * @return Object 同期オブジェクトが返されます.
     */
    protected Object sync() {
        if( engine == null ) {
            return null ;
        }
        return engine.sync() ;
    }
    
    /**
     * セクター管理ファイル数を取得.
     * <BR><BR>
     * 現在オープン中のセクター管理ファイル数を取得します.
     * <BR>
     * @return int セクター管理ファイル数が返されます.<BR>
     *             [-1]が返された場合、マネージャはクローズしています.
     */
    protected int sectorFile() {
        if( engine != null && engine.isUse() ) {
            synchronized( sync() ) {
                return engine.getSector().getSize() ;
            }
        }
        return -1 ;
    }
    
    /**
     * このオブジェクトがトランザクション対応かチェック.
     * <BR>
     * @return boolean [true]の場合、トランザクションに対応しています.
     */
    public boolean isTransaction() {
        return false ;
    }
}
