package kandata.launcher;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;
import java.awt.*;
import java.awt.event.*;
import java.awt.dnd.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import jp.ne.nifty.iga.midori.shell.io.*;
import jp.ne.nifty.iga.midori.shell.eng.MdShellEnv;

public class KandataLauncher extends JFrame {

	// model
	ArrayList siteCategories = null;	// ArrayList<SiteCategory>

	// view
	JTabbedPane tab;
	ArrayList lists;		// ArrayList<SiteListBox>

	/** ランチャを生成する。*/
	KandataLauncher() {
		super("KandataLauncher");

		// 以前保存された環境を読み込む
		siteCategories = loadEnv();
		if (siteCategories == null) {
			// デフォルトのモデルを作る。
			siteCategories = createDefaultSiteCategories();
		}

		// アクションを作る。
		//NewSiteAction      newSiteAction      = new NewSiteAction(this);
		ExitAction         exitAction         = new ExitAction(this);
		SitePropertyAction sitePropertyAction = new SitePropertyAction(this);
		LaunchAction       launchAction       = new LaunchAction(this);
		DeleteSiteAction   deleteSiteAction   = new DeleteSiteAction(this);
		AboutAction        aboutAction        = new AboutAction(this);
		ActionMap actionmap = new ActionMap();
		actionmap.put("exit",          exitAction);
		actionmap.put("site-property", sitePropertyAction);
		//actionmap.put("new-site",      newSiteAction);
		actionmap.put("launch",        launchAction);
		actionmap.put("delete-site",   deleteSiteAction);
		actionmap.put("about",         aboutAction);
		InputMap inputmap = new InputMap();
		inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Event.SHIFT_MASK), "site-property");  // Shift+Enter

		SiteDropRegister siteDropRegister = new SiteDropRegister(this);
		// ビューを作る
		tab = new JTabbedPane();
		lists = new ArrayList();
		for (int i=0; i<siteCategories.size(); i++) {
			SiteCategory cat = (SiteCategory) siteCategories.get(i);
			SiteListBox list = new SiteListBox(this, cat);
			JScrollPane scrollPane = new JScrollPane(list);
			scrollPane.setBorder(new EmptyBorder(10,10,10,10));
			tab.addTab(cat.getName(), scrollPane);
			lists.add(list);
			SwingUtilities.replaceUIActionMap(list, actionmap);
			SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, inputmap);
			// ドロップ可能にする
    	    new DropTarget(list, DnDConstants.ACTION_COPY_OR_MOVE, siteDropRegister, true);
		}
		tab.setSelectedIndex(siteCategories.size()-1);	// サンプルを選択した状態に
		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(tab, BorderLayout.CENTER);
		JToolBar toolbar = new JToolBar("Kandata");
		toolbar.setFloatable(false);	// 浮かせない
		toolbar.add(exitAction);
		toolbar.addSeparator();
		toolbar.add(launchAction);
		toolbar.add(sitePropertyAction);
		//toolbar.add(newSiteAction);
		toolbar.add(deleteSiteAction);
		toolbar.addSeparator();
		toolbar.add(aboutAction);
		getContentPane().add(toolbar, BorderLayout.NORTH);


		//setDefaultCloseOperation(EXIT_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				exitApplication();
			}
		});
