/*
 * @(#)UIFactory.java
 *
 * Copyright 2001 by Intelligent Technology Inc. All rights reserved.
 */
package jp.co.iti.fagot.ui;

import java.awt.Insets;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

import java.net.URL;

import java.util.Enumeration; 
import java.util.Hashtable; 
import java.util.Vector; 

import java.lang.Character;

import javax.swing.JComponent;
import javax.swing.Action;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.AbstractButton;
import javax.swing.ImageIcon;
import javax.swing.Box;
import javax.swing.BoxLayout;

import jp.co.iti.fagot.IZApp;
import jp.co.iti.fagot.IZFileable;

import jp.co.iti.fagot.action.ZAction;

import jp.co.iti.fagot.util.ZResource;
import jp.co.iti.fagot.util.ZString;
import jp.co.iti.fagot.util.ZFile;
import jp.co.iti.fagot.util.ZDebug;
/**
 * メニュー、ツールバー、ステータスバーなどUI共通インターフェイスを定義します
 * @author  福島康治
 * @version 1.01 2001/01/07
 */
public class UIFactory {
	/**
	 * コンストラクタ
	 * @param app アプリケーション(リソースとアクションを取得)
	 */
	public UIFactory(IZApp app) {
	mApp = app;
	}

	/**
	 * コンストラクタ
	 * @param app アプリケーション(アクションを取得)
	 * @param res リソース
	 */
	public UIFactory(IZApp app,ZResource res) {
	mApp = app;
	mRes = res;
	}

	//## メニュー #####################################################
	/**
	 * メニューバーの作成
	 * @param szRes リソース文字列
	 */
	public JMenuBar createMenuBar( String szRes ) {
	// リソースの存在チェック
	ZResource res = getResource() ;
	if ( res == null ) {
		return ( null ) ;
	}

	// メニューバーの作成
	mMenuBar = createMenuBar();

	// メニューの作成
	String[] szMenuKeys = ZString.tokenize(res.getString( szRes ) );
	if ( szMenuKeys == null ) {
		return(null);
	}
	for (int nIndex = 0; nIndex < szMenuKeys.length; nIndex++) {
	    JMenu menu = createMenu(szMenuKeys[nIndex]);
	    if (menu != null) {
			mMenuBar.add(menu);
	    }
	}
	return ( mMenuBar );
	}

	/**
	 * メニューを作成します。
	 * @param szKey メニューキー
	 */
	protected JMenu createMenu(String szKey) {
	JMenu menu = null ;
	String szLabel = getResource().getString(PFX_MENU+szKey+SFX_LABEL);
	if ( szLabel == null ) {
		System.out.println("Resouce not found:"+
							PFX_MENU+szKey+SFX_LABEL);
		return null;
	}
	int nIndex = szLabel.indexOf(MNEMONIC);
	// ニューモニックの設定
	if ( nIndex != -1 ) {
		String szLabel2 = szLabel.substring(0,nIndex)+szLabel.substring(nIndex+1);
		menu = new JMenu(szLabel2);
		menu.setMnemonic( Character.toLowerCase(szLabel.charAt(nIndex+1)) );
	} else {
		menu = new JMenu(szLabel);
	}

	// プルダウンメニューの作成
	String[] szItemKeys = ZString.tokenize(getResource().getString(PFX_MENU+szKey));
	for ( nIndex = 0; nIndex < szItemKeys.length; nIndex++) {
	    if (szItemKeys[nIndex].equals(SEPARATOR)) {
			menu.addSeparator();
	    } else {
			JMenuItem mi = createMenuItem(szItemKeys[nIndex],menu.getPopupMenu());
			menu.add(mi);
	    }
	}

	// メニュー一覧に登録
	mMenus.put(szKey, menu);
	return menu;
	}

	/**
	 * コンテキストメニューを作成します。
	 * @param invoker インボーカー
	 * @param szKey メニューキー
	 */
	public JPopupMenu createContextMenu(JComponent invoker,String szKey) {
//	ZDebug.trace("key:"+szKey+"="+getResource().getString(PFX_MENU+szKey));
	// ポップアップの作成
	JPopupMenu menu = new JPopupMenu();
	menu.setInvoker( invoker );

	// プルダウンメニューの作成
//	ZDebug.trace("menu key:"+PFX_MENU+szKey);
//	ZDebug.trace("menu    :"+getResource().getString(PFX_MENU+szKey));
	String[] szItemKeys = ZString.tokenize(getResource().getString(PFX_MENU+szKey));
	for (int nIndex = 0; nIndex < szItemKeys.length; nIndex++) {
	    if (szItemKeys[nIndex].equals(SEPARATOR)) {
			menu.addSeparator();
	    } else {
			JMenuItem mi = createMenuItem(szItemKeys[nIndex],menu);
			menu.add(mi);
	    }
	}
	return menu;
	}

