/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * 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:
 *     Maskat Project - initial API and implementation
 */

package org.maskat.ui.views.properties.tabbed.masterdetails;

import org.apache.commons.beanutils.BeanUtils;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.forms.DetailsPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.MasterDetailsBlock;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;

import org.maskat.core.MaskatElement;
import org.maskat.core.event.Bind;
import org.maskat.core.event.Event;
import org.maskat.core.event.Header;
import org.maskat.core.event.Param;
import org.maskat.core.event.Result;
import org.maskat.core.event.Source;
import org.maskat.core.event.Target;
import org.maskat.ui.ISharedImages;
import org.maskat.ui.MaskatUIPlugin;
import org.maskat.ui.Messages;
import org.maskat.ui.views.properties.tabbed.action.CopyAction;
import org.maskat.ui.views.properties.tabbed.action.DeleteMultiNodesAction;
import org.maskat.ui.views.properties.tabbed.action.PasteAction;
import org.maskat.ui.views.properties.tabbed.beanwrapper.EventTreeNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.HeaderTreeNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.LocalCallbackFunctions;
import org.maskat.ui.views.properties.tabbed.beanwrapper.LocalDataBinding;
import org.maskat.ui.views.properties.tabbed.beanwrapper.ParamTreeNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.ResultTreeNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.SourceBind;
import org.maskat.ui.views.properties.tabbed.beanwrapper.SourceChildNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.SourceNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.TargetBind;
import org.maskat.ui.views.properties.tabbed.beanwrapper.TargetChildNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.TargetNode;
import org.maskat.ui.views.properties.tabbed.beanwrapper.Validation;
import org.maskat.ui.views.properties.tabbed.command.SetEventPropertyCommand;
import org.maskat.ui.views.properties.tabbed.treenodewrapper.EventTreeNodeWrapper;
import org.maskat.ui.views.properties.tabbed.treenodewrapper.ITreeNode;

/**
 * @since 3.2
 * 
 */
public class EventPropertiesMasterDetailsBlock extends MasterDetailsBlock {

	private static final String SECTION_TEXT = Messages
			.getString("eventPropertiesMasterDetails.title"); //$NON-NLS-1$;

	private static final String SECTION_DESCRIPTION = Messages
			.getString("eventPropertiesMasterDetails.description"); //$NON-NLS-1$;
	private static CommandStack commandStack;

	private static ITreeNode root = null;

	private Tree tree;

	private TreeViewer treeViewer;

	private Clipboard clipboard;

	private EventHandlerDetailsPage eventDetailsPage;

	private HeaderDetailsPage headerDetailsPage;

	private LocalCallbackFunctionsDetailsPage callbackFunctionsDetailsPage;

	private LocalDataBindingDetailsPage localDatabindingDetailsPage;

	private ParamDetailsPage paramDetailsPage;

	private SourceDetailsPage sourceDetailsPage;

	private SourceChildNodeDetailsPage sourceChildNodeDetailsPage;

	private SourceBindDetailsPage sourceBindDetailsPage;

	private ResultDetailsPage resultDetailsPage;

	private TargetDetailsPage targetDetailsPage;

	private TargetChildNodeDetailsPage targetChildNodeDetailsPage;

	private TargetBindDetailsPage targetBindDetailsPage;

	private ValidationDetailsPage validationDetailsPage;

