//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.authoring.ui.views;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.epf.authoring.ui.AuthoringUIResources;
import org.eclipse.epf.authoring.ui.editors.ColumnDescriptor;
import org.eclipse.epf.library.edit.command.IActionManager;
import org.eclipse.epf.library.edit.command.IResourceAwareCommand;
import org.eclipse.epf.library.edit.process.IBSItemProvider;
import org.eclipse.epf.library.edit.process.command.ActivityDropCommand;
import org.eclipse.epf.library.edit.ui.IActionTypeProvider;
import org.eclipse.jface.util.IOpenEventListener;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;


/**
 * Displays the Process elements in a tree table viewer.
 * 
 * @author Phong Nguyen Le
 * @authos Shilpa Toraskar
 * @since 1.0
 */
public class ProcessTreeViewer extends TreeViewer implements
		IActionTypeProvider {
	private static final String COPY_TXT = AuthoringUIResources.copy_text; //$NON-NLS-1$
	private static final String EXTEND_TXT = AuthoringUIResources.extend_text; //$NON-NLS-1$
	private static final String DEEP_COPY_TXT = AuthoringUIResources.deepCopy_text; //$NON-NLS-1$

	private int actionType = IActionTypeProvider.COPY;

	private Menu actionTypePopup;

	private Point actionTypePopupLocation;

	/**
	 * Creates a new instance.
	 */
	public ProcessTreeViewer(Composite parent, int style) {
		super(parent, style);

		Tree tree = getTree();
		tree.setHeaderVisible(true);
		tree.setLinesVisible(true);

		addTreeListener(new ITreeViewerListener() {

			public void treeCollapsed(TreeExpansionEvent event) {
				setItemProviderExpanded(event.getElement(), false);
			}

			public void treeExpanded(TreeExpansionEvent event) {
				setItemProviderExpanded(event.getElement(), true);
			}

		});
	}

	public void setupColumns(ColumnDescriptor[] columnDescriptors) {
		// Remove all old TreeColumns.
		TreeColumn[] cols = getTree().getColumns();
		int size = cols.length;
		for (int i = 0; i < size; i++) {
			cols[i].dispose();
		}

		String[] colProps = new String[columnDescriptors.length];
		for (int i = 0; i < columnDescriptors.length; i++) {
			colProps[i] = columnDescriptors[i].id;
			TreeColumn column = new TreeColumn(getTree(),
					columnDescriptors[i].alignment);
			column.setText(columnDescriptors[i].label);
			column.setResizable(columnDescriptors[i].resizable);
			column.setWidth(columnDescriptors[i].width);
		}

		setColumnProperties(colProps);

	}

	private void setItemProviderExpanded(Object element, boolean expand) {
		Object adapter = getAdapterFactory().adapt(element,
				ITreeItemContentProvider.class);
		if (adapter instanceof IBSItemProvider) {
			((IBSItemProvider) adapter).setExpanded(Boolean.valueOf(expand));
		}
	}

	public AdapterFactory getAdapterFactory() {
		return getContentProvider() == null ? null
				: ((AdapterFactoryContentProvider) getContentProvider())
						.getAdapterFactory();
	}

	/**
	 * @see org.eclipse.jface.viewers.TableTreeViewer#setExpanded(Item, boolean)
	 */
	protected void setExpanded(Item node, boolean expand) {
		super.setExpanded(node, expand);
		Object adapter = getAdapterFactory().adapt(node.getData(),
				ITreeItemContentProvider.class);
		if (adapter instanceof IBSItemProvider) {
			IBSItemProvider bsItemProvider = ((IBSItemProvider) adapter);
			if (bsItemProvider.isExpanded() == null) {
				bsItemProvider.setExpanded((Boolean.valueOf(expand)));
			}
		}
	}

	/**
	 * @see org.eclipse.jface.viewers.AbstractTreeViewer#createTreeItem(Widget,
	 *      Object, int)
	 */
	protected void createTreeItem(Widget parent, Object element, int index) {
		super.createTreeItem(parent, element, index);
		Object adapter = getAdapterFactory().adapt(element,
				ITreeItemContentProvider.class);
		if (adapter instanceof IBSItemProvider) {
			Boolean expand = ((IBSItemProvider) adapter).isExpanded();
			if (expand != null) {
				setExpandedState(element, expand.booleanValue());
			}
		}
	}

	/**
	 * @see org.eclipse.jface.viewers.TableTreeViewer#doUpdateItem(Item, Object)
	 */
	protected void doUpdateItem(Item item, Object element) {
		super.doUpdateItem(item, element);
		setExpanded(item, element);
	}

	private void setExpanded(Item item, Object element) {
		ITreeItemContentProvider adapter = (ITreeItemContentProvider) getAdapterFactory().adapt(element,
				ITreeItemContentProvider.class);
		if(adapter.hasChildren(element)) {
			if (adapter instanceof IBSItemProvider) {
				Boolean expand = ((IBSItemProvider) adapter).isExpanded();
				if (expand != null) {
					setExpandedState(item, expand.booleanValue());
				}
			}
		}
	}

	/**
	 * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefresh(Widget,
	 *      Object, boolean, boolean)
	 */
	protected void internalRefresh(Widget widget, Object element,
			boolean doStruct, boolean updateLabels) {
		super.internalRefresh(widget, element, doStruct, updateLabels);

		if (doStruct) {
			if (widget instanceof Item) {
				setExpanded((Item) widget, element);
			} else {
				Item[] children = getChildren(widget);
				for (int i = 0; i < children.length; i++) {
					Item child = children[i];
					if (child instanceof Item) {
						Item item = (Item) child;
						setExpanded(item, item.getData());
					}
				}
			}
		}
	}

	private Command command;

	private IActionManager actionManager;

	/**
	 * @see org.eclipse.epf.library.edit.ui.IActionTypeProvider#getActionType()
	 */
//	public int getActionType() {
//		if (actionTypePopup == null) {
//			createActionTypePopup();
//		}
//
//		Display.getCurrent().syncExec(new Runnable() {
//
//			public void run() {
//				actionType = 0;
//				actionTypePopup.setLocation(actionTypePopupLocation);
//				actionTypePopup.setVisible(true);
//			}
//
//		});
//
//		return actionType;
//	}

	private void createActionTypePopup(boolean canExtend) {
		actionTypePopup = new Menu(getControl());
		SelectionListener selectionListener = new SelectionAdapter() {

			public void widgetSelected(SelectionEvent e) {
				MenuItem item = ((MenuItem) e.getSource());
				String text = item.getText();
				if (text == COPY_TXT) {
					ProcessTreeViewer.this.actionType = IActionTypeProvider.COPY;
				} else if (text == EXTEND_TXT) {
					ProcessTreeViewer.this.actionType = IActionTypeProvider.EXTEND;
				} else if (text == DEEP_COPY_TXT) {
					ProcessTreeViewer.this.actionType = DEEP_COPY;
				}

				if (command instanceof ActivityDropCommand) {
					ActivityDropCommand cmd = ((ActivityDropCommand) command);
					cmd.setType(ProcessTreeViewer.this.actionType);
					cmd.setLabel(text);
				}

				if (actionManager != null
						&& command instanceof IResourceAwareCommand) {
					actionManager.execute((IResourceAwareCommand) command);
				} else {
					command.execute();
				}

				actionTypePopup.setVisible(false);
			}

		};
		MenuItem menuItem = new MenuItem(actionTypePopup, SWT.CASCADE);
		menuItem.setText(COPY_TXT);
		menuItem.addSelectionListener(selectionListener);
		if (canExtend)	{
			menuItem = new MenuItem(actionTypePopup, SWT.CASCADE);
			menuItem.setText(EXTEND_TXT);
			menuItem.addSelectionListener(selectionListener);
		}
		menuItem = new MenuItem(actionTypePopup, SWT.CASCADE);
		menuItem.setText(DEEP_COPY_TXT);
		menuItem.addSelectionListener(selectionListener);
	}

	/**
	 * @see org.eclipse.epf.library.edit.ui.IActionTypeProvider#setInputData(Object)
	 */
	public void setInputData(Object object) {
		actionTypePopupLocation = (Point) object;
	}

	/**
	 * @see org.eclipse.epf.library.edit.ui.IActionTypeProvider#execute(Command)
	 */
	public void execute(Command cmd, boolean canExtend) {
//		if (actionTypePopup == null) {
			createActionTypePopup(canExtend);
//		}

		this.command = cmd;
		actionTypePopup.setLocation(actionTypePopupLocation);
		actionTypePopup.setVisible(true);
	}

	/**
	 * @see org.eclipse.jface.viewers.TreeViewer#hookControl(Control)
	 */
	protected void hookControl(Control control) {
		// From ContentViewer.
		control.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent event) {
				handleDispose(event);
			}
		});

		// From StructuredViewer.
		OpenStrategy handler = new OpenStrategy(control);
		handler.addSelectionListener(new SelectionListener() {
			public void widgetSelected(SelectionEvent e) {
				handleSelect(e);
			}

			public void widgetDefaultSelected(SelectionEvent e) {
				handleDoubleSelect(e);
			}
		});
		handler.addPostSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handlePostSelect(e);
			}
		});
		handler.addOpenListener(new IOpenEventListener() {
			public void handleOpen(SelectionEvent e) {
				ProcessTreeViewer.this.handleOpen(e);
			}
		});

		// From AbstractTreeViewer.
		addTreeListener(control, new TreeListener() {
			public void treeExpanded(TreeEvent event) {
				handleTreeExpand(event);
			}

			public void treeCollapsed(TreeEvent event) {
				handleTreeCollapse(event);
			}
		});

		getTree().addListener(SWT.MouseDoubleClick, new Listener() {

			public void handleEvent(Event event) {
				TreeItem[] treeItems = getTree().getSelection();

				// Don't edit if more than one items are selected.
				if (treeItems == null || treeItems.length != 1)
					return;

				// Find the edit column.
				TreeItem treeItem = treeItems[0];
				if (treeItem == null || treeItem.isDisposed()) {
					// Item no longer exists.
					return;
				}
				int columnToEdit;
				int columns = getTree().getColumnCount();
				if (columns == 0) {
					// If no TableColumn, Table acts as if it has a single
					// column which takes the whole width.
					columnToEdit = 0;
				} else {
					columnToEdit = -1;
					for (int i = 0; i < columns; i++) {
						Rectangle bounds = treeItem.getBounds(i);
						if (bounds.contains(event.x, event.y)) {
							columnToEdit = i;
							break;
						}
					}
					if (columnToEdit == -1) {
						return;
					}
				}

				editElement(treeItem.getData(), columnToEdit);
			}

		});
	}

	/**
	 * @see org.eclipse.epf.library.edit.ui.IActionTypeProvider#setActionManager(IActionManager)
	 */
	public void setActionManager(IActionManager actionMgr) {
		actionManager = actionMgr;
	}

}