/*
 * Copyright 2006 Mask@ Project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.maskat.ide.view;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.xmlbeans.XmlException;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorPart;
import org.maskat.framework.eventdef.Param;
import org.maskat.framework.eventdef.Result;
import org.maskat.framework.eventdef.Source;
import org.maskat.framework.eventdef.Target;
import org.maskat.ide.MaskatIDEUtil;
import org.maskat.ide.editors.MaskatEditor;
import org.maskat.ide.property.SourceProperty;
import org.maskat.ide.property.TargetProperty;
import org.maskat.ide.view.edit.AbstractNavigateFiller;
import org.maskat.ide.view.edit.BindNavigateFiller;
import org.maskat.ide.view.edit.DefaultNavigateFiller;
import org.maskat.ide.view.edit.ResultNavigateFiller;
import org.maskat.ide.view.edit.TableKeyListener;
import org.maskat.xml.xmlbeans.EventSchemaUtil;

public class MaskatEditViewContent {

	private Group resultGroup, bindGroup, paramGroup, buttonGroup;

	private Table resultTable, bindTable, paramTable;

	private AbstractNavigateFiller resultFiller, bindFiller, paramFiller,
			resultNodeNameFiller, paramNodeNameFiller;

	private Button okBtn, cancelBtn, maruBtn, batuBtn, openSchemaFileBtn;

	private org.eclipse.swt.widgets.List nodeNameList;

	private static final int BLANK_STATE = 0, PARAM_STATE = 1, RESULT_STATE = 1 << 1,
			PARAM_BIND_STATE = 1 << 2 | PARAM_STATE, RESULT_BIND_STATE = 1 << 2
					| RESULT_STATE;

	private Param param;

	private Result result;

	private String eventType;

	private int currentState = BLANK_STATE;

	private CommandStack cs;

	public boolean isBlankState() {
		return currentState == BLANK_STATE;
	}

	/**
	 * stateにより、画面項目のdisable,enable,表示内容クリアとかの切替
	 * 
	 * @param newState
	 */
	private void uiSwitchState(int newState) {
		// TODO table中身のクリア
		if (newState == BLANK_STATE) {
			this.okBtn.setEnabled(false);
			this.cancelBtn.setEnabled(false);
			this.maruBtn.setEnabled(false);
			this.batuBtn.setEnabled(false);
		}
		if (newState == PARAM_STATE || newState == RESULT_STATE) {
			if ((newState == PARAM_STATE)) {
				paramTable.setEnabled(true);
			} else {
				resultTable.setEnabled(true);
			}
			this.okBtn.setEnabled(true);
			this.cancelBtn.setEnabled(true);
			this.maruBtn.setEnabled(false);
			this.batuBtn.setEnabled(false);
		}
		if (newState == PARAM_BIND_STATE || newState == RESULT_BIND_STATE) {
			if (newState == PARAM_BIND_STATE) {
				paramTable.setEnabled(false);
			} else {
				resultTable.setEnabled(false);
			}
			this.okBtn.setEnabled(false);
			this.cancelBtn.setEnabled(false);
			this.maruBtn.setEnabled(true);
			this.batuBtn.setEnabled(true);
		}
		currentState = newState;
	}

	private void uiSwitchOffBindState() {
		if (currentState == PARAM_BIND_STATE) {
			uiSwitchState(PARAM_STATE);
		}
		if (currentState == RESULT_BIND_STATE) {
			uiSwitchState(RESULT_STATE);
		}
	}

	private void disposeAll() {
		dispose(paramTable);
		paramTable = null;
		dispose(resultTable);
		resultTable = null;
		dispose(bindTable);
		bindTable = null;
		dispose(paramGroup);
		paramGroup = null;
		dispose(resultGroup);
		resultGroup = null;
		dispose(bindGroup);
		bindGroup = null;
		dispose(buttonGroup);
		buttonGroup = null;
	}

	private void dispose(Composite comp) {
		if (comp != null) {
			comp.dispose();
		}
	}

	public void showParam(Composite parent, Param param, CommandStack cs) {
		disposeAll();
		GridLayout gridLayout = new GridLayout(4, false);
		parent.setLayout(gridLayout);
		createParamGroup(parent);
		createParamTable(paramGroup);

		createBindGroup(parent);
		createBindTable(bindGroup);

		if (param != null) {
			for (Iterator it = param.getSourcesIt(); it != null && it.hasNext();) {
				Source source = (Source) it.next();
				TableItem item = new TableItem(paramTable, SWT.NONE);
				if (source.getObj() != null)
					item.setText(0, source.getObj());
				if (source.getNode() != null)
					item.setText(1, source.getNode());
				if (source.getChildNode() != null)
					item.setText(2, source.getChildNode());
				if (source.getIdxRef() != null)
					item.setText(3, source.getIdxRef());

				item.setData(source.getAllChildren());
			}
			this.param = param;
			this.eventType = param.getEventType();
		}
		this.paramFiller.resetIndex();

		createButtonGroup(parent);
		uiSwitchState(PARAM_STATE);
		this.cs = cs;
	}

	public void showResult(Composite parent, Result result, CommandStack cs) {
		disposeAll();
		GridLayout gridLayout = new GridLayout(4, false);
		parent.setLayout(gridLayout);
		createResultGroup(parent);
		createResultTable(resultGroup);

		createBindGroup(parent);
		createBindTable(bindGroup);

		// resultをtargetTableに入れる
		if (result != null) {
			for (Iterator it = result.getTargetsIt(); it != null && it.hasNext();) {
				Target target = (Target) it.next();
				TableItem item = new TableItem(resultTable, SWT.NONE);
				if (target.getOut() != null)
					item.setText(0, target.getOut());
				if (target.getIn() != null)
					item.setText(1, target.getIn());
				if (target.getInkey() != null)
					item.setText(2, target.getInkey());
				if (target.getType() != null)
					item.setText(3, target.getType());

				item.setData(target.getBinds());
			}
			this.result = result;
			this.eventType = result.getEventType();
		}
		this.resultFiller.resetIndex();

		createButtonGroup(parent);
		uiSwitchState(RESULT_STATE);
		this.cs = cs;
	}

	public void createPartControl(Composite parent) {
		GridLayout gridLayout = new GridLayout(3, false);
		parent.setLayout(gridLayout);

		// createTargetGroup(parent);
		// createTargetTable(resultGroup);
		//
		// createBindGroup(parent);
		// createBindTable(bindGroup);

		// new Text(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL
		// | SWT.FULL_SELECTION);
		// text.setLayoutData(new GridData(GridData.CENTER));

		// Button button = new Button(parent, SWT.PUSH);
		//
		// button.setText("Open Modeless Dialog");
		// button.addSelectionListener(new SelectionAdapter() {
		// public void widgetSelected(SelectionEvent e) {
		//
		// };
		// });
	}

	private void createParamGroup(Composite parent) {
		paramGroup = new Group(parent, SWT.NONE);

		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		paramGroup.setLayout(layout);
		paramGroup.setText("param");
		GridData data = new GridData(GridData.FILL_BOTH);
		paramGroup.setLayoutData(data);
	}

	private void createBindGroup(Composite parent) {
		bindGroup = new Group(parent, SWT.NONE);

		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		bindGroup.setLayout(layout);
		bindGroup.setText("bind");
		GridData data = new GridData(GridData.FILL_BOTH);
		bindGroup.setLayoutData(data);

	}

	private void createResultGroup(Composite parent) {
		resultGroup = new Group(parent, SWT.NONE);

		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		resultGroup.setLayout(layout);
		resultGroup.setText("target");
		GridData data = new GridData(GridData.FILL_BOTH);
		resultGroup.setLayoutData(data);
	}

	private void createButtonGroup(Composite parent) {
		buttonGroup = new Group(parent, SWT.NONE);

		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		buttonGroup.setLayout(layout);
		GridData data = new GridData(GridData.FILL_BOTH);
		buttonGroup.setLayoutData(data);

		okBtn = new Button(buttonGroup, SWT.PUSH);
		okBtn.setText("OK");
		okBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				IEditorPart ep = MaskatIDEUtil.getActiveEditor();
				if (ep instanceof MaskatEditor) {
					if (currentState == PARAM_STATE) {
						syncDataToParam();
					}
					if (currentState == RESULT_STATE) {
						syncDataToResult();
					}
					// ((MaskatEditor) ep).changed();
					// エディターへの値反映はCommandでの実装により、changedはいらない
					((MaskatEditor) ep).editMode = MaskatEditor.SCREEN_EDIT_MODE;
					// Property Viewにお知らせ。。。
					MaskatIDEUtil.getMaskPropertySheet().refresh();
				}
				uiSwitchState(BLANK_STATE);
			};
		});

		cancelBtn = new Button(buttonGroup, SWT.PUSH);
		cancelBtn.setText("Cancel");
		cancelBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				IEditorPart ep = MaskatIDEUtil.getActiveEditor();
				if (ep instanceof MaskatEditor)
					((MaskatEditor) ep).editMode = MaskatEditor.SCREEN_EDIT_MODE;
				uiSwitchState(BLANK_STATE);
			};
		});

		maruBtn = new Button(buttonGroup, SWT.PUSH);
		maruBtn.setText("○");
		maruBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				uiSwitchOffBindState();
			};
		});

		batuBtn = new Button(buttonGroup, SWT.PUSH);
		batuBtn.setText("×");
		batuBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				uiSwitchOffBindState();
			};
		});

		nodeNameList = new org.eclipse.swt.widgets.List(buttonGroup, SWT.MULTI
				| SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
		GridData gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.horizontalSpan = 2;
		gridData.grabExcessHorizontalSpace = true;
		gridData.grabExcessVerticalSpace = true;
		nodeNameList.setLayoutData(gridData);
		nodeNameList.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent e) {
				String value = nodeNameList.getItem(nodeNameList.getSelectionIndex());
				fillNodeName(value);
			}
		});
		nodeNameList.addKeyListener(new KeyAdapter() {

			public void keyReleased(KeyEvent e) {
				if (e.keyCode == SWT.KEYPAD_CR || e.keyCode == '\r') {
					String[] values = nodeNameList.getSelection();
					for (int i = 0; i < values.length; i++) {
						fillNodeName(values[i]);
					}
				}

			}
		});

		openSchemaFileBtn = new Button(buttonGroup, SWT.PUSH);
		openSchemaFileBtn.setText("Open Schema");
		final FileDialog dialog = new FileDialog(buttonGroup.getShell(), SWT.SINGLE);
		openSchemaFileBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				String fileName = dialog.open();
				try {
					if (fileName == null) {
						return;
					}
					List nodeNames = EventSchemaUtil.scanAllNodeName(new FileInputStream(
							new File(fileName)));
					nodeNameList.removeAll();
					nodeNameList.setItems((String[]) nodeNames
							.toArray(new String[nodeNames.size()]));
					buttonGroup.layout();
				} catch (XmlException e1) {
					MessageDialog.openError(buttonGroup.getShell(), "XML解析失敗！",
							"正確スキーマファイルであることを確認してください。\nエラー：" + e1.getMessage());
				} catch (IOException e1) {
					MessageDialog.openError(buttonGroup.getShell(), "スキーマファイル読み込み失敗！",
							"エラー：" + e1.getMessage());
				}

			};
		});
		gridData = new GridData();
		gridData.horizontalAlignment = GridData.FILL;
		gridData.horizontalSpan = 2;
		openSchemaFileBtn.setLayoutData(gridData);
	}

	/**
	 * ノードネームを適当なテーブルに差し入れる
	 * 
	 * @param value
	 */
	private void fillNodeName(String value) {
		if (currentState == PARAM_STATE) {
			paramNodeNameFiller.acceptString(value);
		}
		if (currentState == RESULT_STATE) {
			resultNodeNameFiller.acceptString(value);
		}
		if (currentState == PARAM_BIND_STATE || currentState == RESULT_BIND_STATE) {
			bindFiller.acceptString(value);
		}
	}

	private void createParamTable(Composite parent) {
		paramTable = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER
				| SWT.FULL_SELECTION);
		GridData gd = new GridData(GridData.FILL_BOTH);
		paramTable.setLayoutData(gd);

		TableColumn tableColumn = new TableColumn(paramTable, SWT.NONE);
		tableColumn.setText("obj");
		tableColumn.setWidth(90);
		tableColumn = new TableColumn(paramTable, SWT.NONE);
		tableColumn.setText("node");
		tableColumn.setWidth(90);
		tableColumn = new TableColumn(paramTable, SWT.NONE);
		tableColumn.setText("childNode");
		tableColumn.setWidth(80);
		tableColumn = new TableColumn(paramTable, SWT.NONE);
		tableColumn.setText("idxRef");
		tableColumn.setWidth(60);

		paramTable.setHeaderVisible(true);
		paramFiller = new DefaultNavigateFiller(paramTable);
		paramNodeNameFiller = new DefaultNavigateFiller(paramTable);
		((DefaultNavigateFiller) paramNodeNameFiller).targetColumn = 1;

		TableKeyListener.bindTableKeyListener(paramTable);

		paramTable.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent e) {
				uiSwitchState(PARAM_BIND_STATE);
			}
		});
	}

	private void createResultTable(Composite parent) {
		resultTable = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER
				| SWT.FULL_SELECTION);
		GridData gd = new GridData(GridData.FILL_BOTH);
		resultTable.setLayoutData(gd);

		TableColumn tableColumn = new TableColumn(resultTable, SWT.NONE);
		tableColumn.setText("out");
		tableColumn.setWidth(90);
		tableColumn = new TableColumn(resultTable, SWT.NONE);
		tableColumn.setText("in");
		tableColumn.setWidth(90);
		tableColumn = new TableColumn(resultTable, SWT.NONE);
		tableColumn.setText("inkey");
		tableColumn.setWidth(80);
		tableColumn = new TableColumn(resultTable, SWT.NONE);
		tableColumn.setText("type");
		tableColumn.setWidth(60);

		resultTable.setHeaderVisible(true);
		resultFiller = new ResultNavigateFiller(resultTable);
		resultNodeNameFiller = new DefaultNavigateFiller(resultTable);
		((DefaultNavigateFiller) resultNodeNameFiller).targetColumn = 1;

		TableKeyListener.bindTableKeyListener(resultTable);

		resultTable.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent e) {
				uiSwitchState(RESULT_BIND_STATE);
			}
		});
	}

	private void createBindTable(Composite parent) {
		bindTable = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER
				| SWT.FULL_SELECTION);
		GridData gd = new GridData(GridData.FILL_BOTH);
		bindTable.setLayoutData(gd);

		TableColumn tableColumn = new TableColumn(bindTable, SWT.NONE);
		tableColumn.setText("fromkey");
		tableColumn.setWidth(60);
		tableColumn = new TableColumn(bindTable, SWT.NONE);
		tableColumn.setText("tokey");
		tableColumn.setWidth(60);
		tableColumn = new TableColumn(bindTable, SWT.NONE);
		tableColumn.setText("node");
		tableColumn.setWidth(60);

		bindTable.setHeaderVisible(true);
		TableKeyListener.bindTableKeyListener(bindTable);

		bindFiller = new BindNavigateFiller(bindTable);
	}

	void packColumns(Table table) {
		int columnCount = table.getColumnCount();
		for (int i = 0; i < columnCount; i++) {
			TableColumn tableColumn = table.getColumn(i);
			tableColumn.pack();
		}
	}

	private void createTableEditor() {
		// editing the second column
		final int EDITABLECOLUMN = 1;
		final TableEditor editor = new TableEditor(resultTable);
		resultTable.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				// Clean up any previous editor control
				Control oldEditor = editor.getEditor();
				if (oldEditor != null)
					oldEditor.dispose();

				// Identify the selected rowPoint
				Point pt = new Point(e.x, e.y);

				TableItem item = resultTable.getItem(pt);
				if (item == null)
					return;

				// The control that will be the editor must be a child of the
				// Table
				Text newEditor = new Text(resultTable, SWT.SINGLE);
				newEditor.setText(item.getText(EDITABLECOLUMN));
				// newEditor.addModifyListener(new ModifyListener() {
				// public void modifyText(ModifyEvent e) {
				// Text text = (Text) editor.getEditor();
				// editor.getItem()
				// .setText(EDITABLECOLUMN, text.getText());
				// }
				// });
				newEditor.selectAll();
				editor.grabHorizontal = true;
				editor.setEditor(newEditor, item, EDITABLECOLUMN);
				newEditor.setFocus();
			}
		});

	}

	private String noEmptyString(String s) {
		if (s == null) {
			return null;
		}
		if ("".equals(s.trim())) {
			return null;
		}
		return s;
	}

	private void syncDataToParam() {
		if (param == null)
			return;
		if (paramTable != null) {
			List children = new ArrayList();
			for (int i = 0; i < paramTable.getItemCount(); i++) {
				Source source = new SourceProperty();
				TableItem item = paramTable.getItem(i);
				source.setObj(noEmptyString(item.getText(0)));
				source.setNode(noEmptyString(item.getText(1)));
				source.setChildNode(noEmptyString(item.getText(2)));
				source.setIdxRef(noEmptyString(item.getText(3)));
				// source.setBinds((List) item.getData());
				children.add(source);
				// XXX paramのほかの子要素はどうしますか？
			}
			cs.execute(new SetChildrenCommand(param, children));
		}
	}

	private void syncDataToResult() {
		if (result == null)
			return;
		if (resultTable != null) {
			List children = new ArrayList();
			for (int i = 0; i < resultTable.getItemCount(); i++) {
				TargetProperty target = new TargetProperty();
				TableItem item = resultTable.getItem(i);
				target.setOut(noEmptyString(item.getText(0)));
				target.setIn(noEmptyString(item.getText(1)));
				target.setInkey(noEmptyString(item.getText(2)));
				target.setType(noEmptyString(item.getText(3)));
				// target.setBinds((List) item.getData());
				children.add(target);
			}
			cs.execute(new SetChildrenCommand(result, children));
		}
	}

	/**
	 * 
	 * @param componentName
	 */
	public void acceptData(String componentName) {
		if (currentState == BLANK_STATE) {
			return;
		}
		if (currentState == PARAM_STATE) {
			this.paramFiller.acceptString(componentName);
		}
		if (currentState == RESULT_STATE) {
			this.resultFiller.acceptString(componentName);
		}
	}

}
