package ash.gui.core;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JToolBar;
import javax.swing.JLabel;
import static java.awt.event.KeyEvent.VK_S;
import ash.util.Log;

/**
 * AshGUIFrameは、Ash標準の GUIフレームを作成するためのベースクラスである。
 * <p>Ash標準の GUIフレームは以下の構成をとる。
 * <ul>
 * <li>メニューバー
 * <li>タスクバー
 * <li>描画領域
 * <li>ステータスバー（メッセージ表示領域、カーソル位置表示領域）
 * </ul>
 * ★Factory Method パターン
 * <p>
 * メニューバー、タスクバー、描画領域は、アプリケーション機能に応じた
 * オブジェクトの生成が必要になるので、それぞれ拡張クラスで該当する
 * オブジェクトを生成するメソッドを FactoryMethod として生成する。
 * <p>
 * AshGUIFrameでは、メニューやタスクバーを生成するためのユーティリティクラスを
 * 提供している。
 */
@SuppressWarnings("serial")
public class AshGUIFrame extends JFrame implements ActionListener {
	/**
	 * コンストラクタ：
	 * メニューバーと画面領域のコンポーネントを生成する。
	 */
	public AshGUIFrame() {
		init();
	}
	public AshGUIFrame(String title) {
		super(title);
		init();
		construct();
		pack();
	}
	private JPanel contentPane;

