package org.maachang.dbm ;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Enumeration;

import org.maachang.conf.Config;
import org.maachang.conf.ConvIniParam;
import org.maachang.conf.ReadIni;
import org.maachang.dbm.engine.MDbmDefine;
import org.maachang.dbm.service.client.MDbmDriverManager;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * MaachangDbmコンソール.
 * 
 * @version 2008/05/21
 * @author masahito suzuki
 * @since MaachangDBM 1.0B
 */
class MDbmCommand {
    
    /**
     * コンストラクタ.
     */
    private MDbmCommand() {
        
    }
    
    /**
     * main.
     */
    public static final void main( String[] args ) throws Exception {
        MDbmDefine.OUT_MODE = false ;
        String opt = "-L" ;
        if( args == null || args.length <= 0 ) {
            opt = "-L" ;
        }
        else {
            opt = args[ 0 ] ;
            opt = opt.trim().toUpperCase() ;
        }
        if( opt.equals( "?" ) || opt.equals( "HELP" ) ||
            ( opt.equals( "-L" ) == false && opt.equals( "-R" ) == false ) ) {
            System.out.println( "MDBM操作コマンド" ) ;
            System.out.println() ;
            System.out.println( "mdcmd [opt]" ) ;
            System.out.println( "[opt] 接続モードを設定します." ) ;
            System.out.println( "      -L ローカル接続で生成します." ) ;
            System.out.println( "      -R リモート接続で生成します." ) ;
            return ;
        }
        new MDbmCommand().execution( opt,args ) ;
    }
    
    /**
     * コンソールOutモード.
     */
    private boolean consoleOut = true ;
    
