/**
* Kandata
* Copyright (C) 2003. Yoshinori Watanabe
* 
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package kandata;

import java.io.*;
//import java.net.*;
//import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Properties;
import kandata.client.*;
import kandata.server.*;

/**
 * デバッグ用のコネクタ.
 * クライアントとサーバを１つのプロセスで実行する。
 */
public class DebugConnector implements KandataConnector, KandataServerContext {

	static final boolean TRACE_ENABLE = true;
	static final void TRACE(String message) { if (TRACE_ENABLE) System.out.println(message); }
	static final void TRACE(Throwable ex) { if (TRACE_ENABLE) ex.printStackTrace(System.out); }

	static KandataServer server = null;

	/** 設定ファイル. */
	private static final String CONFIG_FILENAME = "kandata.properties";

	protected KandataClientContext clientContext = new KandataClientContext(System.currentTimeMillis());

	/** サーバアプリのクラス名. */
	protected String serverClassName;// = null;//"roomreservation.server.RoomReservationServer";

	/** サーバアプリのリソースパス. */
	protected String serverResourcePath;// = "server_resources";

	/** クライアントアプリのエントリクラス名. */
	protected String clientEntryClassName;// = "roomreservation.client.RoomReservationClient";

	/** クライアントアプリのクラスパス. */
	protected String clientClassPath;// = "classes";

	/** クライアントアプリのリソースパス. */
	protected String clientResourcePath;// = "client_resources";

	/** アクションマップ */
	private HashMap actionMap = new HashMap();	// <String, KandataAction>


	/** 接続 */
	public void connect() throws KandataClientException {
		TRACE("DebugConnector.connect()");

		// 設定ファイルを読み込む
		loadConfiguration(CONFIG_FILENAME);

		server.init(this);
		server.connected(clientContext);
	}

	/** 切断 */
	public void disconnect() throws KandataClientException {
		TRACE("DebugConnector.disconnect()");
		server.disconnected(clientContext);
		server.exit();
	}

	/**
	 * サーバ側のリソースを取得します.
	 * @param	path	先頭に<code>File.separator</code>を付けます.
	 */
	public byte[] getResource(String path) throws KandataClientException {
		TRACE("DebugConnector.getResource("+path+") "+clientResourcePath+path);
		InputStream in = null;
		try {
			File file = new File(clientResourcePath + path);
			in = new FileInputStream(file);
			byte[] buf = new byte[ (int) file.length() ];
			in.read(buf);
			return buf;
		}
		catch (IOException e) {
			throw new KandataClientException(
						"ファイルの読み込みに失敗しました :"+clientResourcePath+path, e);
		}
		finally {
			if (in != null) try { in.close(); } catch (IOException e) {}
		}
	}

	/** サーバ側のリソースのパスを返す. */
	public String getResourcePath() {
		return serverResourcePath;
	}

	/**
	 * アクションを登録する. 
	 */
	public void putAction(String command, KandataAction action) {
		actionMap.put(command, action);
	}

	/** サーバ側のメソッドを呼び出す. */
	public Object invoke(String command, Object[] args) throws KandataServerException {
		TRACE("DebugConnector.invoke("+command+")");
		if (args != null) {
			for (int i=0; i<args.length; i++) {
				TRACE("  args["+i+"]="+args[i]);
			}
		}
		Object ret;
		if (command.charAt(0) == '@') {
			ret = invokeSystemCommand(clientContext, command, args);
		}
		else {
			ret = invokeCommand(clientContext, command, args);
		}
		TRACE("   ret="+ret);
		return ret;
	}

	/**
	 * システム要求に答える.
	 * システム要求は'@'から始まる。
	 * <ul>
	 *   <code>@getEntryClassName</code>	エントリのクラス名を返す。
	 *   <code>@getResource</code>			リソースを返す。
	 * </ul>
	 */
	protected Object invokeSystemCommand(KandataClientContext context, String command, Object[] args) 
						throws KandataServerException {

		if (command.equals("@getEntryClassName")) {
			return clientEntryClassName;
		}
		else if (command.equals("@getResource")) {
			String path = clientResourcePath + (String) args[0];
			TRACE("path="+path);
			FileInputStream in = null;
			try {
				File file = new File(path);
				byte[] buf = new byte[(int) file.length()];
				in = new FileInputStream(file);
				in.read(buf, 0, (int) file.length());
				return buf;
			} catch (IOException e) {
				TRACE(e);
				return null;
			}
			finally {
				if (in != null) try { in.close(); } catch (IOException e) {}
			}
		}

		TRACE("未定義のコマンドです command="+command);
		throw new KandataServerException("no such command:"+command, null);
	}

	protected Object invokeCommand(KandataClientContext context, String command, Object[] args)
						throws KandataServerException {

		KandataEvent event = new KandataEvent(context, command, args);
		KandataAction action = (KandataAction) actionMap.get(command);
		if (action == null) {
			throw new KandataServerException("no such command:"+command, null);
		}
		Object result = action.execute(event);
		return result;
	}

	/** エントリのクラス名を取得する. */
	public String getEntryClassName() throws KandataClientException, KandataServerException {
		return (String) invoke("@getEntryClassName", null);
	}
	/**
	 * 設定を読み込む.
	 * XMLではなく、Propertiesにしている。JDK1.3ではデフォルトで入っていないので.
	 */
	protected void loadConfiguration(String fname) {
		FileInputStream in = null;
		try {
			in = new FileInputStream(fname);
			Properties prop = new Properties();
			prop.load(in);
			String appname       = prop.getProperty("application.name");
			serverClassName      = prop.getProperty("application.serverclassname");
			serverResourcePath   = prop.getProperty("application.serverresourcepath");
			clientEntryClassName = prop.getProperty("application.cliententryclassname");
			clientClassPath      = prop.getProperty("application.clientclasspath");
			clientResourcePath   = prop.getProperty("application.clientresourcepath");
			in.close();
			TRACE("application.name="+appname);
			TRACE("application.serverclassname="+serverClassName);
			TRACE("application.serverresourcepath="+serverResourcePath);
			TRACE("application.cliententryclassname="+clientEntryClassName);
			TRACE("application.clientclasspath="+clientClassPath);
			TRACE("application.clientresourcepath="+clientResourcePath);
		}
		catch (IOException e) {
			TRACE(e);
		}
		finally {
			if (in != null) try { in.close(); } catch (IOException e) {}
		}
	}

	public static void main(String[] args) throws Exception {
		if (args.length < 1) {
			System.err.println("usage: java kandata.DebugConnector <server-classname> [<client-type> [<version>]]");
			System.exit(-1);
		}
		
		// アプリケーションサーバを起動する.
		String classname  = args[0];
		String clientType = args.length > 1 ? args[1] : null;	// TODO:どうやって使う？
		String version    = args.length > 2 ? args[2] : null;	// TODO:どうやって使う？
		Class serverClass = Class.forName(classname);
		server = (KandataServer) serverClass.newInstance();
		// デバッグ用クライアントを起動する.
		DebugConnector connector = new DebugConnector();
		connector.connect();
		String entry_classname = connector.getEntryClassName();

		// セキュリティマネージャを設定する。TODO:どうやってアプリごとにセキュリティを設定する？
//		System.out.println("SecurityManager="+System.getSecurityManager());
//		System.setSecurityManager(new java.rmi.RMISecurityManager());
//		System.out.println("SecurityManager="+System.getSecurityManager());

		// クライアントアプリを読み込み、実行する。
		Class entry_class = Class.forName(entry_classname);
		KandataClient client = (KandataClient) entry_class.newInstance();
		client.init(connector);
	}
}
