package org.postgresforest.tool.cli;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.sql.SQLException;

import org.postgresforest.tool.Logger;
import org.postgresforest.tool.ForestToolCommandLine;
import org.postgresforest.tool.lib.ForestToolException;
import org.postgresforest.tool.lib.GSCdata;
import org.postgresforest.tool.util.CommandParser;
import org.postgresforest.tool.util.MessagesCommandLine;

/**
 * コマンドラインクラス コマンドの受けつけ、割り振りを行う。
 */
public class ForestCommandLine {

	private static ForestToolCommandLine ftc = null;

	private GSCdata m_gsc = null;

	/** Added by CWJ to permit \q command */
	boolean done = false;

	/** 実行するコマンド文 */
	private String ExecLine = ""; //$NON-NLS-1$


	//コマンドライン処理クラス
	private AbstractCommandProcessor m_processor;
	
	
	/**
	 * コンストラクタ (通常ログイン時) オプション情報をログイン情報に格納する
	 * 
	 * @return ForestCommandLine
	 * @param hostname
	 *            ホスト名
	 * @param portno
	 *            ポート番号
	 * @param username
	 *            ユーザ名
	 * @param userpass
	 *            ユーザパスワード
	 * @param gscname
	 *            GSC名
	 */
	public ForestCommandLine(String hostname, String portno, String username,
			String userpass, String gscname) throws ForestToolException {

		// ログイン処理
		m_gsc = new GSCdata(hostname, portno,gscname,username,userpass );

		m_processor = new GscDbCommandProcessor(m_gsc);
		// エラー出力にResourceBundleを使うためインスタンス生成
		ftc = new ForestToolCommandLine();
	}

	/**
	 * コマンドライン処理 ユーザからの入力を受けつける。
	 * 
	 * @return void
	 * @throws ClassNotFoundException
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws SQLException
	 */
	public void commandLine() throws ClassNotFoundException,	IOException, SQLException {


		// コマンドライン
		// 通常処理
		if (true) {
			/* 入力プロンプトを表示し、入力されたコマンドを逐次処理する */
			// This provides us the means of reading from stdin
			StreamTokenizer input = new StreamTokenizer(new InputStreamReader(
					System.in));
			input.resetSyntax();
			input.slashSlashComments(true); // allow // as a comment delimiter
			input.eolIsSignificant(false); // treat eol's as spaces
			input.wordChars(32, 126);
			// input.whitespaceChars(59, 59); // セミコロンをスペースには変換しない
			// バックスラッシュコマンド実行時にはセミコロンを入力する必要は無い
			// input.quoteChar(39); *** CWJ: messes up literals in query string
			// ***

			// Now the main loop.
			int tt = 0, lineno = 1;
			boolean IsPronpt = true;

			while (tt != StreamTokenizer.TT_EOF && !done) {
				/* プロンプト表示フラグに従ってプロンプト表示 */
				if (IsPronpt) {
					m_processor.printPrompt();
					IsPronpt = false;
				}

				// Here, we trap SQLException so they don't terminate the
				// application
				try {
					tt = input.nextToken();
					if (tt == StreamTokenizer.TT_WORD
							|| tt == StreamTokenizer.TT_EOL) {
						/* LF改行を受け取ったときのみプロンプト表示フラグを立てる */
						if (input.ttype == StreamTokenizer.TT_EOL) {
							IsPronpt = true;
						} else {
							processLine(input.sval);
							lineno++;
						}
					}
				} catch (IOException e) {
					Logger.error("cui.error");
					Logger.error(e.getMessage());
					Logger.trace(e);
					ExecLine = ""; //$NON-NLS-1$
				}
			}
		}
		try {
			m_gsc.close();
		}
		catch (Exception e)
		{
			Logger.error("An error has been occured while closing connection.");
			Logger.error(e.getMessage());
			Logger.trace(e);
		}

		Logger.println("Now closing the connection");
	}