	/**
	 * コンテキストメニューを作成します。
	 * 複数キーの共通項目を表示します。
	 * @param invoker インボーカー
	 * @param szKey メニューキー群
	 */
	public JPopupMenu createContextMenu(JComponent invoker,Vector szKeys) {
	// ポップアップの作成
	JPopupMenu menu = new JPopupMenu();
	menu.setInvoker( invoker );

	// プルダウンメニューの作成
	Vector keys = new Vector();
	Enumeration elm = szKeys.elements();
	while( elm.hasMoreElements() ) {
		String szKey = (String)elm.nextElement();
		keys.add(getResource().getString(PFX_MENU+szKey));
	}

	String szCommon = ZString.getCommonStrings(keys);

	// 最後にセパレータがあった場合は、削除
	while ( szCommon.endsWith(SEPARATOR) ) {
		szCommon = szCommon.substring(0,szCommon.length()-SEPARATOR.length());
		szCommon = ZString.trimRight(szCommon);
	}

	String[] szItemKeys = ZString.tokenize(szCommon);

	boolean bSep = true;
	for (int nIndex = 0; nIndex < szItemKeys.length; nIndex++) {
	    if (szItemKeys[nIndex].equals(SEPARATOR)) {
			// セパレータが続いた場合はセパレータを追加しない
			if ( !bSep ) {
				menu.addSeparator();
			}
			bSep = true;
	    } else {
			JMenuItem mi = createMenuItem(szItemKeys[nIndex],menu);
			menu.add(mi);
			bSep = false;
	    }
	}
	return menu;
	}

	/**
	 * メニュー項目を作成します。
	 * @param szCmd コマンド名
	 * @param menu  ポップアップメニュー
	 */
	protected JMenuItem createMenuItem(String szCmd,JPopupMenu popup) {
	// サブメニュー
	if ( szCmd.startsWith(SUBMENU) ) {
		return ( createMenu( szCmd.substring(1) ) ) ;
	}

	boolean bToggled = false ;
	if ( szCmd.startsWith(TOGGLED) ) {
		bToggled = true ;
		szCmd = szCmd.substring(1);
	}

	// メニュー項目の作成
	JMenuItem mi = null;
	String szLabel = getResource().getString(PFX_MENU+szCmd+SFX_LABEL);
	// OSがMacの場合はここでラベルを変更する
	// 実際に設定するL&Fは ZUIAction の中でos.archにより決定される
	if ( System.getProperty("os.arch").equals("ppc") && szCmd.equals("view_windows") ) {
//		szLabel = "Mac($M)";
		szLabel = getResource().getString(PFX_MENU+"view_mac"+SFX_LABEL);
	}
	String szLabel2 = szLabel;
	char   mnemonic = '\0';
	if ( szLabel == null ) {
		ZDebug.err("not exist resource key:"+PFX_MENU+szCmd+SFX_LABEL);
		szLabel = new String( szCmd );
		szLabel2 = szLabel;
	} else {

		// ニューモニックの有無の判定
		int nIndex = szLabel.indexOf(MNEMONIC);
		if ( nIndex != -1 ) {
			szLabel2 = szLabel.substring(0,nIndex)+szLabel.substring(nIndex+1);
			mnemonic = Character.toLowerCase(szLabel.charAt(nIndex+1));
		}
	}

//	ZDebug.err("menu:"+szLabel2);
	// メニューアイテムの作成
	if ( bToggled ) {
		mi = new JCheckBoxMenuItem(szLabel2);
	} else {
		mi = new JMenuItem(szLabel2);
	}

	// ニューモニックの設定
	if ( mnemonic != '\0' ) {
		mi.setMnemonic( mnemonic );
	}

	// アイコンの設定
    URL url = getResource().getURL(PFX_MENU+szCmd+SFX_IMAGE);
	if (url == null) {
		url = getResource().getURL(PFX_MENU+"default"+SFX_IMAGE);
	}
	if (url != null) {
	    mi.setHorizontalTextPosition(JButton.RIGHT);
	    mi.setIcon(new ImageIcon(url));
		mi.setMargin(new Insets(0,0,1,1));
	}

	// アクションの設定
	String szAction = getResource().getString(PFX_MENU+szCmd+SFX_ACTION);
	if (szAction == null) {
	    szAction = szCmd;
	}
	mi.setActionCommand(szAction);
	Action act = getApp().getAction(szAction);
	if (act != null) {
	    mi.addActionListener(act);
	    act.addPropertyChangeListener(new UIChangedListener(mi));
	    mi.setEnabled(act.isEnabled());
	    if ( act instanceof ZAction ) {
			popup.addPopupMenuListener((ZAction)act);
		}
	} else {
	    mi.setEnabled(false);
	}

	// メニュー一覧に登録
	mMenuItems.put(szCmd, mi);
	return mi;
	}

