package org.postgresforest.tool.cli.action;

import java.util.ArrayList;
import java.util.Iterator;

import org.postgresforest.tool.Logger;
import org.postgresforest.tool.lib.Database;
import org.postgresforest.tool.lib.ForestToolException;
import org.postgresforest.tool.lib.GSCdata;
import org.postgresforest.tool.lib.Instance;
import org.postgresforest.tool.lib.Table;
import org.postgresforest.tool.util.CommandParser;
import org.postgresforest.tool.util.MessagesCommandLine;
import org.postgresforest.tool.util.Misc;
import org.postgresforest.tool.util.Parser;
import org.postgresforest.tool.util.Table2StringUtil;

/**
 * データベースへのコマンドラインツール用のアクションを提供するクラス
 */
public class DbActions {

	static private final String[] SHOW_DB_HEADER = { "DBNAME", "SERVERID", "HOST", "PORT"}; 

	
	/**
	 * DBの作成
	 * @param cp コマンド解析結果
	 * @param gsc GSCヘの接続情報
	 */	
	public static void create(CommandParser cp, GSCdata gsc) {
		//DB名取得
		String dbName = cp.getDB();
		if(dbName == null || dbName.equals("")){
			//引数が不正
			// FIXME:
			Logger.error("invalid argument.");
			return;
		}

		//オプション取得
		String option = ""; //$NON-NLS-1$
		Iterator it = cp.getOption().iterator();
		while(it.hasNext()){
			option += (String)it.next();
		}
		
		
		ArrayList chkServerList = cp.getServer();
		// 表示用のサーバリストの作成
		String serverListStr = "";
		int ids[] = new int[chkServerList.size()];
		for (int i=0; i<ids.length;i++) {
			String idStr = (String)chkServerList.get(i);
			ids[i] =  Integer.parseInt(idStr);
			serverListStr += idStr + ",";
		}


		//DB作成処理
		try {
			Database db;
			if(option.equals("")){
				db = gsc.createDatabase(ids,dbName);
			}else{
				db = gsc.createDatabase(ids,dbName,option);
			}
			if(db == null){
				Logger.error(MessagesCommandLine.getString("cui.error.db.create",
														   new Object[] {dbName, serverListStr}));
				return;
			}
			
		} catch (Exception e) {
			Logger.error(MessagesCommandLine.getString("cui.error.db.create",
													   new Object[] {dbName, serverListStr}));
			return;
		}

		Logger.println(MessagesCommandLine.getString("cui.message.db.create",
													 new Object[] {dbName, serverListStr}));
	}
	
	/**
	 * DBの削除
	 * @param cp コマンド解析結果
	 * @param gsc GSCヘの接続情報
	 */	
	public static void drop(CommandParser cp, GSCdata gsc) {

		//DB名取得
		String dbName = cp.getDB();
		if(dbName == null || dbName.equals("")){
			//引数が不正
			// FIXME:
			Logger.error("invalid argument.");
			return;
		}
		
		//コマンドラインから強制フラグを取得
		boolean force = cp.isForce();

		
		//データベース削除
		if( ! gsc.dropDatabase(dbName, force) ){
			Logger.error(MessagesCommandLine.getString("cui.error.db.drop", new Object[] {dbName}) );
			return;
		}

		Logger.println(MessagesCommandLine.getString("cui.message.db.drop", new Object[] {dbName}) );
	}

	/**
	 * DBのリストア
	 * @param cp コマンド解析結果
	 * @param gsc GSCヘの接続情報
	 */	
	public static void restore(CommandParser cp, GSCdata gsc) {
		Logger.warning("restore db ");
		Logger.warning("Not implemented yet");

		/**
		//DB名取得
		String dbName = cp.getDB();
		if(dbName == null || dbName.equals("")){
			//引数が不正
			// FIXME:
			Logger.error("invalid argument.");
			return;
		}

		//オプション取得
		String option = ""; //$NON-NLS-1$
		Iterator it = cp.getOption().iterator();
		while(it.hasNext()){
			option += (String)it.next();
		}

		//ダンプファイル名取得
		ArrayList dumpFileList = cp.getDumpFile();
		
		ArrayList chkServerList = cp.getServer();
		// 表示用のサーバリストの作成
		String serverListStr = "";
		int ids[] = new int[chkServerList.size()];
		for (int i=0; i<ids.length;i++) {
			String idStr = (String)chkServerList.get(i);
			ids[i] =  Integer.parseInt(idStr);
			serverListStr += idStr + ",";
		}
		

		// DB選択とダンプファイルの整合性チェック
		// FIXME:
		//validateDumpFile(addDbInfo);
		
		//DB作成処理
		try {
			Database db = null;
			if(option.equals("")){
				db = gsc.createDatabase(ids,dbName);
			}else{
				db = gsc.createDatabase(ids,dbName,option);
			}
			if(db == null){
				//@@指定されたDBはすでに存在している
			}
			
		} catch (Exception e) {
			Logger.error(MessagesCommandLine.getString("cui.error.db.restore",
			new Object[] {dbName, serverListStr}) );
			Logger.error(e.getMessage());
			Logger.trace(e);
			return;
		}

		Logger.println(MessagesCommandLine.getString("cui.message.db.restore", new Object[] {dbName, serverListStr}) );
		**/
		
	}
	
