package org.objectstyle.wolips.baseforuiplugins.plist;

import java.util.List;
import java.util.Set;

import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.TreeViewerFocusCellManager;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.objectstyle.woenvironment.plist.ParserDataStructureFactory;
import org.objectstyle.woenvironment.plist.PropertyListParserException;
import org.objectstyle.woenvironment.plist.WOLPropertyListSerialization;
import org.objectstyle.wolips.baseforuiplugins.utils.WOTreeCellNavigationStrategy;

public class PropertyListEditor extends Viewer implements IPropertyListChangeListener, KeyListener {
	private IPropertyListChangeListener _listener;

	private ParserDataStructureFactory _parserDataStructureFactory;

	private TreeViewer _propertyListTree;

	public PropertyListEditor(Composite parent, boolean rootVisible, boolean canEditRootType, Set<String> filteredKeyPaths) {
		_propertyListTree = new TreeViewer(parent, SWT.BORDER | SWT.FULL_SELECTION | SWT.BACKGROUND | SWT.SINGLE | SWT.NO_SCROLL | SWT.V_SCROLL);
		_propertyListTree.getTree().setHeaderVisible(true);
		_propertyListTree.getTree().setLinesVisible(true);
		_propertyListTree.setAutoExpandLevel(2);
		TreeViewerFocusCellManager focusCellManager = new TreeViewerFocusCellManager(_propertyListTree, new FocusCellOwnerDrawHighlighter(_propertyListTree), new WOTreeCellNavigationStrategy());
		ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(_propertyListTree) {
			protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
				ViewerCell cell = (ViewerCell) event.getSource();
				boolean isEditorActivationEvent;
				if (cell.getColumnIndex() == 1) {
					isEditorActivationEvent = event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == ' ') || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
					//isEditorActivationEvent = event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == ' ') || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
				} else {
					isEditorActivationEvent = event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == ' ') || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
					//isEditorActivationEvent = event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
				}
				return isEditorActivationEvent;
			}
		};
		TreeViewerEditor.create(_propertyListTree, focusCellManager, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION | ColumnViewerEditor.KEEP_EDITOR_ON_DOUBLE_CLICK);

		TreeViewerColumn keyColumn = new TreeViewerColumn(_propertyListTree, SWT.NONE);
		keyColumn.getColumn().setWidth(120);
		keyColumn.getColumn().setText("Key");
		keyColumn.setLabelProvider(new PropertyListKeyLabelProvider());
		keyColumn.setEditingSupport(new PropertyListKeyEditingSupport(_propertyListTree, this));

		TreeViewerColumn typeColumn = new TreeViewerColumn(_propertyListTree, SWT.NONE);
		typeColumn.getColumn().setWidth(75);
		typeColumn.getColumn().setText("Type");
		typeColumn.setLabelProvider(new PropertyListTypeLabelProvider());
		typeColumn.setEditingSupport(new PropertyListTypeEditingSupport(_propertyListTree, this, canEditRootType));

		TreeViewerColumn valueColumn = new TreeViewerColumn(_propertyListTree, SWT.NONE);
		valueColumn.getColumn().setWidth(250);
		valueColumn.getColumn().setText("Value");
		valueColumn.setLabelProvider(new PropertyListValueLabelProvider());
		valueColumn.setEditingSupport(new PropertyListValueEditingSupport(_propertyListTree, this));

		_propertyListTree.getTree().addKeyListener(this);

		_parserDataStructureFactory = new StableDataStructureFactory();
		_propertyListTree.setContentProvider(new PropertyListContentProvider(_parserDataStructureFactory, rootVisible, filteredKeyPaths));
	}

	public void setListener(IPropertyListChangeListener listener) {
		_listener = listener;
	}

	public IPropertyListChangeListener getListener() {
		return _listener;
	}

	@Override
	public Control getControl() {
		return _propertyListTree.getTree();
	}

	@Override
	public Object getInput() {
		try {
			return WOLPropertyListSerialization.propertyListFromString(WOLPropertyListSerialization.stringFromPropertyList(_propertyListTree.getInput()), new StableDataStructureFactory());
		} catch (PropertyListParserException e) {
			throw new IllegalArgumentException("Failed to parse property list.", e);
		}
	}

	@Override
	public ISelection getSelection() {
		return _propertyListTree.getSelection();
	}

	@Override
	public void refresh() {
		_propertyListTree.refresh();
	}

	@Override
	public void setInput(Object input) {
		try {
			_propertyListTree.setInput(WOLPropertyListSerialization.propertyListFromString(WOLPropertyListSerialization.stringFromPropertyList(input), new StableDataStructureFactory()));
		} catch (PropertyListParserException e) {
			throw new IllegalArgumentException("Failed to parse property list.", e);
		}
	}

	@Override
	public void setSelection(ISelection selection, boolean reveal) {
		_propertyListTree.setSelection(selection);
	}

	public void pathRenamed(String oldPath, String newPath) {
		if (_listener != null) {
			_listener.pathRenamed(oldPath, newPath);
		}
	}

	public void pathChanged(String path, Object oldValue, Object newValue) {
		if (_listener != null) {
			_listener.pathChanged(path, oldValue, newValue);
		}
	}

	public void pathAdded(String path, Object value) {
		if (_listener != null) {
			_listener.pathAdded(path, value);
		}
	}

	public void pathRemoved(String path, Object value) {
		if (_listener != null) {
			_listener.pathRemoved(path, value);
		}
	}

	public void addRow() {
		PropertyListPath parentPath;
		PropertyListPath selectedPath = (PropertyListPath) ((IStructuredSelection) _propertyListTree.getSelection()).getFirstElement();
		if (selectedPath.isCollectionValue()) {
			if (_propertyListTree.getExpandedState(selectedPath) || selectedPath.getParent() == null) {
				parentPath = selectedPath;
			} else {
				parentPath = selectedPath.getParent();
			}
		} else {
			parentPath = selectedPath.getParent();
		}
		PropertyListPath newChild = parentPath.addRow();
		_propertyListTree.refresh(parentPath);
		_propertyListTree.setSelection(new StructuredSelection(newChild), true);
		if (_listener != null) {
			_listener.pathAdded(newChild.getKeyPath(), newChild.getValue());
		}
		_propertyListTree.editElement(newChild, 0);
	}

	public void deleteRow() {
		PropertyListPath selectedPath = (PropertyListPath) ((IStructuredSelection) _propertyListTree.getSelection()).getFirstElement();
		String keyPath = selectedPath.getKeyPath();
		PropertyListPath parentPath = selectedPath.getParent();
		int index = parentPath.getIndexOf(selectedPath);
		if (selectedPath.delete()) {
			_propertyListTree.refresh(parentPath);
			if (_listener != null) {
				_listener.pathRemoved(keyPath, selectedPath.getValue());
			}

			List<PropertyListPath> children = parentPath.getChildren();
			if (children.size() == 0) {
				_propertyListTree.setSelection(new StructuredSelection(parentPath), true);
			} else if (index == children.size()) {
				_propertyListTree.setSelection(new StructuredSelection(parentPath.getChildAtIndex(index - 1)), true);
			} else {
				_propertyListTree.setSelection(new StructuredSelection(parentPath.getChildAtIndex(index)), true);
			}
		}
	}

	public void moveRowUp() {
		PropertyListPath selectedPath = (PropertyListPath) ((IStructuredSelection) _propertyListTree.getSelection()).getFirstElement();
		PropertyListPath parentPath = selectedPath.getParent();
		int index = parentPath.getIndexOf(selectedPath);
		if (selectedPath.moveUp()) {
			_propertyListTree.refresh(parentPath);
			if (_listener != null) {
				_listener.pathChanged(parentPath.getKeyPath(), null, null);
			}
			_propertyListTree.setSelection(new StructuredSelection(parentPath.getChildAtIndex(index - 1)), true);
		}
	}

	public void moveRowDown() {
		PropertyListPath selectedPath = (PropertyListPath) ((IStructuredSelection) _propertyListTree.getSelection()).getFirstElement();
		PropertyListPath parentPath = selectedPath.getParent();
		int index = parentPath.getIndexOf(selectedPath);
		if (selectedPath.moveDown()) {
			_propertyListTree.refresh(parentPath);
			if (_listener != null) {
				_listener.pathChanged(parentPath.getKeyPath(), null, null);
			}
			_propertyListTree.setSelection(new StructuredSelection(parentPath.getChildAtIndex(index + 1)), true);
		}
	}

	public void keyPressed(KeyEvent e) {
		if (!_propertyListTree.isCellEditorActive()) {
			if (e.keyCode == SWT.CR) {
				addRow();
			} else if (e.keyCode == SWT.DEL) {
				deleteRow();
			} else if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.COMMAND) != 0) {
				moveRowUp();
				e.doit = false;
			} else if (e.keyCode == SWT.ARROW_DOWN && (e.stateMask & SWT.COMMAND) != 0) {
				moveRowDown();
				e.doit = false;
			}
		}
	}

	public void keyReleased(KeyEvent e) {
		// IGNORE
	}
}