	/**
	 * メニューバーの作成
	 */
	public JMenuBar createMenuBar() {
	return ( new JMenuBar() );
	}

	/**
	 * メニューバーの取得
	 */
	public JMenuBar getMenuBar() {
	return ( mMenuBar );
	}

	/**
	 * メニュー項目を取得します。
	 * @param szCmd  アクション名
	 * @returns item メニュー項目、または、null
	 */
	public JMenuItem getMenuItem(String szCmd) {
	return (JMenuItem) mMenuItems.get(szCmd);
	}

	/**
	 * メニューを取得します。
	 * @param szCmd  アクション名
	 * @returns item メニュー項目、または、null
	 */
	public JMenu getMenu(String szCmd) {
	return (JMenu) mMenus.get(szCmd);
	}

	/**
	 * 最近使ったファイルの更新
	 * @param cmp          ファイルコンポーネント
	 * @param szRecentMenu メニュー項目名
	 * @param szRecentFile 直前に開いたファイル
	 */
	public void updateRecent(IZFileable cmp,
		String szRecentMenu,String szRecentFile) {
	int nRecent = getResource().getInt(szRecentMenu,5);
	int nIndex = 1 ;
	String szRecentn ;
	if ( szRecentFile != null ) {
		// 直前のプロジェクトが存在するかチェック
		// 最後にあっても全部取替なので最後はチェックしない
		for ( ; nIndex < nRecent ; nIndex++ ) {
			szRecentn = getResource().getString(szRecentMenu+nIndex);
			if ( szRecentn == null || szRecentn.equals(szRecentFile) ) {
				break;
			}
		}
		// １つずつずらす
		for ( nIndex-- ; nIndex>0 ; nIndex-- ) {
			szRecentn = getResource().getString(szRecentMenu+nIndex);
			getResource().putString(szRecentMenu+(nIndex+1),szRecentn);
		}
		// 今回開いたファイルを先頭に入れる
		getResource().putString(szRecentMenu+1,szRecentFile);
	}
	// メニューの更新
	String szCurDir = System.getProperty("user.dir");
//	System.out.println("user.dir=" + szCurDir );
	JMenu menu = getMenu(szRecentMenu);
	if ( menu != null ) {
		menu.removeAll();
		for ( nIndex=1 ; nIndex <= nRecent ; nIndex++ ) {
			szRecentn = getResource().getString(szRecentMenu+nIndex);
			// Recent プロパティが設定されていた場合
			if ( szRecentn != null ) {
				// 相対パスではなく、フルパスで表示する。
				String szRecentName = szRecentn ;
/*
				// 表示名の決定(相対パスを表示する)
				if ( szCurDir != null ) {
					szRecentName = ZFile.getSPath(szCurDir,szRecentn);
				}
*/
				JMenuItem mi = new JMenuItem(szRecentName);
				Action action = cmp.createRecentAction(szRecentMenu,szRecentn);
				if ( action != null ) {
				    mi.addActionListener(action);
				    mi.setEnabled(action.isEnabled());
					menu.add(mi);
				} else {
					ZDebug.err("Can't create action") ;
				}
			} else {
//				ZDebug.err("not exist recent:"+ nIndex ) ;
			}
		}
	} else {
		ZDebug.err("not exist menu" ) ;
	}
	}

