package org.objectstyle.wolips.componenteditor.inspector;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.objectstyle.wolips.baseforplugins.util.ComparisonUtils;
import org.objectstyle.wolips.bindings.wod.BindingValueKey;
import org.objectstyle.wolips.bindings.wod.BindingValueKeyPath;
import org.objectstyle.wolips.wodclipse.core.completion.WodParserCache;

public class WOBrowser extends ScrolledComposite implements ISelectionChangedListener, ISelectionProvider, KeyListener {
	private Composite _browserComposite;

	private List<WOBrowserColumn> _columns;

	private List<ISelectionChangedListener> _listeners = new LinkedList<ISelectionChangedListener>();

	private IWOBrowserDelegate _browserDelegate;

	private StringBuffer _keypathBuffer;

	private long _lastKeyTime;

	public WOBrowser(Composite parent, int style) {
		super(parent, SWT.H_SCROLL | style);
		_keypathBuffer = new StringBuffer();
		_columns = new LinkedList<WOBrowserColumn>();

		_browserComposite = new Composite(this, SWT.NONE);
		_browserComposite.setBackground(parent.getBackground());
		// _browser.setLayoutData(new GridData(GridData.FILL_BOTH));

		setContent(_browserComposite);
		setExpandVertical(true);

		GridLayout browserLayout = new GridLayout(1, false);
		browserLayout.horizontalSpacing = 0;
		browserLayout.marginWidth = 5;
		browserLayout.marginHeight = 5;
		browserLayout.horizontalSpacing = 5;
		_browserComposite.setLayout(browserLayout);
	}

	public void setBrowserDelegate(IWOBrowserDelegate browserDelegate) {
		_browserDelegate = browserDelegate;
		for (WOBrowserColumn column : _columns) {
			column.setDelegate(browserDelegate);
		}
	}

	public IWOBrowserDelegate getBrowserDelegate() {
		return _browserDelegate;
	}

	public WOBrowserColumn setRootType(IType type) throws JavaModelException {
		disposeToColumn(-1);
		return addType(type);
	}

	public WOBrowserColumn addType(IType type) throws JavaModelException {
		WOBrowserColumn newColumn = null;
		if (type != null) {
			newColumn = new WOBrowserColumn(this, type, _browserComposite, SWT.NONE);
			newColumn.getViewer().getTable().addKeyListener(this);
			newColumn.setDelegate(_browserDelegate);
			newColumn.addSelectionChangedListener(this);
			GridData columnLayoutData = new GridData(GridData.FILL_BOTH);
			newColumn.setLayoutData(columnLayoutData);
			_columns.add(newColumn);
			if (_browserDelegate != null) {
				_browserDelegate.browserColumnAdded(newColumn);
			}
			((GridLayout) _browserComposite.getLayout()).numColumns = _columns.size();

			_browserComposite.pack();

			for (WOBrowserColumn column : _columns) {
				Object selectedElement = ((IStructuredSelection) column.getSelection()).getFirstElement();
				if (selectedElement != null) {
					column.getViewer().reveal(selectedElement);
				}
			}

			getHorizontalBar().setSelection(getHorizontalBar().getMaximum());
			layout();
		}
		return newColumn;
	}

	public void disposeToColumn(WOBrowserColumn column) {
		int columnIndex = _columns.indexOf(column);
		if (columnIndex != -1) {
			disposeToColumn(columnIndex);
		}
	}

	public void disposeToColumn(int columnIndex) {
		for (int columnNum = _columns.size() - 1; columnNum > columnIndex; columnNum--) {
			WOBrowserColumn column = _columns.get(columnNum);
			if (_browserDelegate != null) {
				_browserDelegate.browserColumnRemoved(column);
			}
			column.dispose();
			_columns.remove(columnNum);
		}

		_browserComposite.pack();
	}

