/*
 * @(#)DocManager.java
 *
 * Copyright 2001 by Intelligent Technology Inc. All rights reserved.
 *
 * Document Manager Super class
 */
package jp.co.iti.fagot.doc;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.IOException;

import java.util.Hashtable;
import java.util.Properties;
import java.util.Locale;

import org.w3c.dom.Element;

import org.xml.sax.InputSource;

import org.apache.xerces.parsers.DOMParser;

import jp.co.iti.fagot.ZApp;

import jp.co.iti.fagot.ui.UIFactory;

import jp.co.iti.fagot.doc.DefaultAction;

import jp.co.iti.fagot.util.ZProperties;
import jp.co.iti.fagot.util.ZResource;
import jp.co.iti.fagot.util.ZScript;
import jp.co.iti.fagot.util.ZString;
import jp.co.iti.fagot.util.ZDebug;
/**
 * Docパッケージのマネージャ
 *
 * カスタマイズ項目全般を管理する
 *
 * @author  福島康治
 * @version 1.01 2002/10/23
 */
public class DocManager {

	//## インスタンスの作成 ###########################################
	/**
	 * アプリケーションインスタンスの作成
	 */
	static public IApplication createApplication() {
	return (IApplication)createInstance(APPLICATION,"main");
	}

	/**
	 * ドキュメントインスタンスの作成
	 * @param szKey ドキュメント識別子
	 */
	static public IDocument createDocument(String szKey) {
	return (IDocument)createInstance(DOCUMENT,szKey);
	}

	/**
	 * エレメントインスタンスの作成
	 * XElementNSの基本クラスにはデフォルトドキュメントがないため、constructElement
	 * でエレメントを初期化する
	 *
	 * @param szKey ドキュメント識別子
	 */
	static public IElement createElement(DocDocument doc,String ns, String name) {
	IElement elm = (IElement)createInstance(ELEMENT,name);
	if ( elm != null ) {
		elm.constructElement(doc,ns,name);
	}
	return elm;
	}

	/**
	 * アクションインスタンスの作成
	 */
	static public DefaultAction[] createAction() {
	ZProperties prop = loadProperty(APPLICATION);
	String[] szActions = ZString.tokenize(prop.getProperty("actions"));
	DefaultAction[] act = new DefaultAction[szActions.length];
	for ( int nIndex=0 ; nIndex<szActions.length ; nIndex++ ) {
		act[nIndex] = (DefaultAction)createInstance(APPLICATION,szActions[nIndex]);
		act[nIndex].setApp(ZApp.getTheApp());
		act[nIndex].setActionName(szActions[nIndex]);
	}
	return act;
	}

	/**
	 * インスタンスの作成
	 * @param szConfig コンフィグレーション名
	 * @param szKey    クラス名
	 */
	static protected Object createInstance(String szConfig,String szKey) {
	// プロパティファイルからクラスの抽出
	String szClass = getClassName(szConfig,szKey);
	if ( szClass != null ) {
		try {
//			ZDebug.trace(szClass);
			Object obj = Class.forName(szClass);
			if ( obj instanceof Class ) {
				return ((Class)obj).newInstance();
			}
			return null;
		} catch ( Throwable t ) {
			ZDebug.trace("Class not found:"+szClass);
			t.printStackTrace();
		}
	}
	return null;
	}

	/**
	 * クラス名の取得
	 * ドキュメントクラスのロード時にクラス名が必要なので、public
	 * @param szConfig コンフィグレーション名
	 * @param szKey ドキュメント識別子
	 */
	static public String getClassName(String szConfig,String szKey) {
	// プロパティファイルからクラスの抽出
	ZProperties prop = loadProperty( szConfig ) ;
	String szPackage = prop.getProperty( szKey+".package" );
	String szClass   = prop.getProperty( szKey+".class" );
	if ( szPackage != null && szClass != null ) {
		return szPackage+"."+szClass;
	}
	return null;
	}

	//## ポップアップメニュー ##########################################
	/**
	 * コンテキストメニューを作成するためのUIFactoryを生成します。
	 * @param szConfig コンフィグレーション名
	 */
	static public UIFactory createUIFactory(String szConfig) {
	UIFactory ui = 	(UIFactory)mFactories.get(szConfig);
	if ( ui == null ) {
		ui = new UIFactory(
						ZApp.getTheApp(),
						new ZResource(loadProperty(szConfig)));
		mFactories.put( szConfig, ui );
	}
	return ui;
	}