    /**
     * MDBMローカル接続.
     */
    private static final MDbmOp localMDBM() throws Exception {
        BufferedReader br = null ;
        try {
            br = new BufferedReader( new InputStreamReader( new FileInputStream( "./conf/mdbm.conf" ),"UTF8" )  ) ;
            Config conf = new Config() ;
            ReadIni.analisys( conf,br ) ;
            br.close() ;
            br = null ;
            MDbmManager.getInstance().open( conf.get( "mdbm","directory",0 ) ) ;
            return MDbmManager.getInstance().getMDbmOp() ;
        } finally {
            if( br != null ) {
                try {
                    br.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
    /**
     * MDBMリモート接続.
     */
    private static final MDbmOp removeMDBM() throws Exception {
        BufferedReader br = null ;
        try {
            br = new BufferedReader( new InputStreamReader( new FileInputStream( "./conf/mdbm.conf" ),"UTF8" )  ) ;
            Config conf = new Config() ;
            ReadIni.analisys( conf,br ) ;
            br.close() ;
            br = null ;
            if( conf.isKeys( "mdbm","flag" ) == true &&
                ConvIniParam.getBoolean( conf.get( "mdbm","flag",0 ) ) == false ) {
                throw new IOException( "接続モードはOFFになっています" ) ;
            }
            return MDbmDriverManager.getConnectionOp( InetAddress.getByName( "127.0.0.1" ),
                conf.getInt( "mdbm","bind-port",0 ) ) ;
        } finally {
            if( br != null ) {
                try {
                    br.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
    private void execution( String opt,String[] args ) throws Exception {
        println( "[ MDBM Command ]" ) ;
        String connMode = "" ;
        MDbmOp op = null ;
        if( "-L".equals( opt ) ) {
            println( "[Local]接続Mode." ) ;
            op = localMDBM() ;
            connMode = "L" ;
        }
        else if( "-R".equals( opt ) ) {
            println( "[Remote]接続Mode." ) ;
            op = removeMDBM() ;
            connMode = "R" ;
        }
        println() ;
        
        // １行コマンド実行の場合.
        if( args.length > 1 ) {
            StringBuilder buf = new StringBuilder() ;
            int len = args.length ;
            for( int i = 1 ; i < len ; i ++ ) {
                if( i != 1 ) {
                    buf.append( " " ) ;
                }
                buf.append( args[ i ] ) ;
            }
            String s = buf.toString() ;
            buf = null ;
            executionOneLine( op,s ) ;
        }
        else {
            BufferedReader br = new BufferedReader(
                new InputStreamReader( System.in ) ) ;
            for( ;; ) {
                print( connMode+"$mdbm>" ) ;
                String input = br.readLine() ;
                // 1行処理を行う.
                if( executionOneLine( op,input ) == true ) {
                    break ;
                }
            }
        }
        op.close() ;
        
    }
    
    /**
     * １行内容の処理を行う.
     */
    private boolean executionOneLine( MDbmOp op,String input ) {
        try {
            if( input == null || ( input = input.trim() ).length() <= 0 ) {
                return false ;
            }
            ArrayList<String> lst = StringUtil.getCsv( false,input," " ) ;
            if( lst == null || lst.size() <= 0 ) {
                return false ;
            }
            String cmd = lst.get( 0 ) ;
            if( cmd == null || ( cmd = cmd.trim() ).length() <= 0 ) {
                return false ;
            }
            cmd = cmd.toLowerCase() ;
            // 指定キー取得コマンド.
            if( "get".equals( cmd ) ) {
                cmdGet( op,lst ) ;
            }
            // キー一覧リスト.
            else if( "key".equals( cmd ) ) {
                cmdKey( op,lst ) ;
            }
            // キーマスク取得一覧リスト.
            else if( "mask".equals( cmd ) ) {
                cmdMask( op,lst ) ;
            }
            // 指定内容設定.
            else if( "set".equals( cmd ) || "put".equals( cmd ) ) {
                cmdSet( op,lst ) ;
            }
            // 指定キー削除.
            else if( "remove".equals( cmd ) ) {
                cmdRemove( op,lst ) ;
            }
            // コミット.
            else if( "commit".equals( cmd ) ) {
                op.commit() ;
            }
            // ロールバック.
            else if( "rollback".equals( cmd ) ) {
                op.rollback() ;
            }
            // 指定内容設定.
            else if( "size".equals( cmd ) ) {
                cmdSize( op ) ;
            }
            else if( "cls".equals( cmd ) || "clear".equals( cmd ) ) {
                for( int x = 0 ; x < 80 ; x ++ ) {
                    println() ;
                }
            }
            else if( "?".equals( cmd ) || "help".equals( cmd ) ) {
                println( " get [key] $[FILE_NAME] 要素取得" ) ;
                println( " key [start] [end] Key一覧取得" ) ;
                println( " mask [key] [start] [end] マスクでKey一覧取得" ) ;
                println( " set/put [key] ([value] or $[FILE_NAME]) 要素設定" ) ;
                println( " remove [key] Key削除" ) ;
                println( " size 格納情報数取得" ) ;
                println( " commit コミット処理" ) ;
                println( " rollback ロールバック処理" ) ;
                println( " exit/quit コマンド終了" ) ;
                println( " cls/clear 画面クリア" ) ;
                println( " ?/help ヘルプ画面表示" ) ;
                println() ;
                println( "getとsetに対して、※1:$[FILE_NAME]を指定することで、" ) ;
                println( "ファイルI/Oで処理することができます." ) ;
                println() ;
                println( "※1:たとえば、c:\\outのファイルを扱う場合は" ) ;
                println( " $[c:\\out]" ) ;
                println( "のように設定します." ) ;
                println() ;
            }
            else if( "exit".equals( cmd ) || "quit".equals( cmd ) ) {
                op.close() ;
                println( "終了します" ) ;
                return true ;
            }
            // コマンド不正.
            else {
                printErr( cmd + ":コマンドは存在しません" ) ;
            }
        } catch( Exception e ) {
            if( consoleOut == true ) {
                e.printStackTrace() ;
            }
        }
        return false ;
    }
    
    /**
     * 要素取得.
     */
    private void cmdGet( MDbmOp op,ArrayList<String> in ) throws Exception {
        if( in.size() < 2 ) {
            printErr( "get:キー名が取得されていません" ) ;
            return ;
        }
        String key = in.get( 1 ) ;
        key = key.trim() ;
        key = cutCote( key ) ;
        if( op.containsKey( key ) == false ) {
            printErr( "get: key[" + key + "]は存在しません" ) ;
            return ;
        }
        String fileName = null ;
        if( in.size() >= 3 ) {
            fileName = in.get( 2 ) ;
            if( fileName == null || ( fileName = fileName.trim() ).length() <= 0 ) {
                fileName = null ;
            }
            else if( fileName.startsWith( "$[" ) && fileName.endsWith( "]" ) ) {
                fileName = fileName.substring( 2,fileName.length()-1 ) ;
                fileName = cutCote( fileName ) ;
            }
        }
        if( fileName == null ) {
            String s = op.getString( key ) ;
            if( s == null ) {
                printErr( "get: key[" + key + "]は存在しません" ) ;
            }
            else {
                println( s ) ;
            }
        }
        else {
            byte[] b = op.get( key ) ;
            if( b == null || b.length <= 0 ) {
                printErr( "get: key[" + key + "]は存在しません" ) ;
            }
            else {
                FileUtil.setFile( fileName,b ) ;
            }
        }
    }
    
    /**
     * キー一覧.
     */
    private void cmdKey( MDbmOp op,ArrayList<String> in ) throws Exception {
        int start = -1 ;
        int end = -1 ;
        if( in.size() >= 2 ) {
            start = ConvIniParam.getInt( in.get( 1 ) ) ;
            if( in.size() >= 3 ) {
                end = ConvIniParam.getInt( in.get( 2 ) ) ;
            }
        }
        if( start <= 0 ) {
            start = 0 ;
        }
        if( end <= 0 ) {
            end = Integer.MAX_VALUE ;
        }
        viewNormalKey( op,start,end ) ;
    }
    
    /**
     * マスクしてKey一覧を取得.
     */
    private void cmdMask( MDbmOp op,ArrayList<String> in ) throws Exception {
        String mask = null ;
        int start = -1 ;
        int end = -1 ;
        boolean noargsFlag = false ;
        if( in.size() < 2 ) {
            noargsFlag = true ;
        }
        else {
            mask = in.get( 1 ) ;
            if( mask == null || ( mask = mask.trim() ).length() <= 0 ) {
                noargsFlag = true ;
            }
            if( in.size() >= 3 ) {
                start = ConvIniParam.getInt( in.get( 1 ) ) ;
                if( in.size() >= 4 ) {
                    end = ConvIniParam.getInt( in.get( 2 ) ) ;
                }
            }
        }
        if( start <= 0 ) {
            start = 0 ;
        }
        if( end <= 0 ) {
            end = Integer.MAX_VALUE ;
        }
        if( noargsFlag == true ) {
            viewNormalKey( op,start,end ) ;
        }
        else {
            viewMasklKey( op,mask,start,end ) ;
        }
    }
    
    /**
     * 要素設定.
     */
    private void cmdSet( MDbmOp op,ArrayList<String> in ) throws Exception {
        if( in.size() < 2 ) {
            printErr( "set:キー名が取得されていません" ) ;
            return ;
        }
        if( in.size() < 3 ) {
            printErr( "set:要素が取得されていません" ) ;
            return ;
        }
        
        String key = in.get( 1 ) ;
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            printErr( "set:キー名が取得されていません" ) ;
        }
        else {
            key = cutCote( key ) ;
        }
        
        byte[] valBin = null ;
        String val = in.get( 2 ) ;
        if( val == null || ( val = val.trim() ).length() <= 0 ) {
            printErr( "set:要素が取得されていません" ) ;
        }
        if( val.startsWith( "$[" ) && val.endsWith( "]" ) ) {
            val = val.substring( 2,val.length()-1 ) ;
            val = cutCote( val ) ;
            valBin = FileUtil.getFile( val ) ;
            val = null ;
        }
        else {
            valBin = val.getBytes( "UTF8" ) ;
            val = null ;
        }
        if( valBin == null || valBin.length <= 0 ) {
            printErr( "set:要素が取得されていません" ) ;
        }
        
        if( op.containsKey( key ) == true ) {
            println( "set: key[" + key + "]を上書きします" ) ;
        }
        op.put( key,valBin ) ;
        println( "set: key[" + key + "]を設定しました." ) ;
    }
    
    /**
     * キー削除.
     */
    private void cmdRemove( MDbmOp op,ArrayList<String> in ) throws Exception {
        if( in.size() < 2 ) {
            printErr( "remove:キー名が取得されていません" ) ;
            return ;
        }
        String key = in.get( 1 ) ;
        key = key.trim() ;
        key = cutCote( key ) ;
        if( op.containsKey( key ) == false ) {
            printErr( "remove: key[" + key + "]は存在しません" ) ;
            return ;
        }
        op.remove( key ) ;
        println( "remove: key[" + key + "]を削除しました." ) ;
    }
    
    /**
     * サイズ取得.
     */
    private void cmdSize( MDbmOp op ) throws Exception {
        println( String.valueOf( op.size() ) ) ;
    }
    
    /**
     * 指定範囲のKey情報を表示.
     */
    private void viewNormalKey( MDbmOp op,int start,int end ) throws Exception {
        int cnt = 0 ;
        Enumeration<byte[]> e = op.elements() ;
        if( e == null ) {
            return ;
        }
        for (; e.hasMoreElements();) {
            if( cnt >= start ) {
                break ;
            }
            e.nextElement();
            cnt ++ ;
        }
        if( cnt < start ) {
            return ;
        }
        cnt = 0 ;
        for (; e.hasMoreElements();) {
            if( cnt >= end ) {
                break ;
            }
            byte[] b = e.nextElement() ;
            println( "[" + ( start + cnt ) + "]:" + new String( b,"UTF8" ) ) ;
            cnt ++ ;
        }
    }
    
    /**
     * 指定範囲のKey情報を表示.
     */
    private void viewMasklKey( MDbmOp op,String mask,int start,int end ) throws Exception {
        int cnt = 0 ;
        Enumeration<byte[]> e = op.elements() ;
        for (; e.hasMoreElements();) {
            if( cnt >= start ) {
                break ;
            }
            byte[] b = e.nextElement() ;
            String s = new String( b,"UTF8" ) ;
            if( s.indexOf( mask ) >= 0 ) {
                cnt ++ ;
            }
        }
        if( cnt < start ) {
            return ;
        }
        cnt = 0 ;
        for (; e.hasMoreElements();) {
            if( cnt >= end ) {
                break ;
            }
            byte[] b = e.nextElement() ;
            String s = new String( b,"UTF8" ) ;
            if( s.indexOf( mask ) >= 0 ) {
                println( "[" + ( cnt ) + "]:" + s ) ;
                cnt ++ ;
            }
        }
    }
    
    /**
     * コーテーション削除.
     */
    private String cutCote( String s ) {
        if( s.startsWith( "\'" ) && s.endsWith( "\'" ) ) {
            s = s.substring( 1,s.length() -1 ) ;
        }
        return s ;
    }
    
    /**
     * コンソールout.
     */
    private void print( String s ) {
        if( consoleOut == true ) {
            System.out.print( s ) ;
        }
    }
    
    /**
     * コンソールout.
     */
    private void println() {
        if( consoleOut == true ) {
            System.out.println() ;
        }
    }
    
    /**
     * コンソールout.
     */
    private void println( String s ) {
        if( consoleOut == true ) {
            System.out.println( s ) ;
        }
    }
    
    /**
     * コンソールout.
     */
    private void printErr( String s ) {
        if( consoleOut == true ) {
            System.out.println( s ) ;
        }
    }
}