	public WOBrowserColumn selectKeyInColumn(BindingValueKey selectedKey, WOBrowserColumn column) {
		WOBrowserColumn addedColumn = null;

		disposeToColumn(column);

		if (selectedKey == null) {
			// System.out.println("WOBrowserPage.selectionChanged: none");
		} else {
			try {
				if (!selectedKey.isLeaf()) {
					IType nextType = selectedKey.getNextType();
					if (nextType != null) {
						addedColumn = addType(nextType);
					}
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}
		}

		return addedColumn;
	}

	public void selectionChanged(SelectionChangedEvent event) {
		WOBrowserColumn selectedColumn = (WOBrowserColumn) event.getSource();

		IStructuredSelection selection = (IStructuredSelection) event.getSelection();
		Object selectedObject = selection.getFirstElement();
		if (selectedObject instanceof BindingValueKey) {
			BindingValueKey selectedKey = (BindingValueKey) selectedObject;
			selectKeyInColumn(selectedKey, selectedColumn);
		}
		else {
			selectKeyInColumn(null, selectedColumn);
		}

		SelectionChangedEvent wrappedEvent = new SelectionChangedEvent(this, getSelection());
		for (Iterator listeners = _listeners.iterator(); listeners.hasNext();) {
			ISelectionChangedListener listener = (ISelectionChangedListener) listeners.next();
			listener.selectionChanged(wrappedEvent);
		}
	}

	public WOBrowserColumn getFocusedColumn() {
		WOBrowserColumn focusedColumn = null;
		for (WOBrowserColumn column : _columns) {
			if (column.getViewer().getTable().isFocusControl()) {
				focusedColumn = column;
			}
		}
		return focusedColumn;
	}

	public WOBrowserColumn getSelectedColumn() {
		WOBrowserColumn selectedColumn = null;
		for (WOBrowserColumn column : _columns) {
			if (column.getSelectedKey() != null) {
				selectedColumn = column;
			}
		}
		return selectedColumn;
	}

	public String getSelectedKeyPath() {
		return getSelectedKeyPath(null);
	}

	public String getSelectedKeyPath(WOBrowserColumn throughColumn) {
		StringBuffer keyPath = new StringBuffer();
		for (WOBrowserColumn column : _columns) {
			BindingValueKey key = column.getSelectedKey();
			if (key != null) {
				if (keyPath.length() > 0) {
					keyPath.append('.');
				}
				keyPath.append(key.getBindingName());
				if (throughColumn == column) {
					break;
				}
			}
		}
		return keyPath.toString();
	}

	public void addSelectionChangedListener(ISelectionChangedListener listener) {
		_listeners.add(listener);
	}

	public ISelection getSelection() {
		return new StructuredSelection(getSelectedKeyPath());
	}

	public void removeSelectionChangedListener(ISelectionChangedListener listener) {
		_listeners.remove(listener);
	}

	public void setSelection(ISelection selection) {
		Object selectedObject = ((IStructuredSelection) selection).getFirstElement();
		if (selectedObject == null || selectedObject instanceof String) {
			String selectedKeyPath = (String) selectedObject;
			if (selectedKeyPath == null) {
				WOBrowserColumn column = _columns.get(0);
				selectKeyInColumn(null, column);
				column.setSelection(new StructuredSelection());
			} else {
				try {
					if (!ComparisonUtils.equals(selectedKeyPath, getSelectedKeyPath())) {
						BindingValueKeyPath bindingValueKeyPath = new BindingValueKeyPath(selectedKeyPath, _columns.get(0).getType(), WodParserCache.getTypeCache());
						disposeToColumn(0);
						if (bindingValueKeyPath != null && bindingValueKeyPath.isValid()) {
							BindingValueKey[] bindingKeys = bindingValueKeyPath.getBindingKeys();
							if (bindingKeys != null) {
								for (BindingValueKey bindingValueKey : bindingKeys) {
									WOBrowserColumn column = _columns.get(_columns.size() - 1);
									column.setSelection(new StructuredSelection(bindingValueKey));
		
									WOBrowserColumn newColumn = selectKeyInColumn(bindingValueKey, column);
									if (newColumn == null) {
										break;
									}
								}
							}
						}
					}
				} catch (JavaModelException e) {
					e.printStackTrace();
				}
			}
		}
	}

	protected String getPreviousSelectedKeyPath() {
		String previousSelectedKeyPath = null;
		String selectedKey = getSelectedKeyPath();
		if (selectedKey.length() > 0) {
			int dotIndex = selectedKey.lastIndexOf('.');
			if (dotIndex != -1) {
				previousSelectedKeyPath = selectedKey.substring(0, dotIndex);
			}
		}
		return previousSelectedKeyPath;
	}

	public void selectPreviousColumn() {
		String previousSelectedKeyPath = getPreviousSelectedKeyPath();
		if (previousSelectedKeyPath != null) {
			setSelection(new StructuredSelection(previousSelectedKeyPath));
			WOBrowserColumn previousColumn = getSelectedColumn();
			if (previousColumn != null) {
				previousColumn.setFocus();
			}
		}
		clearKeyBuffer();
	}

	public void selectNextColumn() {
		WOBrowserColumn column = getSelectedColumn();
		if (column != null) {
			int columnIndex = _columns.indexOf(column);
			if (columnIndex < _columns.size() - 1) {
				WOBrowserColumn nextColumn = _columns.get(_columns.size() - 1);
				Object firstElement = nextColumn.getViewer().getElementAt(0);
				nextColumn.setSelection(new StructuredSelection(firstElement));
				nextColumn.setFocus();
			}
		}
		clearKeyBuffer();
	}

	protected void clearKeyBuffer() {
		_keypathBuffer.setLength(0);
	}

	protected void selectFromKeyBuffer() {
		if (_keypathBuffer.length() > 0) {
			WOBrowserColumn focusedColumn = getFocusedColumn();
			if (focusedColumn != null) {
				BindingValueKey matchingKey = focusedColumn.getBindingValueKeyStartingWith(_keypathBuffer.toString());
				disposeToColumn(focusedColumn);
				if (matchingKey != null) {
					focusedColumn.setSelection(new StructuredSelection(matchingKey));
				}
			}
		}
	}

	protected void deleteFromKeyBuffer() {
		if (_keypathBuffer.length() > 0) {
			_keypathBuffer.setLength(_keypathBuffer.length() - 1);
		}
		selectFromKeyBuffer();
		_lastKeyTime = System.currentTimeMillis();
	}

	protected void appendToKeyBuffer(char ch) {
		long keyTime = System.currentTimeMillis();
		if ((keyTime - _lastKeyTime) > 1000 || _keypathBuffer.length() == 0) {
			_keypathBuffer.setLength(0);
		}
		_keypathBuffer.append(ch);

		selectFromKeyBuffer();

		_lastKeyTime = keyTime;
	}

	public void keyPressed(KeyEvent e) {
		if (e.keyCode == SWT.ARROW_LEFT) {
			selectPreviousColumn();
		} else if (e.keyCode == SWT.ARROW_RIGHT) {
			selectNextColumn();
		} else if (e.keyCode == SWT.ESC) {
			clearKeyBuffer();
		} else if (e.character == '.') {
			selectNextColumn();
		} else if (e.keyCode == SWT.BS || e.keyCode == SWT.DEL) {
			deleteFromKeyBuffer();
		} else if (e.character != 0) {
			appendToKeyBuffer(e.character);
		}
	}

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

}