	/**
	 * DBの表示
	 * @param gsc GSCヘの接続情報
	 */	
	public static void show(GSCdata gsc) {

		String[] dbNames;

		//データベース名一覧を取得
		try {
			dbNames = gsc.getDatabaseNames();
		} catch (ForestToolException e) {
			Logger.error(MessagesCommandLine.getString("cui.error.gsc.show")) ;
			Logger.error(e.getMessage());
			Logger.trace(e);
			return;
		}

		//DB情報表示
		Logger.println(MessagesCommandLine.getString("cui.message.db.show")) ; //$NON-NLS-1$
		Table2StringUtil table2StringUtil = new Table2StringUtil( SHOW_DB_HEADER );

		//IDからインスタンス情報取得し表示
		
		for (int i = 0; i < dbNames.length; i++) {
			Database d = null;

			try {
				d = gsc.getDatabase(dbNames[i]);
			}
			catch (Exception e)
			{
				Logger.error(e.getMessage());
				Logger.trace(e);
				return;
			}

			int[] ids = d.getInstanceIds();

			for (int j=0 ; j<ids.length ; j++)
			{
				Instance instance = gsc.getInstance(ids[j]);

				if (instance == null)
				{
					//インスタンス情報の取得失敗
					Logger.error(MessagesCommandLine.getString("cui.error.gsc.show"));
					return;
				}

				ArrayList row = new ArrayList();
				
				row.add(dbNames[i]);
				row.add(new Integer(instance.getId()));
				row.add(instance.getHostName());
				row.add(instance.getPortNumber());
				
				table2StringUtil.addRow(row);
			}
		}

		table2StringUtil.print();
	}

	/**
	 * DBインスタンスの追加
	 * @param cp コマンド解析結果
	 * @param gsc GSCヘの接続情報
	 */	
	public static void createDbInstance(CommandParser cp, GSCdata gsc) {

		//DB名取得
		String dbName = cp.getDB();
		if (dbName == null || dbName.equals(""))
		{
			//引数が不正
			// FIXME:
			Logger.error("invalid argument.");
			return;
		}

		//インスタンスIDの取得
		int instanceId = Integer.parseInt((String)cp.getServer().get(0));

		//コマンドラインから強制フラグを取得
		boolean force = cp.isForce();
		
		Database database = null;
		try {
			database = gsc.getDatabase(dbName);
		}
		catch (Exception e)
		{
			//FIXME:
			Logger.error(e.getMessage());
			Logger.trace(e);
			return ;
		}
		if (database==null) {
			//FIXME:
			Logger.error("Databese NotFound!");
			return ;
		}
		
		Instance instance = gsc.getInstance(instanceId);
		if (instance == null) {
			//インスタンス情報の取得失敗
			//FIXME:
			Logger.error(MessagesCommandLine.getString("instance notfound!"));
			return;
		}
		
		/*
		 * 既存のデータベースへインスタンスを追加
		 */
		if( ! database.addInstance(instance,force) ){
			Logger.error(MessagesCommandLine.getString("cui.error.db.instance.add",
													   new Object[] {dbName, Integer.toString(instanceId)}));
			return;
		}

		Logger.println(MessagesCommandLine.getString("cui.message.db.instance.add",
													 new Object[] {dbName, Integer.toString(instanceId)}));
	}
	
	/**
	 * DBインスタンスの削除
	 * @param cp コマンド解析結果
	 * @param gsc GSCヘの接続情報
	 */	
	public static void  dropDbInstance(CommandParser cp, GSCdata gsc) {

		//DB名取得
		String dbName = cp.getDB();
		if(dbName == null || dbName.equals("")){
			//引数が不正
			// FIXME:
			Logger.error("invalid argument.");
			return;
		}

		int instanceId = Integer.parseInt((String)cp.getServer().get(0));

		
		//コマンドラインから強制フラグを取得
		boolean force = cp.isForce();
		
		//データベース削除
		if( ! gsc.dropDatabase(dbName, instanceId, force) ){
			Logger.error(MessagesCommandLine.getString("cui.error.db.instance.delete",
													   new Object[] {dbName, Integer.toString(instanceId)}));
			return;
		}

		Logger.println(MessagesCommandLine.getString("cui.message.db.instance.delete",
													 new Object[] {dbName, Integer.toString(instanceId)}));
	}
	
