/* $Id: WorkflowDesignerFrame.java,v 1.2 2007/12/11 09:54:58 nito Exp $
 * 
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow.
 * 
 *  Nautica Workflow is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow 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 General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Nautica Workflow; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.tool.wfd;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.List;

import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;

import org.wfmc.x2002.xpdl10.PackageDocument;

import jp.co.argo21.nautica.commons.swing.DialogUtilities;
import jp.co.argo21.nautica.commons.util.ResourceManager;
import jp.co.argo21.nautica.tool.wfd.action.ActionNames;
import jp.co.argo21.nautica.tool.wfd.element.Element;
import jp.co.argo21.nautica.tool.wfd.element.PackageElement;
import jp.co.argo21.nautica.tool.wfd.element.impl.PackageElementImpl;
import jp.co.argo21.nautica.tool.wfd.xpdl.XPDLFileFilter;

/**
 * Nautica WorkflowDesignerのメイン画面である。
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.2 $
 * @since   Nautica Workflow 1.0
 */
public class WorkflowDesignerFrame extends JFrame
{
	/** シリアルバージョンID */
	private static final long serialVersionUID = 1L;

	/** メニューバー */
	private MenuBar menuBar;
	/** ツールボックス */
	private TabbedToolBox toolbox;
	/** デスクトップ */
	private DesktopPane desktop;
	/** ステータスバー */
	private DesignerStatusBar statusBar;
	
	/** 編集対象のパッケージ */
	private PackageElement pkg;
	
	/** ペースト可能な要素を一時保持するエリア */
	private Element pastable;
	
	/**
	 * メイン画面を生成する。
	 * 
	 * @throws java.awt.HeadlessException	モニターがない場合
	 */
	public WorkflowDesignerFrame() throws HeadlessException
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();

		initInputMap();

		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screen = toolkit.getScreenSize();
		int sw = screen.width;
		int sh = screen.height;
		
		menuBar = new MenuBar();

		toolbox = new TabbedToolBox();
		desktop = new DesktopPane();
		statusBar = new DesignerStatusBar();

		setTitle(rm.getResource("WorkflowDesignerFrame.title"));
		enableEvents(AWTEvent.WINDOW_EVENT_MASK);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setJMenuBar(menuBar);

		Container contentPane = getContentPane();
		BorderLayout layout = new BorderLayout();
		contentPane.setLayout(layout);
		contentPane.add(toolbox, BorderLayout.NORTH);
		contentPane.add(desktop, BorderLayout.CENTER);
		contentPane.add(statusBar, BorderLayout.SOUTH);

		//ウィンドウを中央に配置
		int fw = (int)(sw * 0.95);
		int fh = (int)(sh * 0.95);
		setSize(new Dimension(fw, fh));
		setLocation((sw - fw) / 2, 0);
		setIconImage(((ImageIcon)IconManager.getIcon("application")).getImage());

