package org.postgresforest.tool.commandline;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.postgresforest.tool.bean.LoginInfo;
import org.postgresforest.tool.commandline.action.GscActions;
import org.postgresforest.tool.util.InputShadowPassword;
import org.postgresforest.tool.util.MessagesCommandLine;

/**
 * コマンドラインツール メインクラス
 * オプションの解析、処理の割り振りを行う。
 */
public class ForestTool {

	private static ForestCommandLine fc = null;

	private static String gscHost = null;
	private static String gscPort = null;
	private static String gscName = null;
	private static String userName = null;
	private static String userPass = null;
	private static String dumpFile = null;
	private static String restFile = null;
	private static boolean makeGsc = false; 
	private static String inputFile = null;
	private static boolean isInputFile = false;
			
		
	
	/**
	 * オプション定義の作成
	 * @param isRequired オプションが必須の場合:true
	 * @param hasArg オプションに値が必要な場合：true
	 * @param argName オプションの名前(Key)
	 * @param description オプションの説明
	 * @param longOpt オプションの表記名(--longOpt)
	 * @param opt オプションの表記名(-opt)
	 * @return 定義されたオプション
	 */
	private static Option makeOption(boolean isRequired, boolean hasArg, String argName, 
			String description, String longOpt, String opt)
	{
		// Builder Pattern
		Option retOption = OptionBuilder.isRequired(isRequired)
				.hasArg(hasArg)
				.withArgName(argName)
				.withDescription(description)
				.withLongOpt(longOpt)
				.create(opt);
		return retOption;
	}
	
	/**
	 * オプションのパース
	 * @param args 起動コマンド
	 * @throws ParseException
	 */
	private static void parseOptions(String[] args) throws ParseException
	{
 		Options options = new Options();

		// オプション定義
		Option hostOption = makeOption(true, true, "host", "host name", "host", "h");
		Option portOption = makeOption(true, true, "port", "port no", "port", "p");
		Option gscOption = makeOption(true, true, "gsc", "GSC name", "gsc", "g");
		Option createGscOption = makeOption(false, false, "new gsc", "create initial GSC", "init", "i");
		Option userOption = makeOption(true, true, "user", "user name", "user", "U");
		Option passOption = makeOption(false, true, "pass", "user password", "password", "W");
		Option dumpOption = makeOption(false, true, "dump", "dump GSC", "dump", "D");
		Option restoreOption = makeOption(false, true, "rest", "restore GSC", "restore", "R");
		Option fileOption = makeOption(false, true, "file", "load command file", "file", "f");
		Option helpOption = makeOption(false, false, "", "help", "help", "?");

		// 定義の追加
		options.addOption(hostOption);
		options.addOption(portOption);
		options.addOption(gscOption);		
		options.addOption(createGscOption);		
		options.addOption(userOption);		
		options.addOption(passOption);		
		OptionGroup og = new OptionGroup();
		og.addOption(dumpOption);		
		og.addOption(restoreOption);		
		og.addOption(fileOption);		
		options.addOptionGroup(og);
		options.addOption(helpOption);		
		
		// パーサー生成
		CommandLineParser parser = new GnuParser();

		// オプション解析
		CommandLine cmd = null;
		try {
			cmd = parser.parse(options, args);
			if (cmd.hasOption("?")) {
				// helpオプション
				HelpFormatter help = new HelpFormatter();
				help.printHelp("ForestConfig", options, true);
				System.exit(1);			
			}	
		} catch (ParseException e) {
			// 必須オプション未設定
			HelpFormatter help = new HelpFormatter();
			help.printHelp("ForestConfig", options, true);
			System.exit(1);
		}

		// 解析結果の格納
		if (cmd.hasOption("h")) {
			gscHost = cmd.getOptionValue("h");
		}
		if (cmd.hasOption("p")) {
			gscPort = cmd.getOptionValue("p");
		}
		if (cmd.hasOption("g")) {
			gscName = cmd.getOptionValue("g");
		}
		if (cmd.hasOption("i")) {
			makeGsc = true;
		}
		if (cmd.hasOption("U")) {
			userName = cmd.getOptionValue("U");
		}
		if (cmd.hasOption("W")) {
			userPass = cmd.getOptionValue("W");
		}
		if (cmd.hasOption("D")) {
			dumpFile = cmd.getOptionValue("D");
		}
		if (cmd.hasOption("R")) {
			restFile = cmd.getOptionValue("R");
		}
		if (cmd.hasOption("f")) {
			inputFile = cmd.getOptionValue("f");
			isInputFile = true;
		}
	}
	

	/**
	 * メインメソッド 
	 * @param args 起動コマンド
	 */
	public static void main(String[] args) {
		
		System.out.println("Welcome to PostgresForest Command Line Tool. ");

		// 起動オプションのパース
		try {
			parseOptions(args);
		}
		catch (Exception e)
		{
			System.err.println("Unknown Exception caught.\n" + e);
			e.printStackTrace();
			System.exit(1);
		}
			
		// 起動情報の表示
		System.out.println("Connecting to Database GSC = " 
				+ gscHost + ":" + gscPort + "/" + gscName );
		System.out.println("");
		
		// パスワードの取得(引数で指定していない場合)
		if (userPass == null) {
			PasswordCallback pass = new PasswordCallback("Password >", false);
			Callback[] callbacks = {pass};
			InputShadowPassword cbHandler = new InputShadowPassword();
			try {
				cbHandler.handle(callbacks);
				userPass = new String(pass.getPassword());
			} catch (IOException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			} catch (UnsupportedCallbackException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			}

//			InputPassword auth = new InputPassword();
//			InputPassword.setDefault(auth);
//			char[] passChar = auth.getPasswordAuthentication().getPassword();
//			userPass = new String(passChar);
//			System.out.println(userPass);
		}
		
		if (dumpFile != null) {
			// dump GSC
			System.out.println("dump GSC");
			System.out.println("Sorry, not implemented yet");
		} else if (restFile != null) {
			// restore GSC
			System.out.println("restore GSC");
			System.out.println("Sorry, not implemented yet");
		} else if (makeGsc == true) {
			// make new GSC
			//ログイン情報を取得する
			fc = new ForestCommandLine(gscHost, gscPort, userName, userPass, gscName);
			LoginInfo loginInfo = ForestCommandLine.getLoginInfo();
			GscActions makeGsc = new GscActions();
			makeGsc.make(loginInfo);
		} else {
			// コマンドライン起動
			fc = new ForestCommandLine(gscHost, gscPort, userName, userPass, gscName);
			try {
				fc.commandLine();
			} catch (Exception e) {
				System.err.println(MessagesCommandLine.getString("cui.error"));
				e.printStackTrace();
			}
		}
	}

	public static boolean isInputFile() {
		return isInputFile;
	}

	public static String getInputFile() {
		return inputFile;
	}

}