	/**
	 * DDLコマンドの実行
     * 入力情報"ddlform"(DynaValidatorForm)の、DDLをユーザデータベースに対して実行する。
     * DDL文は構文解析を行い、各DDLに対し以下のように処理す津。
     * ・CREATE TABLE
     *   GSCへテーブル情報（forest_tablepart,forest_tablepartdtl）を挿入する
     *   DDLを各ユーザデータベースで実行する。
     * ・DROP TABLE
     *   GSCへテーブル情報（forest_tablepart,forest_tablepartdtl）を削除する
     *   DDLを各ユーザデータベースで実行する。
     * ・ALTER TABLE
     *   指定されたテーブルが、パーティション化テーブルならば
     * 　テーブル名を実テーブルに置換し、実テーブル数分DDLを各ユーザデータベースで実行する。
     * ・CREATE INDEX
     *   指定されたテーブルが、パーティション化テーブルならば
     * 　テーブル名を実テーブル、インデックス名をインデックス名_00（パーティションNo）に置換し、
     * 　実テーブル数分DDLを各ユーザデータベースで実行する。
     * ・DROP INDEX
     *   指定されたインデックスが定義されたテーブルを特定し、
     *   テーブルが、パーティション化テーブルならば
     * 　テーブル名を実テーブル、インデックス名をインデックス名_00（パーティションNo）に置換し、
     * 　実テーブル数分DDLを各ユーザデータベースで実行する。
     * ・その他
     *   DDLを各ユーザデータベースで実行する。
	 * @param gsc GSCヘの接続情報
	 * @param command DDLコマンド
	 * @param selectDb データベース名
	 */	
	public static void executeDdl( String command, Parser parser, Database database) {
		
		
		//ユーザーデータベース名を取得
		

		try {

			Logger.println(MessagesCommandLine.getString("message.ddl.header")); //$NON-NLS-1$
			Logger.println(command);

			boolean statusOk = false;
			
			String tableName = null; 
			int type = parser.getType();
			switch (type) {
				case Parser.CREATE_VIEW ://CREATE VIEW を多重化テーブルとして登録する
				case Parser.CREATE_TABLE :

					Table table = database.createTable(command);
					if(table != null){
						statusOk = true;
					}
						

					break;

				case Parser.DROP_VIEW ://CREATE VIEW を多重化テーブルとして登録する
				case Parser.DROP_TABLE :
					//入力されたDDL文が、DROP TABLE文だった場合、forest_tablepartテーブルとforest_tablepartdtlテーブルよりレコードを削除します。
							
					tableName = (String)parser.getTables().get(0);
					statusOk = database.dropTable(tableName);

					break;

				case Parser.ALTER_TABLE :

					tableName = (String)parser.getTables().get(0);

					Table table2 = database.getTable(tableName);
					statusOk = table2.alter(command) ;
						

					break;

					
				case Parser.CREATE_INDEX :


					tableName = (String)parser.getTables().get(0);

					Table table3 = database.getTable(tableName);
					statusOk = table3.createIndex(command);
						
						
					break;

				case Parser.ALTER_INDEX :
				case Parser.DROP_INDEX :
					String dropIndexName = parser.getReplaceWord();
					String[] tables;
					//テーブル名のリストを取得
					try {
						tables = database.getTableNames();
						
					} catch (ForestToolException e) {
						//テーブル情報の取得に失敗しました
						//FIXME:
						Logger.error("Can't get table name(s) from GSC.");
						Logger.error(e.getMessage());
						Logger.trace(e);
						break;
					}
					if(tables.length == 0){
						//対象となるテーブルが定義されていません
						//FIXME:
						Logger.error("No table was specified to be dropped.");
						break;
					
					}
					
					//テーブルごとにINDEXのリストを取得して、指定されたINDEXが存在したら
					//DROP INDEX実行
					
					for (int i = 0; i < tables.length; i++) {
						//テーブル名からテーブル情報取得
						Table table4 = database.getTable(tables[i]);

						//指定したINDEXを持っている？
						String[] indexNames = table4.getIndexNames();
						if( Misc.containsIgnoreCase(dropIndexName,indexNames) ){

							if(type == Parser.ALTER_INDEX){
								//ALTER INDEX実行
								statusOk = table4.alter(command);
								
							}else if(type == Parser.DROP_INDEX){
								//DROP INDEX実行
								statusOk = table4.dropIndex(dropIndexName);
							}
							break;
						}
							
					}
					break;
				    
				    
				default :

					Logger.warning("execute DDL");
					Logger.warning("Not implemented yet");

					// FIXME: 
					
					break;

			}

			Logger.println(MessagesCommandLine.getString("message.ddl.result"));

			if( statusOk ) {
			    Logger.println(MessagesCommandLine.getString("message.ddl.execute") );
			}else{
			    Logger.error(MessagesCommandLine.getString("message.ddl.error.detail") );
			    Logger.error("\n");
				// FIXME:
				//				continue;
			}
			Logger.println("\n");

			// FIXME:
			//			System.out.println(MessagesCommandLine.getString("cui.message.db.ddl.execute", new Object[] {dbName})) ;				 //$NON-NLS-1$
		
		
		} catch (Exception e) {
			Logger.error(e.getMessage());
			Logger.trace(e);
			// FIXME:
			//System.out.println(MessagesCommandLine.getString("cui.error.db.ddl.execute", new Object[] {dbName})) ;				 //$NON-NLS-1$
		}				
	}

	


    
}
