/**
 * Copyright (c) 2012 committers of YAKINDU 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:
 * 	committers of YAKINDU - initial API and implementation
 * 
 */
package org.yakindu.sct.ui.editor.partitioning;

import static org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil.openEditor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.internal.ui.viewers.breadcrumb.BreadcrumbViewer;
import org.eclipse.debug.internal.ui.viewers.breadcrumb.IBreadcrumbDropDownSite;
import org.eclipse.debug.internal.ui.viewers.breadcrumb.TreeViewerDropDown;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.yakindu.sct.model.sgraph.CompositeElement;
import org.yakindu.sct.model.sgraph.Region;
import org.yakindu.sct.model.sgraph.State;
import org.yakindu.sct.model.sgraph.Statechart;
import org.yakindu.sct.model.sgraph.Vertex;
import org.yakindu.sct.model.sgraph.provider.SGraphItemProviderAdapterFactory;
import org.yakindu.sct.model.sgraph.provider.StateItemProvider;
import org.yakindu.sct.model.sgraph.provider.StatechartItemProvider;
import org.yakindu.sct.ui.editor.StatechartImages;

/**
 * 
 * @author andreas muelder - Initial contribution and API
 * 
 */
@SuppressWarnings("restriction")
public class DiagramPartitioningBreadcrumbViewer extends BreadcrumbViewer implements ISelectionChangedListener {

	public DiagramPartitioningBreadcrumbViewer(Composite parent, int style) {
		super(parent, style);
	}

	@Override
	protected Control createDropDown(Composite parent, IBreadcrumbDropDownSite site, TreePath path) {
		Control result = createDropDownControl(parent, site, path);
		return result;
	}

	public Control createDropDownControl(Composite parent, final IBreadcrumbDropDownSite site, final TreePath paramPath) {
		TreeViewerDropDown dropDownTreeViewer = new TreeViewerDropDown() {
			protected TreeViewer createTreeViewer(Composite composite, int style, final TreePath path) {
				return createDropDownTreeViewer(composite, paramPath, site);
			}
		};
		return dropDownTreeViewer.createDropDown(parent, site, paramPath);
	}

	protected TreeViewer createDropDownTreeViewer(final Composite composite, TreePath paramPath,
			final IBreadcrumbDropDownSite site) {
		Diagram diagram = (Diagram) paramPath.getParentPath().getLastSegment();
		TreeViewer viewer = null;
		if (diagram != null)
			viewer = createDiagramViewer(composite, diagram);
		else
			viewer = createProjectStatechartViewer(composite, (Diagram) paramPath.getLastSegment());
		viewer.getControl().addPaintListener(new PaintListener() {
			public void paintControl(PaintEvent e) {
				getDropDownShell().pack(true);
			}
		});
		return viewer;
	}

	protected TreeViewer createProjectStatechartViewer(Composite composite, Diagram diagram) {
		TreeViewer viewer = new TreeViewer(composite);
		viewer.setLabelProvider(new StatechartLabelProvider());
		viewer.setContentProvider(new StatechartContentProvider());
		viewer.setInput(getProjectStatechartInput(diagram));
		viewer.addSelectionChangedListener(this);
		return viewer;
	}

	protected Set<IFile> getProjectStatechartInput(Diagram diagram) {
		final IFile file = WorkspaceSynchronizer.getFile(diagram.eResource());
		final IProject project = file.getProject();
		final Set<IFile> result = new HashSet<IFile>();
		try {
			project.accept(new IResourceVisitor() {
				public boolean visit(IResource resource) throws CoreException {
					// TODO check for package explorer filters here
					if (resource.isHidden()) {
						return false;
					}
					if (resource instanceof IFile) {
						if (file.getFileExtension().equals(resource.getFileExtension()))
							result.add((IFile) resource);
					}
					return true;
				}
			});
		} catch (CoreException e) {
			e.printStackTrace();
		}
		return result;
	}

	protected TreeViewer createDiagramViewer(final Composite composite, Diagram diagram) {
		TreeViewer viewer = new TreeViewer(composite);
		viewer.setLabelProvider(new AdapterFactoryLabelProvider(new SGraphItemProviderAdapterFactory()));
		viewer.setContentProvider(new AdapterFactoryContentProvider(new BreadcrumbItemProviderAdapterFactory()));
		EObject element = diagram.getElement();
		viewer.setInput(element);
		viewer.addSelectionChangedListener(this);
		return viewer;
	}

	public void selectionChanged(SelectionChangedEvent event) {
		Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
		if (selection instanceof State || selection instanceof Statechart) {
			Diagram diagram = DiagramPartitioningUtil.getSubDiagram((CompositeElement) selection);
			openEditor(diagram);
		} else if (selection instanceof IFile) {
			openEditor((IFile) selection);
		}
	}

	public static final class StatechartContentProvider implements ITreeContentProvider {

		private Set<IFile> newInput;

		public void dispose() {
		}

		@SuppressWarnings("unchecked")
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			this.newInput = (Set<IFile>) newInput;

		}

		public Object[] getElements(Object inputElement) {
			return newInput.toArray(new Object[] {});
		}

		public Object[] getChildren(Object parentElement) {
			return null;
		}

		public Object getParent(Object element) {
			return null;
		}

		public boolean hasChildren(Object element) {
			return false;
		}

	}

	public static final class StatechartLabelProvider extends BaseLabelProvider implements ILabelProvider {

		public Image getImage(Object element) {
			return StatechartImages.LOGO.image();
		}

		public String getText(Object element) {
			return ((IFile) element).getFullPath().toString();
		}

	}

	public static final class BreadcrumbItemProviderAdapterFactory extends SGraphItemProviderAdapterFactory {

		@Override
		public Adapter createStatechartAdapter() {
			if (statechartItemProvider == null) {
				statechartItemProvider = new StatechartItemProvider(this) {
					@Override
					public Collection<?> getChildren(Object object) {
						return collectSubdiagrams((CompositeElement) object);
					}
				};
			}
			return statechartItemProvider;
		}

		@Override
		public Adapter createStateAdapter() {
			if (stateItemProvider == null) {
				stateItemProvider = new StateItemProvider(this) {
					@Override
					public Collection<?> getChildren(Object object) {
						return collectSubdiagrams((CompositeElement) object);
					}
				};
			}
			return stateItemProvider;
		}

		private Collection<?> collectSubdiagrams(CompositeElement state) {
			List<Vertex> result = new ArrayList<Vertex>();
			EList<Region> regions = state.getRegions();
			for (Region region : regions) {
				EList<Vertex> vertices = region.getVertices();
				for (Vertex vertex : vertices) {
					if (vertex instanceof State
							&& DiagramPartitioningUtil.getSubDiagram((CompositeElement) vertex) != null) {
						result.add(vertex);
					}
				}
			}
			return result;
		}
	}

}