	//## ツールバー ###################################################
	/**
	 * ツールバーを作成します。
	 * ()でくくられていた場合は、横グループ化します。
	 * @param szRes リソース文字列
	 */
	public JToolBar createToolBar( String szRes ) {
	// リソースの存在チェック
	ZResource res = getResource() ;
	if ( res == null ) {
		return ( null ) ;
	}

	// ツールバーの作成
	JToolBar toolbar = new JToolBar();
	JPanel   tmpPanel = null;

	// ツールの作成
	String[] szToolKeys = ZString.tokenize(res.getString( szRes ));
	if ( szToolKeys == null ) {
		return(null);
	}
	for (int nIndex = 0; nIndex < szToolKeys.length; nIndex++) {
	    if (szToolKeys[nIndex].equals(SEPARATOR)) {
			toolbar.add(Box.createHorizontalStrut(5));
	    } else if (szToolKeys[nIndex].equals("(")) {
			tmpPanel = new JPanel();
			tmpPanel.setAlignmentX(0.0f);
			tmpPanel.setLayout( new BoxLayout( tmpPanel, BoxLayout.X_AXIS ) );
	    } else if (szToolKeys[nIndex].equals(")")) {
			toolbar.add(tmpPanel);
			tmpPanel = null;
	    } else {
			if ( tmpPanel == null ) {
				toolbar.add(createTool(szToolKeys[nIndex]));
			} else {
				tmpPanel.add(createTool(szToolKeys[nIndex]));
			}
	    }
	}
	toolbar.add(Box.createHorizontalGlue());
	// 画面左側に貼り付けたときに、DocBarなどがおかしくなるので、フローティング中止
	toolbar.setFloatable( false );
	return toolbar;
	}

	/**
	 * ツール項目を作成します。
	 * @param szCmd コマンド名
	 */
	public JComponent createTool(String szCmd) {
	String szToolClass = getResource().getString( PFX_TOOL+szCmd+SFX_CLASS );

	JComponent cmp = null;
	if ( szToolClass == null ) {
		cmp = createToolBarButton(szCmd);
	} else {
		// クラス名の取得
		Class cClass;
		try {
			cClass = Class.forName( szToolClass );
		} catch ( ClassNotFoundException cn ) {
			System.err.println ( "ClassNotFound:" + szToolClass ) ;
			return( null );
		}

		// インスタンス化
		Object obj ;
		try {
			obj = cClass.newInstance();
		} catch ( InstantiationException ie ) {
			System.err.println ( "Class newInstance Failed:" + szToolClass ) ;
			return( null );
		} catch ( IllegalAccessException ia ) {
			System.err.println ( "Class newInstance Failed:" + szToolClass ) ;
			return( null );
		}

		if ( ! ( obj instanceof JComponent ) ) {
			System.err.println ( "Class is not instanceof Compenent:" + szToolClass ) ;
			return( null );
		}
		cmp = (JComponent) obj;

		// ボタンの登録
		mToolItems.put(szCmd, cmp);
	}
	return ( cmp ) ;
	}

	/**
	 * ツールボタンを作成します。
	 * @param szCmd コマンド名
	 */
	protected AbstractButton createToolBarButton(String szCmd) {
	boolean bToggled = false ;
	if ( szCmd.startsWith(TOGGLED) ) {
		bToggled = true ;
		szCmd = szCmd.substring(1);
	}
	URL url = getResource().getURL(PFX_MENU+szCmd+SFX_IMAGE);
	if ( url == null ) {
		System.out.println("Resouce not found:"+
							PFX_MENU+szCmd+SFX_IMAGE);
		return null;
	}
    AbstractButton btn ;
	ImageIcon img = new ImageIcon(url);
	if ( img == null ) {
		System.out.println("Resouce not found:"+
							PFX_MENU+szCmd+SFX_IMAGE+"="+
							url.toString());
		return null;
	}
/*
	if ( bToggled ) {
		btn = new JToggleButton(img) {
			public float getAlignmentY() { return 0.5f; }
		};
	} else {
		btn = new JButton(img) {
			public float getAlignmentY() { return 0.5f; }
		};
	}
*/
	if ( bToggled ) {
		btn = new JToggleButton(img);
	} else {
		btn = new JButton(img);
	}
	btn.setAlignmentY(0.5f);
    btn.setRequestFocusEnabled(false);
    btn.setMargin(new Insets(1,1,1,1));

	// アクションの設定
	String szAction = getResource().getString(PFX_MENU+szCmd+SFX_ACTION);
	if (szAction == null) {
	    szAction = szCmd;
	}
	Action act = getApp().getAction(szAction);
	if (act != null) {
	    btn.setActionCommand(szAction);
	    act.addPropertyChangeListener(new UIChangedListener(btn));
	    btn.addActionListener(act);
	} else {
	    btn.setEnabled(false);
	}

	// ツールチップの設定
	String szTip = getResource().getString(PFX_TOOL+szCmd+SFX_TOOLTIP);
	if (szTip == null) {
		// ツールチップにない場合メニューラベルから取得
		szTip = getResource().getString(PFX_MENU+szCmd+SFX_LABEL);
		if ( szTip != null ) {
			int nIndex = szTip.indexOf('$');
			if ( nIndex != -1 ) {
				szTip = szTip.substring(0,nIndex) + szTip.substring(nIndex+1);
			}
			nIndex = szTip.indexOf('(');
			if ( nIndex != -1 ) {
				szTip = szTip.substring(0,nIndex);
			}
		}
	}
	if (szTip != null) {
	    btn.setToolTipText(szTip);
	}

	// ボタンの登録
	mToolItems.put(szCmd, btn);

    return btn;
	}