	private GraphicalViewer graphicalViewer;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.forms.MasterDetailsBlock#createMasterPart(org.eclipse.ui.forms.IManagedForm,
	 *      org.eclipse.swt.widgets.Composite)
	 */
	protected void createMasterPart(final IManagedForm managedForm,
			Composite parent) {
		FormToolkit toolkit = managedForm.getToolkit();
		Section masterTreeSection = toolkit.createSection(parent,
				ExpandableComposite.TITLE_BAR | Section.DESCRIPTION);
		masterTreeSection.setText(SECTION_TEXT);
		masterTreeSection.setDescription(SECTION_DESCRIPTION);

		Composite treeComposite = toolkit.createComposite(masterTreeSection);

		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 2;
		/*
		 * gridLayout.marginWidth = 2; gridLayout.marginHeight = 2;
		 */

		// client.setLayout(new FillLayout());
		treeComposite.setLayout(gridLayout);
		tree = toolkit.createTree(treeComposite, SWT.MULTI);
		treeViewer = new TreeViewer(tree);
		treeViewer.setAutoExpandLevel(2);
		GridData gridData = new GridData(GridData.FILL_BOTH);
		tree.setLayoutData(gridData);

		clipboard = new Clipboard(parent.getDisplay());
		hookContextMenu(treeViewer);

		treeViewer.setContentProvider(new EventBeansContentProvider());
		treeViewer.setLabelProvider(new EventBeansLabelProvider());

		// Add buttons
		/*
		 * Composite composite = toolkit.createComposite(treeComposite);
		 * gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
		 * composite.setLayoutData(gridData);
		 * 
		 * Button button = toolkit.createButton(composite, "Add...", SWT.PUSH);
		 * gridData = new GridData(GridData.BEGINNING);
		 * button.setLayoutData(gridData);
		 */

		final SectionPart sectionPart = new SectionPart(masterTreeSection);

		managedForm.addPart(sectionPart);

		treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

			public void selectionChanged(
					SelectionChangedEvent selectionChangedEvent) {
				TreeSelection selection = (TreeSelection) selectionChangedEvent
						.getSelection();
				Object obj = selection.getFirstElement();

				if (obj instanceof EventTreeNode) {

					eventDetailsPage.setEvent((Event) ((EventTreeNode) obj)
							.getModel());
					eventDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof HeaderTreeNode) {
					headerDetailsPage.setHeader((Header) ((HeaderTreeNode) obj)
							.getModel());
					headerDetailsPage.setEvent((Event) root.getModel());
					headerDetailsPage.setStaleFlag(true);

				}
				if (obj instanceof LocalDataBinding) {
					localDatabindingDetailsPage
							.setTarget((Target) ((LocalDataBinding) obj)
									.getModel());
					localDatabindingDetailsPage
							.setGraphicalViewer(graphicalViewer);
					localDatabindingDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof ParamTreeNode) {
					paramDetailsPage.setParam((Param) ((ParamTreeNode) obj)
							.getModel());
					paramDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof SourceNode) {
					sourceDetailsPage.setSource((SourceNode) obj);
					sourceDetailsPage.setGraphicalViewer(graphicalViewer);
					sourceDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof Validation) {
					validationDetailsPage.setSource((Source) ((Validation) obj)
							.getModel());
					validationDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof SourceChildNode) {
					sourceChildNodeDetailsPage
							.setSource((Source) ((SourceChildNode) obj)
									.getModel());
				}
				if (obj instanceof SourceBind) {
					sourceBindDetailsPage.setBind((Bind) ((SourceBind) obj)
							.getModel());
				}
				if (obj instanceof ResultTreeNode) {
					resultDetailsPage.setResult((Result) ((ResultTreeNode) obj)
							.getModel());
					resultDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof TargetNode) {
					targetDetailsPage.setTarget((Target) ((TargetNode) obj)
							.getModel());
					targetDetailsPage.setGraphicalViewer(graphicalViewer);
					targetDetailsPage.setStaleFlag(true);
				}
				if (obj instanceof TargetChildNode) {
					targetChildNodeDetailsPage
							.setTarget((Target) ((TargetChildNode) obj)
									.getModel());
				}

				if (obj instanceof TargetBind) {
					targetBindDetailsPage.setBind((Bind) ((TargetBind) obj)
							.getModel());
				}

				managedForm.fireSelectionChanged(sectionPart,
						selectionChangedEvent.getSelection());
			}
		});

		masterTreeSection.setClient(treeComposite);
	}

	public void setInput(Event event) {
		if (root != null && root.getModel() == event) {
			if (treeViewer.getInput() != root) {
				treeViewer.setInput(root);
			} else {
				treeViewer.refresh();
			}
			return;
		}
		root = new EventTreeNodeWrapper(event);

		if (treeViewer.getContentProvider() == null) {
			treeViewer.setContentProvider(new EventBeansContentProvider());
		}
		if (treeViewer.getLabelProvider() == null) {
			treeViewer.setLabelProvider(new EventBeansLabelProvider());
		}
		treeViewer.setInput(root);
		ITreeNode treeNode = EventPropertiesMasterDetailsBlock.getTreeNode(
				event, EventTreeNode.class);
		if (treeNode != null) {
			treeViewer.setSelection(new StructuredSelection(treeNode), true);
		}
	}

	public void refresh(Event event) {
		eventDetailsPage.setEvent(event);
		callbackFunctionsDetailsPage.setEvent(event);
		setInput(event);
	}

	private void hookContextMenu(final TreeViewer viewer) {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {

			public void menuAboutToShow(IMenuManager manager) {
				fillContextMenu(viewer, manager);
			}
		});
		final Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
	}

	private void fillContextMenu(TreeViewer treeviewer, IMenuManager manager) {
		// TODO
		ISelection selection = treeviewer.getSelection();
		IStructuredSelection ssel = (IStructuredSelection) selection;
		if (ssel.size() == 1) {
			Object object = ssel.getFirstElement();
			if (object instanceof ITreeNode) {
				ITreeNode parent = (ITreeNode) object;
				if (parent.getModel() != null) {
					parent.fillContextMenu(treeviewer, manager);
					manager.add(new Separator());
				}
			}
			manager.add(new CopyAction(treeViewer, clipboard));
			manager.add(new PasteAction(treeViewer, clipboard));
		} else if (ssel.size() > 1) {
			manager.add(new CopyAction(treeViewer, clipboard));
			manager.add(new PasteAction(treeViewer, clipboard));
			manager.add(new DeleteMultiNodesAction(treeViewer));
		}
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	protected void createToolBarActions(IManagedForm managedForm) {
		final ScrolledForm form = managedForm.getForm();

		Action haction = new Action("horizontal", IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
		{

			public void run() {
				sashForm.setOrientation(SWT.HORIZONTAL);
				form.reflow(true);
			}
		};

		Action vaction = new Action("vertical", IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
		{

			public void run() {
				sashForm.setOrientation(SWT.VERTICAL);
				form.reflow(true);
			}
		};

		haction.setChecked(true);
		vaction.setChecked(false);

		haction.setImageDescriptor(MaskatUIPlugin
				.getImageDescriptor(ISharedImages.IMG_HORIZONTAL_ACTION));
		vaction.setImageDescriptor(MaskatUIPlugin
				.getImageDescriptor(ISharedImages.IMG_VERTICAL_ACTION));

		form.getToolBarManager().add(haction);
		form.getToolBarManager().add(vaction);
	}

	public TreeViewer getTreeViewer() {
		return treeViewer;
	}

	protected void registerPages(DetailsPart detailsPart) {
		eventDetailsPage = new EventHandlerDetailsPage(
				treeViewer);

		headerDetailsPage = new HeaderDetailsPage(
				treeViewer);

		callbackFunctionsDetailsPage = new LocalCallbackFunctionsDetailsPage(
				treeViewer);

		localDatabindingDetailsPage = new LocalDataBindingDetailsPage(
				treeViewer);

		paramDetailsPage = new ParamDetailsPage(
				treeViewer);

		sourceDetailsPage = new SourceDetailsPage(
				treeViewer);

		validationDetailsPage = new ValidationDetailsPage(treeViewer);

		sourceChildNodeDetailsPage = new SourceChildNodeDetailsPage(
				treeViewer);

		sourceBindDetailsPage = new SourceBindDetailsPage(
				treeViewer);

		resultDetailsPage = new ResultDetailsPage(
				treeViewer);

		targetDetailsPage = new TargetDetailsPage(
				treeViewer);

		targetChildNodeDetailsPage = new TargetChildNodeDetailsPage(
				treeViewer);

		targetBindDetailsPage = new TargetBindDetailsPage(
				treeViewer);

		detailsPart.registerPage(EventTreeNode.class, eventDetailsPage);

		detailsPart.registerPage(HeaderTreeNode.class, headerDetailsPage);

		detailsPart.registerPage(LocalCallbackFunctions.class,
				callbackFunctionsDetailsPage);

		detailsPart.registerPage(LocalDataBinding.class,
				localDatabindingDetailsPage);

		detailsPart.registerPage(ParamTreeNode.class, paramDetailsPage);

		detailsPart.registerPage(SourceNode.class, sourceDetailsPage);

		detailsPart.registerPage(SourceChildNode.class,
				sourceChildNodeDetailsPage);

		detailsPart.registerPage(SourceBind.class, sourceBindDetailsPage);

		detailsPart.registerPage(Validation.class, validationDetailsPage);

		detailsPart.registerPage(ResultTreeNode.class, resultDetailsPage);

		detailsPart.registerPage(TargetNode.class, targetDetailsPage);

		detailsPart.registerPage(TargetChildNode.class,
				targetChildNodeDetailsPage);

		detailsPart.registerPage(TargetBind.class, targetBindDetailsPage);
	}

	/***************************************************************************
	 * get the treeNode by the Bean instance and treeNode class type
	 * 
	 * @param bean
	 * @param treeNodeClass
	 * @return treenode whoes type is treeNodeClass and it holds the bean .
	 */
	public static ITreeNode getTreeNode(MaskatElement bean, Class treeNodeClass) {
		if (root == null) {
			return null;
		}
		return getTreeNode(root, bean, treeNodeClass);
	}

	private static ITreeNode getTreeNode(ITreeNode node, MaskatElement bean,
			Class treeNodeClass) {
		if (node.getClass() == treeNodeClass && node.getModel() == bean) {
			return node;
		}

		for (int i = 0; i < node.getChildren().length; i++) {
			ITreeNode ret = getTreeNode((ITreeNode) node.getChildren()[i],
					bean, treeNodeClass);
			if (ret != null) {
				return ret;
			}
		}
		return null;
	}

	public void setGraphicalViewer(GraphicalViewer graphicalViewer) {
		this.graphicalViewer = graphicalViewer;
	}

	public GraphicalViewer getGraphicalViewer() {
		return graphicalViewer;
	}

	/** set CommandStack */
	public void setCommandStack(CommandStack commandStack) {
		EventPropertiesMasterDetailsBlock.commandStack = commandStack;
	}

	public static void executeCommand(Command command) {
		commandStack.execute(command);
	}

	public static void setEventProperty(TreeViewer treeViewer, Class classType, Object bean,
			String propertyName, Object newValue) {
		try {
			String oldValue = BeanUtils.getProperty(bean, propertyName);
			if (newValue != null && newValue.equals(oldValue)) {
				return;
			}
			// if "" was set to the property, the BeanUtils will returns as null
			if (oldValue == null && "".equals(newValue)) {
				return;
			}
		} catch (Exception e) {
		}
		executeCommand(new SetEventPropertyCommand(treeViewer, classType, bean,
				propertyName, newValue));
	}
}