	/**
	 * 画面構築前に実行する初期化処理
	 */
	private void init() {
		addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent e) { disposeFrame(); }
			});
		contentPane = (JPanel)getContentPane();
		setJMenuBar(createMenuBar());	// メニューバーの作成と設定
	}

	/**
	 * 画面領域のコンポーネントを構築し、画面を初期生成する。
	 * デフォルトでは、ツールバー、描画領域、ステータスバーを生成する。
	 */
	protected void construct() {
		setNorth(createToolBar());		// ツールバーの作成と設定
		setCenter(createWorkArea());	// 描画領域の設定
		setSouth(getStatusBar());		// ステータスバーの設定
	}

	/**
	 * GUIフレームを終了し、リソースを解放する。
	 */
	protected void disposeFrame() {
		if(confirmDispose()) {
			assert Log.debug(1, "==> AshGUIFrame#disposeFrame()");
			dispose();
		}
	}
	/**
	 * 終了時に終了確認を求めたい場合に、オーバライドする。
	 * @return 終了させない場合はfalse
	 */
	protected boolean confirmDispose() {
		return true;
	}

	private static final int TASKBAR_HEIGHT = 34;
	/**
	 * GUIフレームをスクリーンの枠内に収める。
	 */
	public void adjustLocation() {
		Dimension sZ = Toolkit.getDefaultToolkit().getScreenSize();
		Dimension z = this.getSize();
		sZ.height -= TASKBAR_HEIGHT;
		if(z.height > sZ.height) z.height = sZ.height;
		if(z.width > sZ.width) z.width = sZ.width;
		Point loc = this.getLocation();
		int x = Math.min(Math.max(loc.x, 0), sZ.width - z.width);
		int y = Math.min(Math.max(loc.y, 0), sZ.height - z.height);
		this.setBounds(x, y, z.width, z.height);
	}

	/**
	 * レイアウトには BorderLayout を使用している。
	 * 東西南北と中央の５つの領域のコンポーネントを設定／再設定できる。
	 */
	protected void setNorth(Component comp) {	// タスクバー
		if(comp != null) contentPane.add(comp, BorderLayout.NORTH);
	}
	protected void setCenter(Component comp) {	// 描画領域
		if(comp != null) contentPane.add(comp, BorderLayout.CENTER);
	}
	protected void setSouth(Component comp) {	// ステータスバー
		if(comp != null) contentPane.add(comp, BorderLayout.SOUTH);
	}
	protected void setWest(Component comp) {	// 初期値なし
		if(comp != null) contentPane.add(comp, BorderLayout.WEST);
	}
	protected void setEast(Component comp) {	// 初期値なし
		if(comp != null) contentPane.add(comp, BorderLayout.EAST);
	}

	/*
	 * ★メニューバーとメニューを作成するFactoryMethod：
	 * メニューバーを作成する場合は createMenuBar()をオーバライドする。
	 * メニューバーの実装の際には、メニュー生成のクラス AshMenu を拡張する。
	 * @return メニューバー
	 */
	protected JMenuBar createMenuBar() { return null; }

	/**
	 * ★ツールバーを作成するFactoryMethod：
	 * ツールバーを作成する場合は createToolBar()をオーバライドする。
	 * ツールバーの実装の際には、ツールバー作成クラス AshToolBar を拡張する。
	 * @return ツールバー
	 */
	protected JToolBar createToolBar() { return null; }

	/**
	 * ★描画領域を作成するFactoryMethod：
	 * アプリケーションの主要な機能を実現する領域となるコンポーネントを返す。
	 * @return 描画領域のコンポーネント
	 */
	protected Component createWorkArea() { return null; }

	/**
	 *  ステータスバーの作成と操作：
	 * @return ステータスバー
	 */
	protected StatusBar getStatusBar() {
		if(statusBar == null) statusBar = new StatusBar();
		return statusBar;
	}
	private StatusBar statusBar;
	/**
	 * ステータスバーの表示／非表示を切り替える。
	 * @param visible 表示の場合true
	 */
	protected void statusSetVisible(boolean visible) {
		statusBar.setVisible(visible);
	}
	/**
	 * ステータスバーの表示／非表示を切り替えるメニュ項目を作成する。
	 * @return ステータスバーの表示／非表示を切り替えるメニュ項目
	 */
	public JMenuItem makeStatusMenuItem() {
		return getStatusBar().makeStatusMenuItem();
	}
	/**
	 * ステータスバーにメッセージを表示する。
	 * @param message ステータスバーに表示するメッセージ
	 */
	public void setMessage(String message) {
		getStatusBar().showMessage(message);
	}
	/**
	 * ステータスバーにカーソル位置を表示する。
	 * @param row 行位置
	 * @param column 列位置
	 */
	public void showCursorPos(int row, int column) {
		getStatusBar().showCursorPos(row, column);
	}
	/**
	 * カーソル位置表示の書式を決める。
	 * 変更したい場合はオーバライド定義する。
	 * @param row 行位置
	 * @param column 列位置
	 * @return 位置情報を書式化した文字列
	 */
	protected String cursorPosDisplay(int row, int column) {
		return "(" + row + ", " + column + ")";
	}
	/**
	 * メッセージ表示領域とカーソル位置表示領域をもつステータスバー：
	 */
	protected class StatusBar extends JPanel {
		private JLabel statusLabel = new JLabel("情報を表示する領域です。");
		private JLabel cursorPos = new JLabel();
		private StatusBar() {
			setLayout(new BorderLayout());
			add(statusLabel, BorderLayout.CENTER);
			add(cursorPos, BorderLayout.EAST);
		}
		void showMessage(String message) {
			statusLabel.setText(message);
		}
		void showCursorPos(int row, int column) {
			cursorPos.setText(cursorPosDisplay(row, column));
		}
		JMenuItem makeStatusMenuItem() {
			final JCheckBoxMenuItem mi =
				new JCheckBoxMenuItem("ステータスバー(S)", true);
			mi.setMnemonic(VK_S);
			mi.addItemListener(new ItemListener() {
					public void itemStateChanged(ItemEvent e) {
						setVisible(mi.isSelected());
					}
				});
			return mi;
		}
	}

	/**
	 * GUI 関連のユーティリティ
	 */
	/**
	 * ビープ音を発生する。
	 */
	public static void beep() { toolkit.beep(); }
	public static Toolkit toolkit = Toolkit.getDefaultToolkit();

	/**
	 * 一意なアクション名を生成する
	 */
	protected static String actionName() {
		return String.format("ACTION_%03d", actionSerial++);
	}
	private static int actionSerial = 0;

	/*
	 * ActionListener インタフェースの実装：
	 * デフォルトのアクション実行ではactionをAshのコマンドとして実行する。
	 */
	@Override public void actionPerformed(ActionEvent ae) {
		GUIAdapter.getListener().actionPerformed(ae);
	}

	/**
	 * メッセージ表示や確認のためのダイアログ・ユーティリティ
	 */
	public void showMessage(String message) {
		JOptionPane.showMessageDialog(contentPane, message);
	}
	public void showError(String message, String title) {
		beep();
		JOptionPane.showMessageDialog(contentPane, message, title,
									  JOptionPane.ERROR_MESSAGE);
	}
	public int showConfirm(String message) {
		return JOptionPane.showConfirmDialog(contentPane, message);
	}
	public int showConfirm(String message, String title) {
		return JOptionPane.showConfirmDialog(contentPane, message, title,
											 JOptionPane.YES_NO_OPTION);
	}
	public String inputDialog(String message, String title) {
		return JOptionPane.showInputDialog(contentPane, message, title,
										   JOptionPane.QUESTION_MESSAGE);
	}
}