	/**
	 * ツール項目を取得します。
	 * @param szCmd  項目名
	 * @returns item メニュー項目、または、null
	 */
	public JComponent getToolItem(String szCmd) {
	return (JComponent) mToolItems.get(szCmd);
	}

	//## ステータスバー ###############################################
	/**
	 * ステータスバーを作成します。
	 */
/*	public FwStatusbar createStatusbar() {
		FwStatusbar sts = new FwStatusbar();
		return ( sts ) ;
	}
*/

	//## 共通 #########################################################
	/**
	 * リソースの取得
	 */
	protected ZResource getResource() {
	if ( mRes == null ) {
		return ( getApp().getResource() );
	}
	return mRes;
	}

	/**
	 * アプリケーションの取得
	 */
	protected IZApp getApp() {
	return ( mApp );
	}

	//## サブクラス ###################################################
	/**
	 * アクション変更リスナー
	 */
	private class UIChangedListener implements PropertyChangeListener {
	/**
	 * コンストラクタ
	 * @param mi メニュー項目
	 */
	UIChangedListener(AbstractButton btn) {
	super();
	mButton = btn;
	}

	/**
	 * プロパティ変更。
	 * @param evt プロパティ変更イベント
	 */
	public void propertyChange(PropertyChangeEvent e) {
	String propertyName = e.getPropertyName();
	if (e.getPropertyName().equals(ZAction.LABEL)) {
		String text = (String) e.getNewValue();
		mButton.setText(text);
	} else if (propertyName.equals(ZAction.ENABLED)) {
		Boolean enabledState = (Boolean) e.getNewValue();
		mButton.setEnabled(enabledState.booleanValue());
	} else if (propertyName.equals(ZAction.CHECKED)) {
		Boolean checkedState = (Boolean) e.getNewValue();
		mButton.setSelected(checkedState.booleanValue());

/*
		if ( mButton instanceof JCheckBoxMenuItem ) {
			((JCheckBoxMenuItem)mButton).setState(enabledState.booleanValue());
		} else if ( mButton instanceof 
		mButton.setSelected(enabledState.booleanValue());
*/
	}
	}

    /**
	 * メニュー項目、ツールボタン
     */
	private AbstractButton mButton;
//=====================================================================
}
	//## アトリビュート ###############################################
    /**
	 * アプリケーションクラス
     */
	protected IZApp		mApp;

    /**
	 * リソース
     */
	protected ZResource mRes = null;

    /**
	 * メニューバー
     */
	protected JMenuBar	mMenuBar;

    /**
	 * メニュー項目一覧：メニューアイテム
     */
    protected Hashtable	mMenuItems	 = new Hashtable();

    /**
	 * メニュー項目一覧：メニュー
     */
    protected Hashtable	mMenus		 = new Hashtable();

    /**
	 * メニュー項目一覧：ツールボタン
     */
    protected Hashtable	mToolItems	 = new Hashtable();
    /**
	 * リソース取得用文字列
     */
    private static final String PFX_MENU    = "menu.";
    private static final String PFX_TOOL    = "tool.";
    private static final String SFX_IMAGE   = ".image";
    private static final String SFX_LABEL   = ".label";
    private static final String SFX_ACTION  = ".action";
    private static final String SFX_TOOLTIP = ".tooltip";
    private static final String SFX_CLASS   = ".class";
	private static final String MNEMONIC	= "$";
	private static final String SUBMENU		= "%";
	private static final String TOGGLED		= "+";
	private static final String SEPARATOR	= "-";
}
