/**
 * WikiMainWindowController
 * @author Ryuhei Terada
 */
package com.wiki.standalone.moxkiriya;

import java.awt.Desktop;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.ResourceBundle;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.version.Version;
import javax.jcr.version.VersionIterator;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.events.MouseEvent;
import org.w3c.dom.html.HTMLAnchorElement;

import com.wiki.standalone.moxkiriya.dialog.AboutMoxkiriyaDialog;
import com.wiki.standalone.moxkiriya.dialog.AlertDialog;
import com.wiki.standalone.moxkiriya.dialog.DialogBase;
import com.wiki.standalone.moxkiriya.dialog.DialogBase.CloseType;
import com.wiki.standalone.moxkiriya.dialog.SelectPartyDialog;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableView.TableViewSelectionModel;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Callback;

/**
 * MainWindowコントローラ
 */
public class WikiMainWindowController implements Initializable {
	/** Primary Stage */
	private Stage          stage_;
	
	/** resourceBundle */
	private ResourceBundle resourceBundle_;
	
	/** WikiEngine */
	private WikiEngine     wikiEngine_;

	/** Preview window */
	private WikiPreviewWindow previewWindow_ = null;
	
	/*
	 * MenuBar controls.
	 */
	@FXML	private MenuItem menuItemNew;
	@FXML	private MenuItem menuItemImport;
	@FXML	private MenuItem menuItemExport;
	@FXML	private MenuItem menuItemExit;
	@FXML	private MenuItem menuItemSelectParty;
	@FXML	private MenuItem menuItemAboutMoxkiriya;

	/*
	 * ChoiceView controls.
	 */
	@FXML	private Hyperlink choiceViewHyperlinkReading;
	@FXML	private Hyperlink choiceViewHyperlinkPageList;
	@FXML	private Hyperlink choiceViewHyperlinkEdit;

	/*
	 * Loading animation. 
	 */
	@FXML	private ImageView ImageViewLoading;
	
	/*
	 * WebView controls.
	 */
	@FXML   private AnchorPane AnchorPaneWebView;
	@FXML	private Button     webViewMenuAnchorPaneButtonBack;
	@FXML	private Button     webViewMenuAnchorPaneButtonForward;
	@FXML	private Button     webViewMenuAnchorPaneButtonReload;
	@FXML	private Button     webViewMenuAnchorPaneButtonHome;
	@FXML	private TextField  webViewMenuAnchorPaneTextFieldSearchWiki;
	@FXML	private Button     webViewMenuAnchorPaneButtonSearch;

	@FXML   private Hyperlink  webViewHyperlinkMain;
	@FXML   private Hyperlink  webViewHyperlinkHistory;
	@FXML   private AnchorPane webViewAnchorPaneMain;
	@FXML	private WebView    webView;
	@FXML   private AnchorPane webViewAnchorPaneHistory;
	@FXML   private FlowPane   webViewHistoryBreadcrumbsPane;
	@FXML   private ListView<VersionHistoryListItem>   webViewHistoryListView;
	@FXML   private AnchorPane webViewHistoryAnchorPaneContent;
	@FXML   private TextField  webViewHistoryTextFieldTitle;
	@FXML	private TextArea   webViewHistorytextAreaContents;
	@FXML   private Button     webViewHistoryButtonRestoreVersion;

	/*
	 * editAnchorPane controls.
	 */
	@FXML	private AnchorPane      AnchorPaneEdit;
	@FXML	private TextField       textFieldTitle;
	@FXML	private TextArea        textAreaContents;
	@FXML   private Label           labelAttacheFile;
	@FXML   private TextField       textFieldAttachFile;
	@FXML   private Button          buttonChoiceFile;
	@FXML	private Hyperlink       hyperlinkEditAttachFiles;
	@FXML	private Button          buttonPreview;
	@FXML	private Button          buttonSave;
	@FXML   private Button          buttonCancel;

	/*
	 * AnchorPaneContentList controls.
	 */
	@FXML	private AnchorPane       AnchorPaneContentList;
	@FXML   private ComboBox<String> comboBoxNamespace;
	@FXML   private Hyperlink        hyperlinkRedraw;
	@FXML	private TableView<PageData>             tableViewContentList;
	@FXML   private TableColumn<PageData, Boolean>  tableColumnCheck;
	@FXML   private TableColumn<PageData, String>   tableColumnTitle;
	@FXML   private TableColumn<PageData, String>   tableColumnContent;
	@FXML   private TableColumn<PageData, String>   tableColumnDate;
	@FXML   private TableColumn<PageData, String>   tableColumnRefs;
	@FXML   private Button                          buttonDeletePages;
	private CheckBox                                checkBoxAll_;	

	/**
	 * Edit mode NEW or MODIFY
	 */
	private enum EditMode {
		NONE,
		NEW,
		MODIFY
	};