		this.validate();
	}

	/**
	 * ウィンドウイベントを制御する。
	 *
	 * @param	e		WindowEvent
	 */
	protected void processWindowEvent(WindowEvent e)
	{
		super.processWindowEvent(e);

		if (e.getID() == WindowEvent.WINDOW_CLOSING) {
			notifyApplicationWillExit();
		} else {
			repaint();
		}
	}

	/**
	 * 終了
	 */
	public void notifyApplicationWillExit()
	{
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();
		if (isNeedSaved()) {
			int result = DialogUtilities.showYesNoConfirm(rm.getResource("message.confirm.0002"));
			if (result == JOptionPane.YES_OPTION) {
				save();
			}
		}
		setVisible(false);
		dispose();
		System.exit(0);
	}
	
	/**
	 * ショートカットキーのマッピングを行う。
	 */
	private void initInputMap()
	{
		JComponent c = (JComponent)this.getContentPane();
		
		//アクションマップを登録
		ActionMap am = WorkflowDesignerManager.getActionMap();
		c.setActionMap(am);
		
		//インプットマップを登録
		InputMap inputmap = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
		inputmap.put(KeyStroke.getKeyStroke('N', Event.CTRL_MASK), ActionNames.NEW_PACKAGE);
		inputmap.put(KeyStroke.getKeyStroke('O', Event.CTRL_MASK), ActionNames.OPEN_PACKAGE);
		inputmap.put(KeyStroke.getKeyStroke('S', Event.CTRL_MASK), ActionNames.SAVE_PACKAGE);
		inputmap.put(KeyStroke.getKeyStroke('P', Event.CTRL_MASK), ActionNames.PRINT_PACKAGE);
		inputmap.put(KeyStroke.getKeyStroke('Q', Event.CTRL_MASK), ActionNames.EXIT);
		inputmap.put(KeyStroke.getKeyStroke('X', Event.CTRL_MASK), ActionNames.CUT_ACTIVITY);
		inputmap.put(KeyStroke.getKeyStroke('C', Event.CTRL_MASK), ActionNames.COPY_ACTIVITY);
		inputmap.put(KeyStroke.getKeyStroke('V', Event.CTRL_MASK), ActionNames.PASTE_ACTIVITY);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), ActionNames.DELETE_ACTIVITY);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0), ActionNames.EXCEPTION_VIEWER);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), ActionNames.HELP);
		inputmap.put(KeyStroke.getKeyStroke('H', Event.CTRL_MASK), ActionNames.HELP);

		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK), ActionNames.REDUCE_HEIGHT);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK), ActionNames.EXPAND_HEIGHT);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.ALT_MASK), ActionNames.REDUCE_WIDTH);
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.ALT_MASK), ActionNames.EXPAND_WIDTH);
	}

	/**
	 * パッケージをロードする。
	 */	
	void load()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		try {
			PackageDocument doc = fileManager.getDocument();
			PackageElement p = new PackageElementImpl(doc);
			PackageConverter conv = PackageConverter.getInstance();
			fileManager.setDocument(conv.convert(p));
			desktop.closeAllFrame();
			setPackage(p);
			toolbox.setSelectedIndex(TabbedToolBox.TAB_PROCESS);
		} catch (Exception ex) {
			//これがエラーになるならバグ
			ex.printStackTrace();
		}
	}
	
	/**
	 * 保存していない変更があるかどうかを返す。
	 * 
	 * @return	保存が必要ならtrue
	 */
	boolean isNeedSaved()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		try {
			PackageDocument doc = fileManager.getDocument();
			PackageConverter conv = PackageConverter.getInstance();
			PackageDocument converted = conv.convert(pkg);
			if (doc == null) return false;
			if (converted == null) return false;
			
			if (doc.toString().equals(converted.toString())) return false;
			else return true;
		} catch (Exception ex) {
			return true;
		}
	}
	
	/**
	 * 新規パッケージ処理。
	 */
	public void newPackage()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();
		try {
			if (isNeedSaved()) {
				int result = DialogUtilities.showYesNoConfirm(rm.getResource("message.confirm.0002"));
				if (result == JOptionPane.YES_OPTION) {
					save();
				}
			}
			fileManager.initDocument();
			load();
		} catch (Exception ex) {
			WorkflowDesignerManager.getExceptionViewer().setThrowable(ex);
			DialogUtilities.showError(rm.getResource("message.error.0011"));
		}
	}

	/**
	 * 既存パッケージを開く処理。
	 */	
	public void open()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();
		try {
			if (isNeedSaved()) {
				int result = DialogUtilities.showYesNoConfirm(rm.getResource("message.confirm.0002"));
				if (result == JOptionPane.YES_OPTION) {
					save();
				}
			}
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setAcceptAllFileFilterUsed(false);
			fileChooser.setFileFilter(new XPDLFileFilter(rm.getResource("XPDLFileFilter.description")));
			fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

			int selected = fileChooser.showOpenDialog(this);
			if (selected == JFileChooser.CANCEL_OPTION) return;

			File file = fileChooser.getSelectedFile();
			if (file == null) return;

			String path = file.getAbsolutePath();
			if (! path.endsWith(XPDLFileFilter.EXTENSION)) {
				path = path + "." + XPDLFileFilter.EXTENSION;
			}
			fileManager.loadDocument(path);
			load();
		} catch (Exception ex) {
			WorkflowDesignerManager.getExceptionViewer().setThrowable(ex);
			DialogUtilities.showError(rm.getResource("message.error.0012"));
		}
	}
	
	/**
	 * 保存処理。
	 */
	public void save()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();
		String fn = fileManager.getFilename();

		//まだ一度も保存されていない場合は、別名保存のロジックを使用する。
		if (fn == null) {
			saveTo();
			return;
		}

		try {
			PackageConverter conv = PackageConverter.getInstance();
			fileManager.setDocument(conv.convert(pkg));
			fileManager.saveDocument();
		} catch (Exception ex) {
			WorkflowDesignerManager.getExceptionViewer().setThrowable(ex);
			DialogUtilities.showError(rm.getResource("message.error.0013"));
		}
	}

	/**
	 * 別名で保存処理。
	 */	
	public void saveTo()
	{
		FileManager fileManager = WorkflowDesignerManager.getFileManager();
		ResourceManager rm = WorkflowDesignerManager.getResourceManager();
		JFileChooser fileChooser = null;
		File selectedFile = new File(System.getProperty("user.home", ".")
			+ System.getProperty("file.separator", "/")
			+ pkg.getName() + "." + XPDLFileFilter.EXTENSION
			);

		fileChooser = new JFileChooser(selectedFile);
		fileChooser.setAcceptAllFileFilterUsed(false);
		fileChooser.setFileFilter(new XPDLFileFilter(rm.getResource("XPDLFileFilter.description")));
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

		int selected = fileChooser.showSaveDialog(this);
		if (selected == JFileChooser.CANCEL_OPTION) return;

		File file = fileChooser.getSelectedFile();
		if (file == null) return;

		String path = file.getAbsolutePath();
		if (! path.endsWith(XPDLFileFilter.EXTENSION)) {
			path = path + "." + XPDLFileFilter.EXTENSION;
		}

		try {
			PackageConverter conv = PackageConverter.getInstance();
			fileManager.setDocument(conv.convert(pkg));
			fileManager.saveDocument(path);
		} catch (Exception ex) {
			WorkflowDesignerManager.getExceptionViewer().setThrowable(ex);
			DialogUtilities.showError(rm.getResource("message.error.0013"));
		}
	}

	/**
	 * パッケージの検証処理。
	 */	
	public void validatePackage()
	{
		List<ValidationResult> results = PackageValidator.getInstance().validate(pkg);
		ValidationDialog.showDialog(this, results);
	}

	/**
	 * パッケージのインスペクタを表示する。
	 */	
	public void showPackageInspector()
	{
		InspectorDialog.showDialog(this, pkg);
	}

	/**
	 * メニューバーを返す。
	 * 
	 * @return	メニューバー
	 */
	public MenuBar getFrameMemuBar()
	{
		return menuBar;
	}

	/**
	 * デスクトップを返す。
	 * 
	 * @return	デスクトップ
	 */
	public DesktopPane getDesktop()
	{
		return desktop;
	}

	/**
	 * ステータスバーを返す。
	 * 
	 * @return	ステータスバー
	 */
	public DesignerStatusBar getStatusBar()
	{
		return statusBar;
	}

	/**
	 * ツールボックスを返す。
	 * 
	 * @return	ツールボックス
	 */
	public TabbedToolBox getToolbox()
	{
		return toolbox;
	}
	/**
	 * パッケージを返す。
	 * 
	 * @return	パッケージ
	 */
	public PackageElement getPackage()
	{
		return pkg;
	}

	/**
	 * パッケージを設定する。
	 * 
	 * @param elem	パッケージ
	 */
	public void setPackage(PackageElement elem)
	{
		pkg = elem;
		toolbox.loadElements(elem);
	}

	/**
	 * コピーの可否を返す。
	 * 
	 * @return	コピー可能ならtrue
	 */	
	public boolean getEnableCopyAction()
	{
		return menuBar.getEnableCopyAction();
	}

	/**
	 * コピーの可否を設定する。
	 * 
	 * @param f	コピーの可否
	 */	
	public void setEnableCopyAction(boolean f)
	{
		menuBar.setEnableCopyAction(f);
	}
	
	/**
	 * ペーストの可否を返す。
	 * 
	 * @return	ペースト可能ならtrue
	 */
	public boolean getEnablePasteAction()
	{
		return menuBar.getEnablePasteAction();
	}
	
	/**
	 * ペーストの可否を設定する。
	 * 
	 * @param f	ペーストの可否
	 */
	public void setEnablePasteAction(boolean f)
	{
		menuBar.setEnablePasteAction(f);
	}
	
	/**
	 * 削除の可否を返す。
	 * 
	 * @return	削除可能ならtrue
	 */
	public boolean getEnableDeleteAction()
	{
		return menuBar.getEnableDeleteAction();
	}
	
	/**
	 * 削除の可否を設定する。
	 * 
	 * @param f	削除の可否
	 */
	public void setEnableDeleteAction(boolean f)
	{
		menuBar.setEnableDeleteAction(f);
	}

	/**
	 * ペースト可能な要素を返す。
	 * 
	 * @return	要素
	 */
	public Element getPastable()
	{
		return pastable;
	}

	/**
	 * ペースト可能な要素を設定する。
	 * 
	 * @param element	要素
	 */
	public void setPastable(Element element)
	{
		pastable = element;
		menuBar.setEnablePasteAction(pastable != null);
	}
}