//		setSize(480, 480);
		setSize(480, 0);
		setVisible(true);

		// ウィンドウを縦に伸ばしながら開いていく。
		// さすが次世代！今までのアプリとはチョット違うね！(-o-;
		new GrowWindowThread(this, 480, 480).start();
	}

	/** アプリケーションを終了する。 */
	void exitApplication() {
		storeEnv();
		System.exit(0);
	}

	/** 選択されたサイトを取得。選択されていなければ <code>null</code> を返す。*/
	Site getSelectedSite() {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return null;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		JList list = (JList) lists.get(curcat);
		// リストから選択されたサイトを取得する
		int cursite = list.getSelectedIndex();
		if (cursite == -1) return null;
		Site site = cat.getSiteInfo(cursite);
		return site;
	}

	/** 選択されたサイトのプロパティを編集する. */
	void newSite() {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		SiteListBox list = (SiteListBox) lists.get(curcat);
		// ダイアログを表示
		Site newSite = SiteInfoPane.showModal(this, "新しいサイトの登録", null);
		if (newSite == null) return; 	// cancel
		cat.addSiteInfo(newSite);
		list.fireChanged();
	}
	/** 選択されたサイトのプロパティを編集する. */
	void addSite(Site site) {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		SiteListBox list = (SiteListBox) lists.get(curcat);
		// サイト情報を追加
		cat.addSiteInfo(site);
		list.fireChanged();
	}

	/** 選択されたサイトのプロパティを編集する. */
	void editProperty() {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		SiteListBox list = (SiteListBox) lists.get(curcat);
		// リストから選択されたサイトを取得する
		int cursite = list.getSelectedIndex();
		if (cursite == -1) return;
		Site site = cat.getSiteInfo(cursite);
		// ダイアログを表示
		Site newSite = SiteInfoPane.showModal(this, "サイトの編集", site);
		if (newSite == null) return; 	// cancel
		cat.setSiteInfo(cursite, newSite);
		list.fireChanged();
	}

	/** 選択されたサイトを起動する. */
	void launch() {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		JList list = (JList) lists.get(curcat);
		// リストから選択されたサイトを取得する
		int cursite = list.getSelectedIndex();
		if (cursite == -1) return;
		Site site = cat.getSiteInfo(cursite);

		// 別プロセスで実行（非同期）FIX ME
		String command = "java -cp kandata_launcher.jar kandata.client.http.HttpConnector "+site.getUrl();
		MdShellEnv shellenv = new MdShellEnv();
		shellenv.setIn(System.in);
		shellenv.setOut(System.out);
		shellenv.setErr(System.err);
		int rc = MdShellUtilExec.exec(shellenv, command+"&");	//@@@ TODO:失敗したらメッセージボックスを出す
		System.out.println(site.getUrl()+" rc="+rc);
/*	以下のソースではうまくいかない。why?
		try {
			Process process = Runtime.getRuntime().exec(command);
		}
		catch (Exception e) {
			e.printStackTrace();
			showMessageBox("プロセスの起動に失敗しました", e);
		}
*/
	}

	/** 選択されたサイトを削除する(複数可) */
	int deleteSite() {
		// 選択されたカテゴリ
		int curcat = tab.getSelectedIndex();
		if (curcat == -1) return 0;
		SiteCategory cat = (SiteCategory) siteCategories.get(curcat);
		SiteListBox list = (SiteListBox) lists.get(curcat);
		// リストから選択されたサイトを取得する
		int[] indeces = list.getSelectedIndices();
		if (indeces == null || indeces.length == 0) return 0;
		// 問い合わせる。
		String message;
		if (indeces.length==1) {
			Site site = cat.getSiteInfo(indeces[0]);
			message = site.getName() + " を削除しますか？";
		}
		else {
			message = "" + indeces.length + " 個のサイトを削除しますか？";
		}
		int rc = JOptionPane.showConfirmDialog(this, message,
					"サイトの削除", JOptionPane.YES_NO_OPTION);
		if (rc != JOptionPane.YES_OPTION) return 0;
		// 末尾から削除していく。
		for (int i=indeces.length-1; i>=0; i--) {
			cat.removeSiteInfo(indeces[i]);
		}
		list.fireChanged();
		return indeces.length;
	}

	/** ユーザのディレクトリから保存された環境を読み込む。*/
	ArrayList loadEnv() {
		System.out.println("loadEnv()");
		// 読み込むファイルのパス
		String dir = System.getProperty("user.home") + File.separator + ".KandataLauncher";
		String path = dir + File.separator + "KandataLauncher.properties";
		// ファイルから入力する。
		Properties prop = new Properties();
		FileInputStream in = null;
		try {
			in = new FileInputStream(path);
			prop.load(in);
		}
		catch (IOException e) {
			//showMessageBox("環境の読み込みに失敗しました。\n"+path, e);
			return null;
		}
		finally {
			if (in != null) try { in.close(); } catch (IOException e) {}
		}
		// 設定をファイルから読み込む。
		System.out.println("loadEnv() 設定をファイルから読み込む。");
		int catsize = Integer.parseInt(prop.getProperty("siteCategories.size", "0"));
		ArrayList siteCategories = new ArrayList();
		for (int i=0; i<catsize; i++) {
			String catName = prop.getProperty("siteCategories."+i+".name");
			if (catName != null) {
				SiteCategory cat = new SiteCategory(catName);
				int sitesize = Integer.parseInt(prop.getProperty("siteCategories."+i+".size", "0"));
				for (int j=0; j<sitesize; j++) {
					String url        = prop.getProperty("siteCategories."+i+"."+j+".url");
					String clientType = prop.getProperty("siteCategories."+i+"."+j+".clientType");
					String version    = prop.getProperty("siteCategories."+i+"."+j+".version");
					String name       = prop.getProperty("siteCategories."+i+"."+j+".name");
					String desc       = prop.getProperty("siteCategories."+i+"."+j+".desc");
					String iconName   = prop.getProperty("siteCategories."+i+"."+j+".iconName");
					String iconExt = prop.getProperty("siteCategories."+i+"."+j+".iconExt");
					// アイコンを読み込む
					Image  icon = null;
					byte[] byteArray = null;
					if (iconName != null) {
						icon = Toolkit.getDefaultToolkit().createImage(dir+File.separator+iconName);
						try {
							byteArray = Utilities.getByteArray(new URL(dir+File.separator+iconName));
						} catch (Exception e) {
						}
					}
					Site site = new Site(url, clientType, version, name, desc, icon);
					site.setIconEx(byteArray, iconExt);
					cat.addSiteInfo(site);
				}
				siteCategories.add(cat);
			}
		}
		return siteCategories;
	}

	/**
	 * ユーザのディレクトリに保存する。
	 * ホームディレクトリに .KandataLauncher/ というディレクトリを作成しています。
	 */
	void storeEnv() {
		System.out.println("storeEnv()");
		// 読み込むファイルのパス
		String dir = System.getProperty("user.home") + File.separator + ".KandataLauncher";
		new File(dir).mkdirs();
		String path = dir + File.separator + "KandataLauncher.properties";
		// 設定をファイルに出力する。
		Properties prop = new Properties();
		prop.setProperty("siteCategories.size", ""+siteCategories.size());
		for (int i=0; i<siteCategories.size(); i++) {
			SiteCategory cat = (SiteCategory) siteCategories.get(i);
			prop.setProperty("siteCategories."+i+".name", cat.getName());
			prop.setProperty("siteCategories."+i+".size", ""+cat.getSize());
			for (int j=0; j<cat.getSize(); j++) {
				Site site = (Site) cat.getSiteInfo(j);
				prop.setProperty("siteCategories."+i+"."+j+".url", site.getUrl());
				if (site.getClientType() != null) {
					prop.setProperty("siteCategories."+i+"."+j+".clientType", site.getClientType());
				}
				if (site.getVersion() != null) {
					prop.setProperty("siteCategories."+i+"."+j+".version", site.getVersion());
				}
				prop.setProperty("siteCategories."+i+"."+j+".name", site.getName());
				prop.setProperty("siteCategories."+i+"."+j+".desc", site.getDesc());
				// アイコンを保存する
				if (site.getIconExt() != null) {
					String iconName = ""+ i + "." + j + site.getIconExt();
					prop.setProperty("siteCategories."+i+"."+j+".iconName", iconName);
					prop.setProperty("siteCategories."+i+"."+j+".iconExt", site.getIconExt());
					byte[] byteArray = site.getIconByteArray();
					if (byteArray != null) {
						BufferedOutputStream out = null;
						try {
							String iconPath = dir + File.separator + iconName;
							out = new BufferedOutputStream(new FileOutputStream(new File(iconPath)));
							out.write(byteArray, 0, byteArray.length);
						}
						catch (IOException e) {
						}
						finally {
							if ( out != null ) try { out.close(); } catch (IOException e) {}
						}
					}
				}
			}
		}
		// ファイルに出力する。
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(path);
			prop.store(out, "KandataLauncher");
		}
		catch (IOException e) {
			// どうする？
			showMessageBox("環境の保存に失敗しました。\n"+path, e);
		}
		finally {
			try { out.close(); } catch (IOException e) {}
		}
	}

	/** デフォルトのサイトカテゴリとサンプルを作成します。 */
	ArrayList createDefaultSiteCategories() {
		System.out.println("createDefaultSiteCategories()");
		ArrayList siteCategories = new ArrayList();
		// モデルを作る。
		siteCategories.add(new SiteCategory("お気に入り"));
		siteCategories.add(new SiteCategory("お仕事"));
		siteCategories.add(new SiteCategory("趣味"));
		siteCategories.add(new SiteCategory("その他"));
		SiteCategory samples = new SiteCategory("サンプル");
		Site site = new Site(
			"http://localhost:8080/kandata-hello/servlet/kandata.server.http.ServletServer",
			null,
			null,
			"HelloKandata(localhost)",
			"もっとも簡単なサンプルです",
			ApplicationResources.loadImage("kandata-hello.png"));
		try {
			byte[] byteArray = Utilities.getByteArray(ApplicationResources.getUrl("kandata-hello.png"));
			site.setIconEx(byteArray, ".png");
		} catch (IOException e) {
			e.printStackTrace();
		}
		samples.addSiteInfo(site);
		site = new Site(
			"http://localhost:8080/kandata-roomreservation/servlet/kandata.server.http.ServletServer",
			null,
			null,
			"会議室予約システム(localhost)",
			"ちょっと役に立ちそうなサンプルです",
			ApplicationResources.loadImage("kandata-roomreservation.png"));
		try {
			byte[] byteArray = Utilities.getByteArray(ApplicationResources.getUrl("kandata-roomreservation.png"));
			site.setIconEx(byteArray, ".png");
		} catch (IOException e) {
			e.printStackTrace();
		}
		samples.addSiteInfo(site);
		siteCategories.add(samples);
		return siteCategories;
	}

	/** メッセージBOXを表示します. */
	void showMessageBox(String message, Throwable t) {
		StringWriter st = new StringWriter();
		t.printStackTrace(new PrintWriter(st));
		JOptionPane.showMessageDialog(this, message+"\n"+st.toString());
	}

	public static void main(String[] args) {
		new KandataLauncher();
	}
	
}
