package org.postgresforest.tool.lib;

import java.sql.*;
import java.util.ArrayList;
import org.postgresforest.tool.ArrayUtil;
import org.postgresforest.tool.Logger;

public class Instance {
	GSCdata gsc = null;
	int id = -1;
	String hostName = null;
	String portNumber = null;
	int status = -1;
	Connection con = null;

	/**
	 * コンストラクタ。
	 * インスタンスIDが未定のインスタンスとしてInstanceオブジェクトを生成する。
	 *
	 * @param gsc GSCdataオブジェクト
	 * @param hostName インスタンスのホスト名
	 * @param portNumber インスタンスのポート番号
	 */	 
    public Instance(GSCdata gsc, String hostName, String portNumber)
    {
        this(gsc, -1, hostName, portNumber);
    }

	/**
	 * コンストラクタ。
	 * インスタンスIDが指定してInstanceオブジェクトを生成する（主に内部生成用）。
	 *
	 * @param gsc GSCdataオブジェクト
	 * @param id インスタンスのインスタンスID
	 * @param hostName インスタンスのホスト名
	 * @param portNumber インスタンスのポート番号
	 */	 
    public Instance(GSCdata gsc, int id, String hostName, String portNumber)
    {
        this.gsc        = gsc;

        this.id         = id;
        this.hostName   = hostName;
        this.portNumber = portNumber;
    }

	/**
	 * このインスタンス上の指定したデータベースへ接続し、
	 * Connectionオブジェクトを返却する。
	 *
	 * このインスタンス内のデータベースへの操作を行う場合に使用する。
	 *
	 * @param dbName データベース名
	 * @param user ユーザ名
	 * @param pass パスワード
	 *
	 * @return Connection 指定したデータベースへのConnectionオブジェクト
	 */	 
    protected Connection getDatabaseConnection(String dbName,
											   String user, String pass)
        throws ForestToolException
    {
		Connection con = null;

		try {
            Class.forName("org.postgresql.Driver");

			con = DriverManager.getConnection("jdbc:postgresql://" + hostName + ":" + portNumber + "/" + dbName,
											  user, pass);

			Logger.debug("connected: jdbc:postgresql://" + hostName + ":" + portNumber + "/" + dbName);
		}
		catch (Exception e)
		{
			throw new ForestToolException(e);
		}

		return con;
    }


	/**
	 * インスタンスのインスタンスIDを取得する。
	 *
	 * @return int インスタンスID
	 */
	public int getId()
	{
		return id;
	}

	/**
	 * インスタンスのインスタンスIDを設定する。
	 *
	 * @param id インスタンスID
	 */
	public boolean setId(int id)
	{
		this.id = id;
		return setParam("serverid", id);
	}

	/**
	 * インスタンスのホスト名を取得する。
	 *
	 * @return String ホスト名
	 */
	public String getHostName()
	{
		String url = getParam("url");
		if ( url!=null )
			hostName = url.substring(2, url.indexOf(':'));

		return hostName;
	}

	/**
	 * インスタンスのホスト名を設定する。
	 *
	 * @param hostName ホスト名
	 */
	public boolean setHostName(String hostName)
	{
		this.hostName = hostName;
		return setParam("url", "//" + hostName + ":" + portNumber + "/");
	}

	/**
	 * インスタンスのポート番号を取得する。
	 *
	 * @return String ポート番号
	 */
	public String getPortNumber()
	{
		String url = getParam("url");
		if ( url!=null )
			portNumber = url.substring(url.indexOf(':')+1, url.length()-1);

		return portNumber;
	}

	/**
	 * インスタンスのポート番号を設定する。
	 *
	 * @param portNumber ポート番号
	 */
	public boolean setPortNumber(String portNumber)
	{
		this.portNumber = portNumber;
		return setParam("url", "//" + hostName + ":" + portNumber + "/");
	}

	/**
	 * インスタンスのステータスを取得する。
	 *
	 * @return int インスタンスのステータス
	 */
    public int getStatus()
    {
        return Integer.parseInt( getParam("status") );
    }

	/**
	 * インスタンスのステータスを設定する。
	 *
	 * @param status ステータス（稼働中：1、障害中：-1）
	 */
    public boolean setStatus(int status)
    {
		this.status = status;

		return setParam("status", status);
    }


