package org.maachang.leveldb ;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Leveldbファクトリ.
 * 
 * @version 2014/08/01
 * @author  masahito suzuki
 * @since   leveldb-1.00
 */
public final class LeveldbFactory {
    protected LeveldbFactory() {}
    protected static final LeveldbFactory SNGL = new LeveldbFactory() ;
    
    /**
     * ファクトリオブジェクトを取得.
     * @return LeveldbFactory ファクトリオブジェクトが返却されます.
     */
    public static final LeveldbFactory getInstance() {
        return SNGL ;
    }
    
    /** 基本フォルダ. **/
    protected String baseFolder = "" ;
    
    /** 管理オブジェクト. **/
    protected final Map<String,LevelMap> factory = new ConcurrentHashMap<String,LevelMap>() ;
    
    /** 同期オブジェクト. **/
    protected final Object sync = new Object() ;
    
    /**
     * ベースフォルダを設定.
     * @param folder 対象のフォルダを設定します.
     */
    public final void setBaseFolder( String folder ) {
        if( folder == null || ( folder = folder.trim() ).length() == 0 ) {
            baseFolder = "" ;
        }
        // 指定フォルダが存在するかチェック.
        File f = new File( folder ) ;
        if( !( f.exists() && f.isDirectory() ) ) {
            // 存在しない場合は、作成.
            f.mkdirs() ;
        }
        folder = cs( folder,"\\","/" ) ;
        if( !folder.endsWith( "/" ) ) {
            folder += "/" ;
        }
        synchronized( sync ) {
            baseFolder = folder ;
        }
    }
    
    /**
     * ベースフォルダを取得.
     * @return String ベースフォルダが返却されます.
     */
    public final String getBaseFolder() {
        synchronized( sync ) {
            return baseFolder ;
        }
    }
    
    /** 文字変換. **/
    private static final String cs( String src,String s,String d ) {
        int j,k ;
        char t = s.charAt( 0 ) ;
        int len = src.length() ;
        int lenS = s.length() ;
        StringBuilder buf = new StringBuilder( len ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( src.charAt( i ) == t ) {
                j = i ; k = 0 ;
                while( ++ k < lenS && ++ j < len && src.charAt( j ) == s.charAt( k ) ) ;
                if( k >= lenS ) {
                    buf.append( d ) ;
                    i += (lenS-1) ;
                }
                else {
                    buf.append( t ) ;
                }
            }
            else {
                buf.append( src.charAt( i ) ) ;
            }
        }
        return buf.toString() ;
    }
    
    /**
     * 全オブジェクトのクローズ.
     */
    public final void closeAll() {
        
        LevelMap m ;
        Iterator<String> it = factory.keySet().iterator() ;
        while( it.hasNext() ) {
            m = factory.get( it.next() ) ;
            if( m != null ) {
                m.close() ;
            }
        }
        factory.clear() ;
    }
    
    /**
     * オブジェクトのクローズ.
     * @param name 対象のLeveldb名を設定します.
     */
    public final void close( String name ) {
        LevelMap m = factory.remove( name ) ;
        if( m != null ) {
            m.close() ;
        }
    }
    
    /**
     * 対象のLevelMapを取得.
     * @param write_buffer_size leveldbの「write_buffer_size」値を設定します.
     * @return LevelMap LevelMapオブジェクトが返却されます.
     * @exception Exception 例外.
     */
    public final LevelMap get( String name )
        throws Exception {
        return get( name,-1,-1,-1,-1 ) ;
    }
    
    /**
     * 対象のLevelMapを取得.
     * @param write_buffer_size leveldbの「write_buffer_size」値を設定します.
     * @param max_open_files leveldbの「max_open_files」値を設定します.
     * @param block_size leveldbの「block_size」値を設定します.
     * @param block_restart_interval leveldbの「block_restart_interval」値を設定します.
     * @param name 対象のLeveldb名を設定します.
     * @return LevelMap LevelMapオブジェクトが返却されます.
     * @exception Exception 例外.
     */
    public final LevelMap get( String name,int write_buffer_size,
        int max_open_files,int block_size,int block_restart_interval )
        throws Exception {
        if( name == null || name.length() == 0 ) {
            return null ;
        }
        LevelMap map = factory.get( name ) ;
        if( map == null ) {
            synchronized( sync ) {
                if( ( map = factory.get( name ) ) == null ) {
                    map = new LevelMap( baseFolder + name,write_buffer_size,max_open_files,
                        block_size,block_restart_interval ) ;
                    factory.put( name,map ) ;
                }
            }
        }
        return map ;
    }
    
    /**
     * 対象のLeveldbをファクトリから削除.
     * @param name 対象の名前を設定します.
     */
    public final void remove( String name ) {
        if( name == null || name.length() == 0 ) {
            return ;
        }
        factory.remove( name ) ;
    }
    
    /**
     * 対象のLeveldbが存在するかチェック.
     * @param name 対象の名前を設定します.
     * @return boolean [true]の場合、存在します.
     */
    public final boolean containsKey( String name ) {
        if( name == null || name.length() == 0 ) {
            return false ;
        }
        return factory.containsKey( name ) ;
    }
    
    /**
     * 対象のLeveldbを取得.
     * @param write_buffer_size leveldbの「write_buffer_size」値を設定します.
     * @return Leveldb Leveldbオブジェクトが返却されます.
     * @exception Exception 例外.
     */
    public final Leveldb getLeveldb( String name )
        throws Exception {
        LevelMap m = get( name,-1,-1,-1,-1 ) ;
        if( m != null ) {
            return m.leveldb ;
        }
        return null ;
    }
    
    /**
     * 対象のLeveldbを取得.
     * @param write_buffer_size leveldbの「write_buffer_size」値を設定します.
     * @param max_open_files leveldbの「max_open_files」値を設定します.
     * @param block_size leveldbの「block_size」値を設定します.
     * @param block_restart_interval leveldbの「block_restart_interval」値を設定します.
     * @param name 対象のLeveldb名を設定します.
     * @return Leveldb Leveldbオブジェクトが返却されます.
     * @exception Exception 例外.
     */
    public final Leveldb getLeveldb( String name,int write_buffer_size,
        int max_open_files,int block_size,int block_restart_interval )
        throws Exception {
        LevelMap m = get( name,write_buffer_size,max_open_files,
            block_size,block_restart_interval ) ;
        if( m != null ) {
            return m.leveldb ;
        }
        return null ;
    }
    
    /**
     * ファクトリ登録名一覧を取得.
     * @return List<String> ファクトリ登録名一覧が返却されます.
     */
    public final List<String> names() {
        List<String> ret = new ArrayList<String>() ;
        Iterator<String> it = factory.keySet().iterator() ;
        while( it.hasNext() ) {
            ret.add( it.next() ) ;
        }
        return ret ;
    }
    
    /**
     * ファクトリ管理数を取得.
     * @return int ファクトリ管理数が返却されます.
     */
    public final int size() {
        return factory.size() ;
    }
    
}