	//## プロパティレイアウト ##########################################
	/**
	 * プロパティレイアウトxmlを読み込みます
	 * @param szTag エレメントタグ名
	 */
	static public Element createElementLayout(String szTag) {
	Element elm = (Element)mLayouts.get(szTag);
	if ( elm == null ) {
		ZProperties prop = loadProperty( ELEMENT ) ;
		String szLayout = prop.getProperty( szTag+".layout" );
		try {
			DOMParser parser = new DOMParser();
			parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
//			ZDebug.trace("Open:"+getConfigFile(ELEMENT,szLayout));
//			InputSource in = new InputSource(getConfigFile(ELEMENT,szLayout));
			InputSource in = createInputSource(getConfigFile(ELEMENT,szLayout));
			parser.parse(in);
			elm = parser.getDocument().getDocumentElement();
		} catch ( Throwable t ) {
			ZDebug.err("FileOpen failed:"+getConfigFile(ELEMENT,szLayout));
			ZDebug.err("    "+t.toString());
			t.printStackTrace();
		}
		mLayouts.put( szTag, elm );
	}
	return elm;
	}

	/**
	 * XML読み込み用InputSourceを作成します
	 * @param szFile ファイル名
	 */
	static public InputSource createInputSource(String szFile) {
	InputSource in = null;
	try {
		FileInputStream     fis = new FileInputStream(szFile);
		BufferedInputStream bis = new BufferedInputStream(fis);
		in = new InputSource(bis);
	} catch ( Throwable t ) {
		ZDebug.err("FileOpen failed:"+szFile);
		ZDebug.err("    "+t.toString());
//		t.printStackTrace();
	}
	return in;
	}

	//## マップ用コマンドファイルハッシュ ##############################
	/**
	 * マップ用コマンドxmlを読み込みます
	 * @param szFile ファイルフルパス名
	 */
	static public Element createMapElement(String szFile) {
	Element elm = (Element)mMaps.get(szFile);
	if ( elm == null ) {
		try {
			DOMParser parser = new DOMParser();
			parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
//			ZDebug.trace("Open:"+szFile);
//			InputSource in = new InputSource(szFile);
			InputSource in = createInputSource(szFile);
			parser.parse(in);
			elm = parser.getDocument().getDocumentElement();
		} catch ( Throwable t ) {
			ZDebug.err("FileOpen failed:"+szFile);
			ZDebug.err("    "+t.toString());
			t.printStackTrace();
		}
		mMaps.put( szFile, elm );
	}
	return elm;
	}

	/**
	 * マップ用コマンドファイルを読み込みます
	 * @param szFile ファイルフルパス名
	 */
	static public Reader createTextFile(String szFile) {
	String text = (String)mTexts.get(szFile);
	if ( text == null ) {
		StringBuffer sb = new StringBuffer();
		try {
			FileInputStream fis = new FileInputStream(szFile);
			byte buf[] = new byte[4096];
			int len;
			while ((len = fis.read(buf)) != -1) {
				sb.append( new String( buf,0,len ) );
			}
		} catch ( Throwable t ) {
			ZDebug.err("FileOpen failed:"+szFile);
			ZDebug.err("    "+t.toString());
			t.printStackTrace();
		}
		mTexts.put( szFile, sb.toString() );
	}
	return new StringReader(text);
	}

	//## スクリプト ###################################################
	/**
	 * ドキュメントバーメソッドの呼び出し
	 * @param szKey  アクションキー
	 * @param szFunc メソッド名
	 * @param params パラメータ
	 */
	static public Object invokeDocBarFunction(String szKey, String szFunc, Object[] params ) {
	ZProperties prop = loadProperty( DOC_BAR );
	String szScript = prop.getProperty( szKey + DocBar.SFX_SCRIPT );
	String szFile   = getConfigFile( DOC_BAR, szScript );
	return invokeFunction(szFile,szFunc,params);
	}

	/**
	 * エレメントメソッドの呼び出し(Element名指定)
	 * @param szScript スクリプト
	 * @param szFunc   メソッド名
	 * @param params   パラメータ
	 */
	static private Object invokeFunction( String szScript, String szFunc, Object[] params ) {
	ZScript mgr = (ZScript)mScripts.get(szScript);
	if ( mgr == null ) {
		mgr = new ZScript();
		mgr.loadScript(szScript);
		mScripts.put( szScript, mgr );
	}
	return mgr.invoke(szFunc,params);
	}

