package org.maachang.mimdb.core;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Mimdbテーブル管理.
 * 
 * @version 2013/10/14
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public class MimdbTableManager implements TableManager {
    /** 自オブジェクト. **/
    private static final MimdbTableManager SNGL = new MimdbTableManager() ;
    
    /** メモリテーブル管理. **/
    private final Map<String,MimdbUpdateLock> tablesManager = new ConcurrentHashMap<String,MimdbUpdateLock>( 64 ) ;
    
    /** メモリーテーブル情報. **/
    private final Map<String,MimdbTable> tables = new ConcurrentHashMap<String,MimdbTable>( 64 ) ;
    
    /**
     * コンストラクタ.
     */
    private MimdbTableManager() {
        
    }
    
    /**
     * オブジェクトを取得.
     * @return MimdbTableManager オブジェクトが返却されます.
     */
    public static final MimdbTableManager getInstance() {
        return SNGL ;
    }
    
    /**
     * オブジェクトを破棄.
     */
    public void destroy() {
        tables.clear() ;
        tablesManager.clear() ;
    }
    
    /** テーブル名を取得 **/
    private static final String tableName( String name ) {
        if( name == null || ( name = name.trim().toLowerCase() ).length() <= 0 ) {
            throw new IllegalArgumentException( "テーブル名が不明です" ) ;
        }
        return name ;
    }
    
    /** テーブル名を取得 **/
    private static final String tableNameNoError( String name ) {
        if( name == null || ( name = name.trim().toLowerCase() ).length() <= 0 ) {
            return null ;
        }
        return name ;
    }
    
    /**
     * テーブルが登録されているかチェック.
     * @param name 対象のテーブルを設定します.
     * @return boolean [true]の場合、登録されています.
     */
    public boolean isTable( String name ) {
        if( ( name = tableNameNoError( name ) ) == null ) {
            return false ;
        }
        return tables.containsKey( name ) ;
    }
    
    /**
     * 書き込みロックを開始.
     * @param name 書き込みロックを行うテーブル名を設定します.
     * @return boolean [true]の場合、ロックが成功しました.
     */
    protected boolean begin( String name ) {
        return begin( name,false ) ;
    }
    
    /**
     * 書き込みロックを開始.
     * @param name 書き込みロックを行うテーブル名を設定します.
     * @param notCreate [true]を設定した場合、テーブル名が
     *                  存在しない場合は、ロック情報を新規作成します.
     * @return boolean [true]の場合、ロックが成功しました.
     */
    protected boolean begin( String name,boolean notCreate ) {
        name = tableName( name ) ;
        MimdbUpdateLock lock = tablesManager.get( name ) ;
        if( lock == null ) {
            // テーブル管理オブジェクトに新規登録を許可する場合は、
            // 情報追加を行い、ロック開始.
            if( notCreate ) {
                lock = new MimdbUpdateLock() ;
                tablesManager.put( name,lock ) ;
            }
            else {
                return false ;
            }
        }
        lock.writeBegin() ;
        return true ;
    }
    
    /**
     * 書き込みロックを解除.
     * @param name 書き込みロック解除を行うテーブル名を設定します.
     * @return boolean [true]の場合、アンロックが成功しました.
     */
    protected boolean end( String name ) {
        name = tableName( name ) ;
        MimdbUpdateLock lock = tablesManager.get( name ) ;
        if( lock == null ) {
            return false ;
        }
        // 書き込みロック解除時において、テーブル登録が行われていない場合は、
        // テーブル管理オブジェクトは削除.
        if( !tables.containsKey( name ) ) {
            tablesManager.remove( name ) ;
        }
        lock.writeEnd() ;
        return true ;
    }
    
    /**
     * テーブル更新IDを更新.
     * @param name 対象のテーブル名を設定します.
     * @return long 新しいテーブル更新IDが返却されます.
     *              [-1L]が返却された場合は、テーブル名は存在しません.
     */
    protected long updateId( String name ) {
        name = tableName( name ) ;
        MimdbUpdateLock lock = tablesManager.get( name ) ;
        if( lock == null ) {
            return -1L ;
        }
        return lock.update() ;
    }
    
    /**
     * テーブル更新IDを以前の状態に戻す.
     * @name 対象のテーブル名を設定します.
     * @param id 戻し対象のテーブルIDを設定します.
     */
    protected void rollbackId( String name,long id ) {
        name = tableName( name ) ;
        MimdbUpdateLock lock = tablesManager.get( name ) ;
        if( lock == null ) {
            return ;
        }
        lock.setDbId( id ) ;
    }
    
    /**
     * テーブル更新IDを取得.
     * @param name 対象のテーブル名を設定します.
     * @return long 新しいテーブル更新IDが返却されます.
     *              [-1L]が返却された場合は、テーブル名は存在しません.
     */
    public long getId( String name ) {
        name = tableName( name ) ;
        MimdbUpdateLock lock = tablesManager.get( name ) ;
        if( lock == null ) {
            return -1L ;
        }
        return lock.getDbId() ;
    }
    
    /**
     * テーブル登録.
     * @param table 登録対象のテーブルを設定します.
     */
    public void put( MimdbTable table ) {
        if( table == null ) {
            throw new IllegalArgumentException( "テーブルは存在しません" ) ;
        }
        String name = table.name ;
        begin( name,true ) ;
        try {
            tables.put( name,table ) ;
        } finally {
            end( name ) ;
        }
    }
    
    /**
     * 登録テーブル情報の取得.
     * @param name テーブル名を設定します.
     * @return MimdbTable テーブル情報が返されます.
     */
    public BaseTable get( String name ) {
        MimdbUpdateLock lock = tablesManager.get( ( name = tableName( name ) ) ) ;
        if( lock == null ) {
            return null ;
        }
        MimdbTable ret ;
        lock.readBegin() ;
        try {
            ret = tables.get( name ) ;
        } finally {
            lock.readEnd() ;
        }
        return ret ;
    }
    
    /**
     * 登録テーブル情報を削除.
     * @param name テーブル名を設定します.
     * @return boolean [true]の場合、削除できました.
     */
    public boolean remove( String name ) {
        MimdbUpdateLock lock = tablesManager.get( ( name = tableName( name ) ) ) ;
        if( lock == null ) {
            return false ;
        }
        lock.writeBegin() ;
        try {
            tables.remove( name ) ;
            tablesManager.remove( name ) ;
        } finally {
            lock.writeEnd() ;
        }
        return true ;
    }
    
    /**
     * 登録テーブル数を取得.
     * @return int 登録されているテーブル数が返却されます.
     */
    public int size() {
        return tables.size() ;
    }
    
    /**
     * 登録テーブル一覧を取得.
     * @return String[] 登録されているテーブル名一覧が返却されます.
     */
    public String[] getTables() {
        int len = tables.size() ;
        if( len == 0 ) {
            return null ;
        }
        int cnt = 0 ;
        String[] ret = new String[ len ] ;
        Iterator<String> n = tables.keySet().iterator() ;
        if( n.hasNext() ) {
            ret[ cnt ++ ] = n.next() ;
        }
        return ret ;
    }
    
}