	/** edit mode.*/
	private EditMode editMode_ = EditMode.NONE;

	/** Close type */
	private WikiMainWindow.CloseType closeType_ = WikiMainWindow.CloseType.EXIT;

	/**
	 * Version history list item
	 */
	public class VersionHistoryListItem {
		/** version */
		private Version version_;
		
		/** Created data. */
		private Date created_;
		
		/** version node */
		private Node node_;
		
		/**
		 * Constructor.
		 * @param created
		 * @param node
		 */
		public VersionHistoryListItem(Version version, Date created, Node node) {
			version_ = version;
			created_ = created;
			node_    = node;
		}

		/**
		 * Version getter.
		 * @return Version
		 */
		public Version getVersion() {
			return version_;
		}
		
		/**
		 * Created getter.
		 * @return Date
		 */
		public Date getCreated() {
			return created_;
		}

		/**
		 * Node getter.
		 * @return Node
		 */
		public Node getNode() {
			return node_;
		}		
	}
	
	@Override
	public void initialize(URL url, ResourceBundle resourceBundle) {
		resourceBundle_ = resourceBundle;

		setLinkClickListner(webView.getEngine());
		webView.setContextMenuEnabled(false);

		previewWindow_  = new WikiPreviewWindow(stage_, resourceBundle_);
		previewWindow_.setMainWindowController(this);

		checkBoxAll_    = new CheckBox();
		checkBoxAll_.selectedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> value, Boolean oldValue, Boolean newValue) {
				ObservableList<PageData> pageDataList = tableViewContentList.getItems();
				
				for(PageData pageData: pageDataList) {
					pageData.checkProperty().setValue(newValue);;
				}
			}
		});
		
		tableColumnCheck.setCellFactory(new Callback<TableColumn<PageData, Boolean>, TableCell<PageData, Boolean>>() {
			@Override
			public TableCell<PageData, Boolean> call(TableColumn<PageData, Boolean> param) {
				CheckBoxTableCell<PageData, Boolean> checkBoxTableCell = new CheckBoxTableCell<PageData, Boolean>();
				checkBoxTableCell.setAlignment(Pos.CENTER);

				return checkBoxTableCell;
			}
		});

		tableColumnTitle.setCellFactory(new Callback<TableColumn<PageData, String>, TableCell<PageData, String>>() {
			@Override
			public TableCell<PageData, String> call(TableColumn<PageData, String> param) {
				TableCell<PageData, String>
				           tableCell = new TableCell<PageData, String>() {
					@Override
					public void updateItem(String uuid, boolean empty) {
						super.updateItem(uuid, empty);
						if(empty != true) {
							try {
								HashMap<String, PageData> pageMap  = wikiEngine_.queryPageUUID(uuid);
								PageData                  pageData = pageMap.get(uuid);
								Hyperlink                 hyperlink = new Hyperlink(pageData.getTitle());

								hyperlink.setUserData(pageData);
								hyperlink.setOnMouseClicked(new EventHandler<Event>() {
									@Override
									public void handle(Event event) {
										try {
											Hyperlink                 source   = (Hyperlink)event.getSource();
											PageData                  pageData = (PageData)source.getUserData();

											wikiEngine_.setPageDataMap(pageData);
											loadWikiContent();

											webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
											webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
											webViewAnchorPaneMain.setVisible(true);
											webViewAnchorPaneHistory.setVisible(false);
										} catch (Exception e) {
											e.printStackTrace();
										}
									}
								});
								
								setGraphic(hyperlink);
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
						else {
							setGraphic(null);
						}
					}
				};

				return tableCell;
			}
		});

		webViewHistoryListView.setCellFactory(new Callback<ListView<VersionHistoryListItem>, ListCell<VersionHistoryListItem>>() {
			@Override
			public ListCell<VersionHistoryListItem> call(ListView<VersionHistoryListItem> arg0) {
				return new ListCell<VersionHistoryListItem>() {
					@Override
					protected void updateItem(VersionHistoryListItem item, boolean empty) {
						try {
							super.updateItem(item, empty);
							if(empty != true) {
								Hyperlink hyperlinkCreated = new Hyperlink(item.getCreated().toString());
								Version   version          = item.getVersion();
								Version   baseVersion      = wikiEngine_.getBaseVersion();
								
								if(version.getIdentifier().equals(baseVersion.getIdentifier()) == true) {
									hyperlinkCreated.setId("webViewHistoryBaseVersion");
								}
								
								hyperlinkCreated.setUserData(item);
								hyperlinkCreated.setOnAction(new EventHandler<ActionEvent>() {
									@Override
									public void handle(ActionEvent event) {
										try {
											ObservableList<javafx.scene.Node> breadcrumbsList = webViewHistoryBreadcrumbsPane.getChildren();
											
											breadcrumbsList.clear();
											breadcrumbsList.add(createBreadcrumbsHyperlink("key.History.Breadcrumbs.List", 
													new EventHandler<ActionEvent>() {
												@Override
												public void handle(ActionEvent event) {
													ObservableList<javafx.scene.Node> breadcrumbsList = webViewHistoryBreadcrumbsPane.getChildren();
													
													breadcrumbsList.clear();
													breadcrumbsList.add(createBreadcrumbsLabel("key.History.Breadcrumbs.List"));
													webViewHistoryListView.setVisible(true);
													webViewHistoryAnchorPaneContent.setVisible(false);
												}
											}));
											breadcrumbsList.add(createBreadcrumbsLabel("key.History.Breadcrumbs.Separator"));
											breadcrumbsList.add(createBreadcrumbsLabel("key.History.Breadcrumbs.Content"));
		
											Hyperlink              target = (Hyperlink)event.getTarget();
											VersionHistoryListItem item   = (VersionHistoryListItem)target.getUserData();
											Node                   node   = item.getNode();
	
											webViewHistoryTextFieldTitle.setText(node.getProperty(WikiRepository.PROPERTY_TITLE).getString());
											webViewHistorytextAreaContents.setText((node.getProperty(WikiRepository.PROPERTY_CONTENT).getString()));
											webViewHistoryAnchorPaneContent.setUserData(item);
	
											webViewHistoryListView.setVisible(false);
											webViewHistoryAnchorPaneContent.setVisible(true);
										} catch (Exception e) {
											e.printStackTrace();
										}
									}								
								});
								setGraphic(hyperlinkCreated);
							}
						} catch (Exception e1) {
							e1.printStackTrace();
						}
					}
				};
			}
		});
		tableColumnCheck.setGraphic(checkBoxAll_);
		tableColumnCheck.setCellValueFactory(new PropertyValueFactory<PageData, Boolean>("check"));
		tableColumnTitle.setCellValueFactory(new PropertyValueFactory<PageData, String>("uuid"));
		tableColumnContent.setCellValueFactory(new PropertyValueFactory<PageData, String>("introduction"));
		tableColumnDate.setCellValueFactory(new PropertyValueFactory<PageData, String>("date"));
		
		webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
		webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
		webViewAnchorPaneMain.setVisible(true);
		webViewAnchorPaneHistory.setVisible(false);
	}

	/**
	 * Close type getter. 
	 * @return WikiMainWindow.CloseType
	 */
	public WikiMainWindow.CloseType getCloseType() {
		return closeType_;
	}
	
	/**
	 * Stageを設定する。
	 * @param stage
	 */
	public void setStage(Stage stage) {
		stage_ = stage;
	}

	/**
	 * WikiEngineを設定する。
	 * @param wikiEngine
	 */
	public void setWikiEngine(WikiEngine wikiEngine) {
		wikiEngine_ = wikiEngine;
	}
	
	/**
	 * WikiEngineを取得する。
	 * @return WikiEngine
	 */
	public WikiEngine getWikiEngine() {
		return wikiEngine_;
	}

	/**
	 * Hide preview window.
	 */
	public void hidePreviewWindow() {
		previewWindow_.hide();
	}
	
	@FXML
	public void onActionMenuItemNew(ActionEvent event) {
		/*
		 * 各コントロールの初期値を入力する。
		 */
		textFieldTitle.clear();
		textAreaContents.clear();
		buildEditView("");
	}

	@FXML
	public void onActionMenuItemImport(ActionEvent event) {
		FileChooser fileChooser = new FileChooser();
		
		fileChooser.setTitle(resourceBundle_.getString("key.ImportDialog.Title"));
		fileChooser.getExtensionFilters().addAll(new ExtensionFilter("XML Files", "*.xml"));

		File selectedFile = fileChooser.showOpenDialog(stage_);

		if(selectedFile != null) {
			try {
				wikiEngine_.importSystemView(selectedFile);

				AlertDialog dialog = new AlertDialog(AlertDialog.MessageType.SuccessImport);
				dialog.showDialog(stage_, resourceBundle_);				
				stage_.hide();
			} catch (Exception e) {
				e.printStackTrace();
				try {
					AlertDialog dialog = new AlertDialog(AlertDialog.MessageType.FailureImport);
					dialog.showDialog(stage_, resourceBundle_);
					stage_.hide();
				} catch (Exception e1) {
					e1.printStackTrace();
				}				
			}
		}
	}

	@FXML
	public void onActionMenuItemExport(ActionEvent event) {
		FileChooser fileChooser = new FileChooser();
		
		fileChooser.setTitle(resourceBundle_.getString("key.ExportDialog.Title"));
		fileChooser.getExtensionFilters().addAll(new ExtensionFilter("XML Files", "*.xml"));

		File selectedFile = fileChooser.showSaveDialog(stage_);

		if(selectedFile != null) {
			try {
				wikiEngine_.exportSystemView(selectedFile);
				AlertDialog dialog = new AlertDialog(AlertDialog.MessageType.SuccessExport);
				dialog.showDialog(stage_, resourceBundle_);				
			} catch (Exception e) {
				e.printStackTrace();
				try {
					AlertDialog dialog = new AlertDialog(AlertDialog.MessageType.FailureExport);
					dialog.showDialog(stage_, resourceBundle_);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	@FXML
	public void onActionMenuItemExit(ActionEvent event) {
		try {
			if (AnchorPaneEdit.isVisible() == true) {
				/*
				 * editAnchorPane表示中の場合、
				 * 編集キャンセル確認ダイアログを表示する。
				 */
				if(showConfirmEditCancelDialog() == AlertDialog.CloseType.OK) {
					editMode_ = EditMode.NONE;
					wikiEngine_.cancelCheckout();
					previewWindow_.hide();
					stage_.hide();
				}
			}
			else {
				stage_.hide();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@FXML
	public void onActionMenuItemSelectParty(ActionEvent event) {
		try {
			if (AnchorPaneEdit.isVisible() == true) {
				/*
				 * editAnchorPane表示中の場合、
				 * 編集キャンセル確認ダイアログを表示する。
				 */
				if(showConfirmEditCancelDialog() == AlertDialog.CloseType.OK) {
					editMode_ = EditMode.NONE;
					wikiEngine_.cancelCheckout();
				}
			}
			
			SelectPartyDialog selectPartyDialog = new SelectPartyDialog();

			if(selectPartyDialog.showDialog(stage_, resourceBundle_) == CloseType.OK) {
				closeType_ = WikiMainWindow.CloseType.RESTART;
				stage_.hide();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionMenuItemAboutMoxkiriya(ActionEvent event) {
		try {
			DialogBase dialog = new AboutMoxkiriyaDialog();
			dialog.showDialog(stage_, resourceBundle_);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onKeyPressedTextFieldSearchWiki(KeyEvent event) {
		try {
			if(event.getCode().equals(KeyCode.ENTER) == true) {
				doSearchWiki();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}			
	}

	@FXML
	public void onActionButtonSearch(ActionEvent event) {
		try {
			doSearchWiki();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onMouseClickedHyperlinkReading() {
		try {
			AlertDialog.CloseType closeType = AlertDialog.CloseType.OK;

			if(AnchorPaneEdit.isVisible() == true) {
				/*
				 * editAnchorPane表示中の場合、
				 * 編集キャンセル確認ダイアログを表示する。
				 */
				closeType = showConfirmEditCancelDialog();
			}

			if(closeType == AlertDialog.CloseType.OK) {
				editMode_ = EditMode.NONE;
				wikiEngine_.cancelCheckout();

				PageData pageData = getSelectingPageData();

				if(pageData != null) {
					wikiEngine_.setPageDataMap(pageData);
				}

				menuItemNew.setDisable(false);

				loadWikiContent();

				AnchorPaneEdit.setVisible(false);
				AnchorPaneContentList.setVisible(false);
				AnchorPaneWebView.setVisible(true);

				webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
				webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
				webViewAnchorPaneMain.setVisible(true);
				webViewAnchorPaneHistory.setVisible(false);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onMouseClickedHyperlinkEdit() {
		try {
			wikiEngine_.refreshSession();
			buildEditView(EditMode.MODIFY, "");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@FXML
	public void onMouseClickedHyperlinkPageList() {
		try {
			AlertDialog.CloseType closeType = AlertDialog.CloseType.OK;

			if(AnchorPaneEdit.isVisible() == true) {
				/*
				 * editAnchorPane表示中の場合、
				 * 編集キャンセル確認ダイアログを表示する。
				 */
				closeType = showConfirmEditCancelDialog();
			}

			if(closeType == AlertDialog.CloseType.OK) {
				editMode_ = EditMode.NONE;
				wikiEngine_.cancelCheckout();

				buildComboBoxNamespace();

				buildTableViewPageList();
				AnchorPaneWebView.setVisible(false);
				AnchorPaneEdit.setVisible(false);		
				AnchorPaneContentList.setVisible(true);
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	@FXML
	public void onActionButtonBack(ActionEvent event) {
		try {
			String    content = wikiEngine_.back();
			WebEngine webEngine = webView.getEngine();
			webEngine.loadContent(content);
			
			HashMap<String, PageData> map     = wikiEngine_.getPageDataMap();
			boolean                   canEdit = (map.size() != 1) ? true : false;

			choiceViewHyperlinkEdit.setDisable(canEdit);		
			menuItemNew.setDisable(false);

			AnchorPaneEdit.setVisible(false);
			AnchorPaneContentList.setVisible(false);
			AnchorPaneWebView.setVisible(true);

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);

			webViewMenuAnchorPaneButtonBack.setDisable(!wikiEngine_.canBack());
			webViewMenuAnchorPaneButtonForward.setDisable(!wikiEngine_.canForward());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@FXML
	public void onActionButtonForward(ActionEvent event) {
		try {
			String    content = wikiEngine_.forward();
			WebEngine webEngine = webView.getEngine();
			webEngine.loadContent(content);

			HashMap<String, PageData> map     = wikiEngine_.getPageDataMap();
			boolean                   canEdit = (map.size() != 1) ? true : false;

			choiceViewHyperlinkEdit.setDisable(canEdit);
			menuItemNew.setDisable(false);

			AnchorPaneEdit.setVisible(false);
			AnchorPaneContentList.setVisible(false);
			AnchorPaneWebView.setVisible(true);

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);

			webViewMenuAnchorPaneButtonBack.setDisable(!wikiEngine_.canBack());
			webViewMenuAnchorPaneButtonForward.setDisable(!wikiEngine_.canForward());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionButtonReload(ActionEvent event) {
		try {
			wikiEngine_.refreshSession();
			loadWikiContent();

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionButtonHome(ActionEvent event) {
		try {
			wikiEngine_.setPageDataMap(WikiEngine.MAINPAGE_TITLE);
			loadWikiContent();

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onMouseClickedHyperlinkRedraw() {
		try {
			buildTableViewPageList();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onMouseClickedWebViewHyperlinkMain() {
		webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
		webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
		webViewAnchorPaneMain.setVisible(true);
		webViewAnchorPaneHistory.setVisible(false);
	}

	@FXML
	public void onMouseClickedWebViewHyperlinkHistory() {
		try {
			ObservableList<javafx.scene.Node> breadcrumbsList = webViewHistoryBreadcrumbsPane.getChildren();
			
			breadcrumbsList.clear();
			breadcrumbsList.add(createBreadcrumbsLabel("key.History.Breadcrumbs.List"));

			ObservableList<VersionHistoryListItem> list = webViewHistoryListView.getItems();

			list.clear();
	
			VersionIterator versionIter = wikiEngine_.getVersionHistory();
			versionIter.skip(1);
			while(versionIter.hasNext() == true) {
				Version      version  = versionIter.nextVersion();
				NodeIterator nodeIter = version.getNodes();
				
				while(nodeIter.hasNext() == true) {
					Node node = nodeIter.nextNode();
					
					list.add(new VersionHistoryListItem(version, version.getCreated().getTime(), node));
				}
			}

			webViewHyperlinkMain.setId("webViewMainHyperlinkInactive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkActive");
			webViewAnchorPaneMain.setVisible(false);
			webViewAnchorPaneHistory.setVisible(true);

			webViewHistoryListView.setVisible(true);
			webViewHistoryAnchorPaneContent.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionButtonRestoreVersion(ActionEvent event) {
		try {
			VersionHistoryListItem item = (VersionHistoryListItem)webViewHistoryAnchorPaneContent.getUserData();
			wikiEngine_.restoreVersion(item.getVersion());

			wikiEngine_.refreshSession();
			loadWikiContent();

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@FXML
	public void onActionButtonDeletePages(ActionEvent event) {
		try {
			ObservableList<PageData> pageDataList = tableViewContentList.getItems();
	
			for(PageData pageData: pageDataList) {
				if(pageData.checkProperty().getValue() == true) {
					wikiEngine_.deletePage(pageData);
				}
			}

			HashMap<String, PageData> map = wikiEngine_.queryPageTitle(WikiEngine.MAINPAGE_TITLE);
			if(map.size() < 1) {
				/*
				 * MainPageがなくなった場合
				 */
				PageData pageData = new PageData();

				pageData.setNamespace(WikiRepository.PROPERTY_MAIN);
				pageData.setTitle(WikiEngine.MAINPAGE_TITLE);
				pageData  = wikiEngine_.checkin(pageData);
			}

			wikiEngine_.clearHistory();
			wikiEngine_.setPageDataMap(WikiEngine.MAINPAGE_TITLE);
			loadWikiContent();

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@FXML
	public void onActionButtonChoiceFile(ActionEvent event) {
		try {
			FileChooser fileChooser = new FileChooser();
			fileChooser.setTitle("Select " + textFieldTitle.getText());
			File file = fileChooser.showOpenDialog(stage_);
			
			if(file != null) {
				textFieldAttachFile.setText(file.getPath());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionButtonPreview(ActionEvent event) {
		try {
			previewWindow_.setWikiRepository(wikiEngine_.getWikiRepository());
			previewWindow_.show();
		} catch (Exception e) {
			e.printStackTrace();
		}			
	}

	@FXML
	public void onActionButtonSave(ActionEvent event) {
		try {			
			String  title = textFieldTitle.getText();

			if(title.isEmpty() == true) {
				Stage       stage       = new Stage(StageStyle.UTILITY);
				stage.initOwner(stage_);
				stage.initModality(Modality.WINDOW_MODAL);

				AlertDialog alertDialog = new AlertDialog(AlertDialog.MessageType.AlertTitleEmpty);
				alertDialog.showDialog(stage, resourceBundle_);
			}
			else {
				PageData pageData = (PageData)AnchorPaneEdit.getUserData();
				
				if(editMode_ == EditMode.NEW) {
					if(pageData == null) {
						pageData = new PageData();
						pageData.setNamespace(WikiRepository.PROPERTY_MAIN);
					}
				}
				
				pageData.setTitle(title);
				pageData.setContent(textAreaContents.getText());

				String filename = textFieldAttachFile.getText();
				if(filename.isEmpty() != true) {
					pageData.setFileData(new File(filename));					
				}
				pageData  = wikiEngine_.checkin(pageData);
				editMode_ = EditMode.NONE;
				previewWindow_.hide();

				/*
				 * 更新後のページを再parse
				 */
				wikiEngine_.setPageDataMap(pageData);
				loadWikiContent();

				menuItemNew.setDisable(false);

				/*
				 * PaneをWebViewAnchorPaneに切り替える。
				 */
				AnchorPaneWebView.setVisible(true);
				AnchorPaneEdit.setVisible(false);

				webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
				webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
				webViewAnchorPaneMain.setVisible(true);
				webViewAnchorPaneHistory.setVisible(false);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@FXML
	public void onActionButtonCancel(ActionEvent event) {
		try {
			if(showConfirmEditCancelDialog() == AlertDialog.CloseType.OK) {
				editMode_ = EditMode.NONE;
				wikiEngine_.cancelCheckout();
				previewWindow_.hide();
				menuItemNew.setDisable(false);

				/*
				 * 確認ダイアログでOKが押下された場合
				 * EditAnchorPaneからWebViewペインに表示を切り替え
				 */
				AnchorPaneWebView.setVisible(true);
				AnchorPaneEdit.setVisible(false);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 編集キャンセル確認ダイアログを表示する。
	 * @return 終了種別
	 * @throws Exception 
	 */
	private AlertDialog.CloseType showConfirmEditCancelDialog() throws Exception {
		AlertDialog alertDialog = new AlertDialog(AlertDialog.MessageType.ConfirmEditCancel);

		return alertDialog.showDialog(stage_, resourceBundle_);
	}	

	/**
	 * 編集Viewを構築する。
	 */
	public void buildEditView(String title) {
		buildEditView(EditMode.NEW, title);
	}

	/**
	 * 編集Viewを構築する。
	 */
	public void buildEditView(EditMode editMode, String title) {
		try {
			String content   = "";
			String namespace = "";

			editMode_ = editMode;

			if(editMode == EditMode.MODIFY) {
				HashMap<String, PageData> map      = wikiEngine_.getPageDataMap();
				PageData                  pageData = map.values().iterator().next();

				AnchorPaneEdit.setUserData(pageData);
				wikiEngine_.checkout();
				title     = pageData.getTitle();
				content   = pageData.getContent();
				namespace = pageData.getNamespace();
			}
			else {
				AnchorPaneEdit.setUserData(null);

				if(editMode_ == EditMode.NEW) {
					if(title.isEmpty() != true) {
						PageData pageData  = new PageData();
						String   pageTitle = title;

						pageData.setNamespace(WikiRepository.PROPERTY_MAIN);						

						if(title.contains(":")) {
							namespace = title.substring(0, title.indexOf(":"));
						}

						if(namespace.isEmpty() != true) {
							if(wikiEngine_.isContainsNamespaceList(namespace) == true) {
								pageData.setNamespace(namespace);								
								pageTitle = title.substring(namespace.length() + ":".length(), title.length());
							}
							else {
								pageTitle = title;
							}
						}

						pageData.setTitle(pageTitle);
						AnchorPaneEdit.setUserData(pageData);

						title     = pageData.getTitle();
						namespace = pageData.getNamespace();
					}
				}
			}

			boolean cannotAttacheFile = namespace.equals(WikiEngine.NAMESPACE_FILE) == true
									? false
									: true;
			textFieldAttachFile.setDisable(cannotAttacheFile);
			labelAttacheFile.setDisable(cannotAttacheFile);
			buttonChoiceFile.setDisable(cannotAttacheFile);

			/*
			 * 各コントロールの初期値を入力する。
			 */
			textFieldTitle.setText(title);
			textAreaContents.setText(content);
			textFieldAttachFile.setText("");

			menuItemNew.setDisable(true);

			/*
			 * WebViewペインからEditAnchorPaneに表示を切り替え
			 */
			AnchorPaneWebView.setVisible(false);
			AnchorPaneContentList.setVisible(false);
			AnchorPaneEdit.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}
	
	/**
	 * Latest contents getter.
	 * @return PageData
	 * @throws Exception 
	 */
	public PageData getLatestContents() throws Exception {
		PageData pageData = new PageData();
		PageData editData = (PageData)AnchorPaneEdit.getUserData();
	
		pageData.setTitle(textFieldTitle.getText());
		pageData.setContent(textAreaContents.getText());
		
		String namespace = editData != null
							? editData.getNamespace()
							: WikiRepository.PROPERTY_MAIN;
		pageData.setNamespace(namespace);
		String pathname = textFieldAttachFile.getText();
		if( (pathname != null)
		 && (pathname.isEmpty() != true)) {
			pageData.setFileData(new File(pathname));
		}

		return pageData;
	}

	/**
	 * WikiContentをロードする。
	 * @param contentDir
	 */
	public void loadWikiContent() {
		try {
			WebEngine webEngine = webView.getEngine();
			webEngine.loadContent(wikiEngine_.parse());

			HashMap<String, PageData> map     = wikiEngine_.getPageDataMap();
			boolean                   canEdit = (map.size() != 1) ? true : false;

			choiceViewHyperlinkEdit.setDisable(canEdit);

			menuItemNew.setDisable(false);
			AnchorPaneEdit.setVisible(false);
			AnchorPaneContentList.setVisible(false);
			AnchorPaneWebView.setVisible(true);

			webViewMenuAnchorPaneButtonBack.setDisable(!wikiEngine_.canBack());
			webViewMenuAnchorPaneButtonForward.setDisable(!wikiEngine_.canForward());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * ページ内の指定セクションリンクへジャンプする。
	 * @param target
	 * @throws Exception 
	 */
	private void jumpToSectionLink(EventTarget target) throws Exception {
		HTMLAnchorElement anchor      = (HTMLAnchorElement)target;
		String            href        = anchor.getAttribute("href");
		String            pageTitle   = URLDecoder.decode(href, "UTF-8");
		String            sectionName = pageTitle.substring(pageTitle.indexOf("#") + "#".length());
		webView.getEngine().executeScript("scrollTo('" + sectionName + "')");
	}

	/**
	 * webViewを取得する。
	 * @return webView
	 */
	public WebView getWebView() {
		return webView;
	}
	
	/**
	 * ImageViewLoadingを取得する。
	 * @return ImageViewLoading
	 */
	public ImageView getImageViewLoading() {
		return ImageViewLoading;
	}

	/**
	 * Execute search wiki.
	 * @throws Exception
	 */
	private void doSearchWiki() throws Exception {
		String key = webViewMenuAnchorPaneTextFieldSearchWiki.getText();
		
		if(key.isEmpty() != true) {				
			HashMap<String, PageData> map = wikiEngine_.queryPageFullTextSearch(key);

			wikiEngine_.setPageDataMap(map);
			loadWikiContent();

			webViewHyperlinkMain.setId("webViewMainHyperlinkActive");
			webViewHyperlinkHistory.setId("webViewMainHyperlinkInactive");
			webViewAnchorPaneMain.setVisible(true);
			webViewAnchorPaneHistory.setVisible(false);
		}
	}
	
	/**
	 * 選択中または現在表示中のコンテンツのディレクトリを取得する。
	 * @return 選択中または表示中のコンテンツのディレクトリ
	 * @throws Exception 
	 */
	private PageData getSelectingPageData() throws Exception {
		PageData pageData = null;
		
		if(AnchorPaneContentList.isVisible() == true) {
			TableViewSelectionModel<PageData> selectedData = tableViewContentList.selectionModelProperty().getValue();
			pageData = selectedData.getSelectedItem();
		}
		else if(AnchorPaneEdit.isVisible() == true) {
			pageData = (PageData)AnchorPaneEdit.getUserData();
		}
		
		return pageData;
	}

	/**
	 * Build a namespace comboBox.
	 * @throws Exception
	 */
	private void buildComboBoxNamespace() throws Exception {
		ArrayList<String>      namespaceList = wikiEngine_.getNamespaceList();
		ObservableList<String> items         = comboBoxNamespace.getItems();

		items.clear();
		for(int index = 0; index < namespaceList.size(); index++) {
			String item = namespaceList.get(index);
			items.add(item);
			
			if(item.equals(WikiRepository.PROPERTY_MAIN) == true) {
				comboBoxNamespace.getSelectionModel().select(index);
			}
		}
	}

	/**
	 * Build page list view
	 * @throws Exception
	 */
	private void buildTableViewPageList() throws Exception {
		String                    namespace      = comboBoxNamespace.getSelectionModel().getSelectedItem();
		HashMap<String, PageData> pageMap        = wikiEngine_.queryPageNamespace(namespace);
		ObservableList<PageData>  observableList = FXCollections.observableArrayList();
		
		tableViewContentList.getItems().clear();

		for(String key: pageMap.keySet()) {
			observableList.add(pageMap.get(key));
		}
		tableViewContentList.setItems(observableList);
	}

	/**
	 * A要素のクリックリスナーを登録する。
	 * @param webEngine
	 */
	private void setLinkClickListner(WebEngine webEngine) {
		webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
			@Override
			public void changed(ObservableValue<? extends State> arg0, State oldState, State newState) {
				WebView        webView  = getWebView();
				EventListener  listener =  new EventListener() {						
					@Override
					public void handleEvent(org.w3c.dom.events.Event event) {
						try {
							MouseEvent        mouseEvent  = (MouseEvent)event;
							EventTarget       target      = mouseEvent.getCurrentTarget();

							if(isExternalLink(target) == true) {
								/*
								 * WebEngineのデフォルトの動作(リンク先ページをロード)をキャンセル
								 */
								event.preventDefault();	
								externalLinkHandle(target);
							}
							else {
								internalLinkHandle(target);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}

					/**
					 * targetが外部リンクか判定する。
					 * @param target
					 * @return
					 */
					private boolean isExternalLink(EventTarget target) {
						final ArrayList<String> schemeList = new ArrayList<String>() {
							private static final long serialVersionUID = 1L;
							{ add("http://"); }
							{ add("https://"); }
							{ add("file://"); }
						};

						HTMLAnchorElement aElem      = (HTMLAnchorElement)target;
						String            href       = aElem.getAttribute("href");
						boolean           isExternal = false;

						for(String scheme: schemeList) {
							if(href.startsWith(scheme) == true) {
								isExternal = true;
								break;
							}
						}

						return isExternal;
					}
					
					/**
					 * ExternalLinkを処理する。
					 * @param target
					 * @throws Exception
					 */
					private void externalLinkHandle(EventTarget target) throws Exception {
						String targetpath   = target.toString();
						if(targetpath.startsWith("file:///") == true) {
							targetpath = URLDecoder.decode(targetpath, "UTF-8");
						}
						/*
						 * 既定のブラウザを起動してリンク先ページを開く
						 */
						Desktop.getDesktop().browse(new URI(targetpath));
					}

					/**
					 * InternalLinkを処理する。
					 * @param target
					 * @throws Exception
					 */
					private void internalLinkHandle(EventTarget target) throws Exception {
						HashMap<String, PageData> pageDataMap;
						HTMLAnchorElement         anchor    = (HTMLAnchorElement)target;
						String                    href      = anchor.getAttribute("href");
						String                    pageTitle = URLDecoder.decode(href, "UTF-8");
						String                    uuid      = anchor.getAttribute(WikiEngine.ATTRIBUTE_JCR_UUID);

						if(uuid != null) {
							pageDataMap = wikiEngine_.queryPageUUID(uuid);
						}
						else {
							pageDataMap = wikiEngine_.queryPageTitle(pageTitle);								
						}
						if(pageDataMap.size() == 1) {
							wikiEngine_.setPageDataMap(pageDataMap);
							loadWikiContent();
						}
						else {
							if(href.contains("#")) {
								/*
								 * ページ内セクションリンク
								 */
								jumpToSectionLink(target);
							}
							else {
								buildEditView(href);
							}
						}

					}
            	};

				if(newState == State.SUCCEEDED) {
					Document  doc   = webView.getEngine().getDocument();
		            NodeList  listA = doc.getElementsByTagName("a");
		            
		            for(int index = 0; index < listA.getLength(); index++) {
		            	((EventTarget)listA.item(index)).addEventListener("click", listener, false);
		            }
					getImageViewLoading().setVisible(false);
					webView.setVisible(true);
				}
			}
		});
	}

	/**
	 * Bread crumbs label creator.
	 * @param key
	 * @return Label
	 */
	private Label createBreadcrumbsLabel(String key) {
		Label labelCrumbsList = new Label(resourceBundle_.getString(key));
		
		labelCrumbsList.setId("webViewBreadcrumbs");
		labelCrumbsList.setPrefHeight(40);

		return labelCrumbsList;
	}

	/**
	 * Bread crumbs hyperlink creator.
	 * @param key
	 * @param eventHandler
	 * @param layoutX
	 * @return Label
	 */
	private Hyperlink createBreadcrumbsHyperlink(String key, EventHandler<ActionEvent> eventHandler) {
		Hyperlink linkCrumbsList = new Hyperlink(resourceBundle_.getString(key));
		
		linkCrumbsList.setId("webViewBreadcrumbs");
		linkCrumbsList.setPrefHeight(40);
		linkCrumbsList.setOnAction(eventHandler);
		
		return linkCrumbsList;
	}
}