	//## プロパティ ###################################################
	/**
	 * ディレクトリ名の取得
	 * @param szPath パス名
	 */
	static public String getConfigDirectory( String szPath ) {
	ZResource res   = ZApp.getTheApp().getResource();
	String install = res.getString("app.install.path");
	String config  = res.getString("app.config.dir");
//	ZDebug.trace("ins:"+install+" conf:"+config+" path:"+szPath);
	return install+File.separator+config+File.separator+szPath;
	}

	/**
	 * コンフィグレーションファイル名の取得
	 * @param szPath パス名
	 * @param szFile ファイル名
	 */
	static public String getConfigFile( String szPath, String szFile ) {
	return getConfigDirectory( szPath ) + File.separator + szFile ;
	}

	/**
	 * プロパティの読み込み
	 * @param szPath パス名
	 */
	static public ZProperties loadProperty(String szPath ) {
	return loadProperty( szPath, szPath );
	}

	/**
	 * プロパティの読み込み
	 * デフォルトのプロパティを読み込んだ後、デフォルトロケールのプロパティを上書きする
	 * デフォルトロケールのファイルが存在しなくてもよい
	 * @param szPath パス名
	 * @param szFile ファイル名
	 */
	static public ZProperties loadProperty(String szPath, String szFile ) {
	String szConf = getConfigFile( szPath, szFile );
	String szProp = szConf + ".properties" ;
	ZProperties prop = (ZProperties)mProperties.get(szProp);
	if ( prop == null ) {
		prop = new ZProperties();
		try {
			FileInputStream inStream = new FileInputStream(szProp);
			prop.load(inStream);
			inStream.close();
			
			// デフォルトロケールのプロパティ読み込み処理
			String localeSuffix = Locale.getDefault().getLanguage();
			if ( localeSuffix.length() > 0 ) {
				try {
					ZProperties propLocal = new ZProperties();
					String szLocal = szConf + "_" + localeSuffix + ".properties" ;
					inStream = new FileInputStream(szLocal);
					propLocal.load(inStream);
					inStream.close();
					
					prop.putAll( propLocal );
				} catch (IOException io) {
					// デフォルトロケールのプロパティ読み込みに失敗しても何もしない
				}
			}
			
			mProperties.put(szProp,prop);
		} catch (IOException io) {
			ZDebug.err("Custom Properties load failed:("+szProp+")"+io);
		}
	}
	return prop;
	}

	//## アトリビュート ###############################################
	/**
	 * プロパティハッシュテーブル
	 */
	protected static Hashtable mProperties = new Hashtable();

	/**
	 * スクリプトハッシュテーブル
	 */
	protected static Hashtable mScripts = new Hashtable();

	/**
	 * UIファクトリハッシュテーブル
	 */
	protected static Hashtable mFactories = new Hashtable();

	/**
	 * プロパティレイアウトハッシュテーブル
	 */
	protected static Hashtable mLayouts = new Hashtable();

	/**
	 * マップコマンドハッシュテーブル
	 */
	protected static Hashtable mMaps = new Hashtable();

	/**
	 * マップテキストハッシュテーブル
	 */
	protected static Hashtable mTexts = new Hashtable();

	//## リテラル:config ##############################################
	/**
	 * panelディレクトリ
	 */
	public static final String APPLICATION	= "application";

	/**
	 * panelディレクトリ
	 */
	public static final String PANEL	= "panel";

	/**
	 * documentディレクトリ
	 */
	public static final String DOCUMENT	= "document";

	/**
	 * elementディレクトリ
	 */
	public static final String ELEMENT	= "element";

	/**
	 * docbarディレクトリ
	*/
	public static final String DOC_BAR	= "docbar";

	//## リテラル:method ##############################################
	/**
	 * アプリケーションメソッド
	 */
	public static final String UPDATE_DOCUMENT	= "updateDocument";

	/**
	 * パネルメソッド
	 */
	public static final String CREATE_PANEL		= "createPanel";

	/**
	 * Documentメソッド
	 */
	public static final String GET_BACKGROUND	= "getBackground";

	/**
	 * Elementメソッド：生成
	 */
	public static final String CREATE_ELEMENT	= "create";

	/**
	 * Elementメソッド：ヒットモード
	 */
	public static final String GET_HIT_MODE		= "getHitMode";

}