	/**
	 * 入力されたステートメントを処理する This processes a statement
	 * 
	 * @return void
	 * @param line
	 *            コマンドラインより入力された文字列
	 * @throws SQLException
	 */
	private void processLine(String line) throws SQLException {

		// コメント文
		if (line.startsWith("--") || line.startsWith("#")) {
			return;
		}

		if (line.startsWith("\\")) //$NON-NLS-1$
		{
			/* バックスラッシュコマンド内でセミコロンが使われていた場合、これを削除 */
			if (line.endsWith(";")) { //$NON-NLS-1$
				ExecLine = line.substring(0, line.indexOf(';'));
			} else {
				ExecLine = line;
			}
			processSlashCommand(ExecLine);
			ExecLine = ""; //$NON-NLS-1$
			return;
		}
		if (line.indexOf(';') < 0) {
			/* 入力文字列を連結する */
			ExecLine += " " + line; //$NON-NLS-1$
		} else {
			/* セミコロンが入力されるまで入力文字列を連結する */
			ExecLine += line.substring(0, line.indexOf(';'));

			// コマンドのパース・実行
			try {
				// コマンドの実行
				m_processor = m_processor.executeCommand(ExecLine);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				ExecLine = ""; //$NON-NLS-1$
			}
		}
	}



	/**
	 * コマンドファイルを読み込み実行する
	 * 
	 * @param filename
	 *            入力されたコマンドファイル(フルパス)
	 * @throws SQLException
	 * @throws IOException
	 */
	public void processCommandFile(String filename) throws SQLException,
			IOException {
		BufferedReader br;
		String line;

		try {
			br = new BufferedReader(new FileReader(filename));
			while ((line = br.readLine()) != null) {
				processLine(line);
			}
		} catch (FileNotFoundException e) {
			Logger.error(MessagesCommandLine.getString("cui.error.command.filenotfound",
													   new Object[] { filename }));
			Logger.error(e.getMessage());
			Logger.trace(e);
		}
	}

	/**
	 * バックスラッシュコマンド(\q,\i,\fi,\fg\,\fu,\fc,\fh\,fb,\ft,\fp)を処理する This process /
	 * commands (for now just /d)
	 * 
	 * @return void
	 * @param line
	 *            コマンドラインより入力された文字列
	 * @throws SQLException
	 * 
	 */
	public void processSlashCommand(String line) throws SQLException {

		// ファイルの読み込み
		if (line.startsWith("\\i")) //$NON-NLS-1$
		{
			if (line.startsWith("\\i ")) //$NON-NLS-1$
			{
				// Display details about a table
				String file = CommandParser.extractSlashCmdArgument(line, 3);
				// fileの読み込み
				try {
					Logger.println("execute command"); //$NON-NLS-1$
					Logger.println("  file：" + file); //$NON-NLS-1$
					ExecLine = ""; //$NON-NLS-1$
					processCommandFile(file);
				} catch (IOException e) {
					Logger.error(MessagesCommandLine.getString("cui.error.command.io",
															   new Object[] { file }));
					Logger.error(e.getMessage());
					Logger.trace(e);
				}
			} else {
				// Display details about a table
				Logger.println(MessagesCommandLine.getString("cui.help.back.i"));
			}
		} else if (line.startsWith("\\f") ) { //$NON-NLS-1$
			// PostgresForest CommandLineTool用のバックスラッシュコマンド
			m_processor.processSlashCommand(line);

		} else if (line.startsWith("\\h")) { //$NON-NLS-1$
			// PostgresForest CommandLineTool用のバックスラッシュコマンド
			m_processor = m_processor.processSlashCommandHistory(line);
			
		} else if (line.equals("\\q")) // Added by CWJ to permit \q command
										// //$NON-NLS-1$
			done = true;
		else {
			// System.out.println("Unsupported \\ command: " + line);
			Logger.error(MessagesCommandLine.getString("cui.error.back.f"));
			Logger.println(MessagesCommandLine.getString("cui.help.back.f"));
		}
	}



}