    /**
     * 指定したオプションでユーザデータベースを作成する。
     *
     * データベースを作成する際、"forest_template" データベースをテンプレートとして使用する。
     *
     * @param dbName データベース名
     *
     * @return boolean 作成に成功したらtrue、失敗したらfalse
     */
	public boolean createDatabase(String dbName)
		throws ForestToolException
	{
		return createDatabase(dbName, null);
	}

	/**
	 * 指定したオプションでユーザデータベースを作成する。
	 *
	 * データベースを作成する際、"forest_template" データベースをテンプレートとして使用する。
	 * データベースはすべて小文字で作成される。
	 *
	 * @param dbName データベース名
	 * @param option データベース作成時オプション(CREATE DATABASEコマンドに引き渡される文字列)
	 *
	 * @return boolean 作成に成功したらtrue、失敗したらfalse
	 */
	public boolean createDatabase(String dbName, String option)
		throws ForestToolException
	{
		dbName = dbName.toLowerCase();

		try {
			String url = "jdbc:postgresql://" + hostName + ":" + portNumber + "/" + GSCdata.TemplateDatabase;

			con = DriverManager.getConnection(url, gsc.getUser(), gsc.getPassword());

			String sql = "CREATE DATABASE " + dbName + " WITH TEMPLATE forest_template ";

			if ( option != null )
				sql = sql + option;

			Logger.debug("createDatabase: " + sql);

			Statement stmt = con.createStatement();

			int rc = stmt.executeUpdate(sql);

			con.commit();
			con.close();
		}
		catch (Exception e)
		{
			throw new ForestToolException(e);
		}

		return true;
	}

	/**
	 * インスタンスの保持しているデータベース名の一覧を取得する
	 *
	 * @return String[] データベース名を保持したStringの配列
	 */
	public String[] getDatabaseNames()
	{
		String sql = "SELECT dbname FROM forest_servdb WHERE serverid=" + id;

		ArrayList a = new ArrayList();

		try {
			ResultSet rs = gsc.executeQueryGSC(sql);

			while ( rs.next() )
			{
				a.add( rs.getString(1) );
			}

			rs.close();
		}
		catch (Exception e)
		{
			Logger.error(e.getMessage());
		}

		return ArrayUtil.array2stringarray(a);
	}

	public String toString()
	{
        return "Instance[" + id + "] " + hostName + ":" + portNumber;
	}
	
	/**
	 * forest_serverへのパラメータの設定を行う(整数型)
	 */
	private boolean setParam(String param, int value)
	{
		return setParam(param, "" + value);
	}

	/**
	 * forest_serverへのパラメータの設定を行う(文字列型)
	 */
	private boolean setParam(String param, String value)
	{
		if ( id<0 )
		{
			Logger.debug("Instance#setParam(): serverid is negative. - " + toString());
			return false;
		}

		try {
			int rc;

			rc = gsc.executeUpdateGSC("UPDATE forest_server SET " + param + "='" + value + "' " +
									  " WHERE serverid=" + id);
		}
		catch (Exception e)
		{
			Logger.error(e.getMessage());
			Logger.trace(e);

			return false;
		}

		return true;
	}

	/**
	 * forest_serverからのパラメータの取得を行う(文字列型)
	 */
	private String getParam(String param)
	{
		String value = null;

		if ( id<0 )
		{
			Logger.debug("Instance#setParam(): serverid is negative. - " + toString());
			return null;
		}

		try {
			ResultSet rs = null;

			rs = gsc.executeQueryGSC("SELECT " + param + " FROM forest_server " +
									  " WHERE serverid=" + id);

			if ( rs.next() )
				value = rs.getString(1);

			if ( rs!=null )
				rs.close();
		}
		catch (Exception e)
		{
			Logger.error(e.getMessage());
			Logger.trace(e);

			return null;
		}

		return value;
	}


	/**
	 * 指定したデータベースを削除する
	 *
	 * @param dbName 削除するデータベース名
	 *
	 * @return boolean 削除できたらtrue、できなければfalse
	 */
	public boolean dropDatabase(String dbName)
	{
		/*
		 * 実DBをDROP
		 */
		try {
			Connection con = getDatabaseConnection(GSCdata.TemplateDatabase,
												   gsc.getUser(),
												   gsc.getPassword());

			Statement stmt = con.createStatement();

			stmt.executeUpdate("DROP DATABASE " + dbName);

			con.close();
		}
		catch (Exception e)
		{
			Logger.error(e.getMessage());
			return false;
		}

		return true;
	}

	/**
	 * FIXME:
	 */
	public String[] getGscNames()
	{
		return null;
	}
}
