/**
 * File:    GuardEditPart.java
 * Created: 17.09.2006
 *
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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:
 *    Wolf Fischer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.parts.processes;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jwt.meta.model.core.Package;
import org.eclipse.jwt.meta.model.data.Data;
import org.eclipse.jwt.meta.model.data.impl.DataImpl;
import org.eclipse.jwt.meta.model.processes.Guard;
import org.eclipse.jwt.meta.model.processes.GuardSpecification;
import org.eclipse.jwt.meta.model.processes.ProcessesPackage;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.editors.preferences.PreferenceReader;
import org.eclipse.jwt.we.figures.core.NamedElementFigure;
import org.eclipse.jwt.we.figures.processes.GuardFigure;
import org.eclipse.jwt.we.misc.guards.NotaFormulaException;
import org.eclipse.jwt.we.misc.guards.StringtoTreeParser;
import org.eclipse.jwt.we.misc.guards.TreetoStringParser;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.util.GeneralHelper;
import org.eclipse.jwt.we.misc.util.internal.StringParser;
import org.eclipse.jwt.we.misc.views.Views;
import org.eclipse.jwt.we.parts.core.GraphicalModelElementEditPart;
import org.eclipse.jwt.we.parts.processes.directEdit.GuardCellEditorLocator;
import org.eclipse.jwt.we.parts.processes.directEdit.GuardDirectEditManager;
import org.eclipse.jwt.we.parts.processes.policies.GuardDirectEditPolicy;
import org.eclipse.jwt.we.parts.processes.policies.GuardSelectionEditPolicy;
import org.eclipse.swt.graphics.Color;


/**
 * Edit Part for a {@link Guard}
 * 
 * @version $Id: GuardEditPart.java,v 1.16 2009-11-26 12:41:36 chsaad Exp $
 * @author Wolf Fischer, Programming distributed Systems Lab, University of Augsburg,
 *         Germany, www.ds-lab.org
 * @since 0.6.0
 */
public class GuardEditPart
		extends GraphicalModelElementEditPart
{

	/**
	 * A logger.
	 */
	private Logger logger = Logger.getLogger(GuardEditPart.class);

	/**
	 * The direct edit manager.
	 */
	private DirectEditManager directEditManager;


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jwt.we.parts.core.GraphicalModelElementEditPart#getModelClass()
	 */
	@Override
	public Class getModelClass()
	{
		return Guard.class;
	}


	/**
	 * Retrieves this Guard Model,
	 * 
	 * @return The Guard
	 */
	public Guard getGuard()
	{
		return (Guard) getModel();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()
	 */
	@Override
	@SuppressWarnings("unchecked")
	protected List getModelChildren()
	{
		List result = new ArrayList();
		if (getGuard().getDetailedSpecification() != null)
		{
			result.add(getGuard().getDetailedSpecification());
		}
		return result;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jwt.we.parts.core.GraphicalModelElementEditPart#createEditPolicies()
	 */
	@Override
	protected void createEditPolicies()
	{
		super.createEditPolicies();

		// install a selection feedback policy
		installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE,
				new GuardSelectionEditPolicy());

		// install a direct edit policy
		installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new GuardDirectEditPolicy());
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jwt.we.parts.core.GraphicalModelElementEditPart#performRequest(org.
	 * eclipse.gef.Request)
	 */
	@Override
	public void performRequest(Request request)
	{
		super.performRequest(request);

		if (request.getType() == RequestConstants.REQ_DIRECT_EDIT)
		{
			performDirectEdit();
		}
	}


	/**
	 * Performs a directEdit request.
	 */
	protected void performDirectEdit()
	{
		if (directEditManager == null)
		{
			directEditManager = new GuardDirectEditManager(this,
					new GuardCellEditorLocator((GuardFigure) getFigure()));
		}

		directEditManager.show();
	}


	/**
	 * Sets the text of the Shortdescription tag, if the property of one of the
	 * GuardSpecification children changes.
	 */
	private void refreshShortDesc()
	{
		try
		{
			GuardSpecification gs = this.getGuard().getDetailedSpecification();
			if (gs == null)
				return;
			String newformula = TreetoStringParser.getString(gs);
			this.getGuard().setShortdescription(newformula);
		}
		catch (Exception ex)
		{
			logger.debug(ex);
		}
	}


	/**
	 * If the ShortDescription property is visible in the current view this returns the
	 * short description feature, if not the textual description feature is returned.
	 * 
	 * @return The active description feature.
	 */
	public EAttribute getActiveFeature()
	{
		// set result
		if (Views.getInstance().displayObject(
				ProcessesPackage.Literals.GUARD__SHORTDESCRIPTION))
		{
			return ProcessesPackage.eINSTANCE.getGuard_Shortdescription();
		}
		else
		{
			return ProcessesPackage.eINSTANCE.getGuard_Textualdescription();
		}
	}


	/**
	 * If the ShortDescription property is visible in the current view this returns the
	 * short description, if not the textual description is returned.
	 * 
	 * According to the user preferences, the text is wrapped and/or trimmed.
	 * 
	 * @param Trim
	 *            indicates whether the text should be trimmed
	 * @return The active description.
	 */
	@SuppressWarnings("nls")
	public String getActiveText(boolean trim)
	{
		Guard guard = getGuard();
		if (guard == null)
		{
			return "";
		}

		String result = "";
		int maxLineLengthCut;
		int maxLineLengthWrap;

		boolean autoWrap = PreferenceReader.guardAutoWrap.get();

		// set result
		if (Views.getInstance().displayObject(
				ProcessesPackage.Literals.GUARD__SHORTDESCRIPTION))
		{
			// set text to short description
			if (guard.getShortdescription() != null)
			{
				result = guard.getShortdescription();
			}

			// get line preferences
			maxLineLengthCut = PreferenceReader.guardShortCut.get();
			maxLineLengthWrap = PreferenceReader.guardShortWrap.get();
		}
		else
		{
			// set text to textual description
			if (guard.getTextualdescription() != null)
			{
				result = guard.getTextualdescription();
			}

			// get line preferences
			maxLineLengthCut = PreferenceReader.guardTextualCut.get();
			maxLineLengthWrap = PreferenceReader.guardTextualWrap.get();
		}

		// if output text is too long, trim text
		// if trim is set, the text is needed as is for direct edit
		if (trim && maxLineLengthCut != 0 && result.length() > maxLineLengthCut)
		{
			result = result.substring(0, maxLineLengthCut - 1);
			result += "...";
		}

		// if output text is too long, wrap text
		// if trim is set, the text is needed as is for direct edit
		if (trim && maxLineLengthWrap != 0 && result.length() > maxLineLengthWrap)
		{
			String temp = result;
			ArrayList splittedString;

			// smart- or simple wrapping
			if (autoWrap)
			{
				String[] controlCharacters =
				{ " ", "==", ">", "<", "!=", "&&", "||", "(", ")", ".", "," };

				splittedString = StringParser.splitChar(temp, controlCharacters, 3);
			}
			else
			{
				splittedString = StringParser.splitLength(temp, maxLineLengthWrap);
			}

			result = StringParser.concatWithLineWraps(splittedString, maxLineLengthWrap);
		}

		// return result
		return result;
	}


	/**
	 * Returns the color depending on the view.
	 * 
	 * @return The color.
	 */
	private Color getActiveColor()
	{
		Color color;

		if (Views.getInstance().displayObject(
				ProcessesPackage.Literals.GUARD__SHORTDESCRIPTION))
		{
			color = PreferenceReader.appearanceguardShortColor.get();
		}
		else
		{
			color = PreferenceReader.appearanceguardTextualColor.get();
		}

		return color;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
	 */
	@Override
	protected void refreshVisuals()
	{
		try
		{
			super.refreshVisuals();

			// set the text and color depending on the current view
			((NamedElementFigure) getFigure()).setName(getActiveText(true));
			((NamedElementFigure) getFigure()).getLabel().setForegroundColor(
					getActiveColor());
		}
		catch (Exception ex)
		{
			logger.debug(ex);
		}
	}


	/**
	 * Locates the rootnode.
	 * 
	 * @param eobj
	 * @return Root node
	 */
	private EObject getRootNode(EObject eobj)
	{
		if (eobj == null || eobj.eContainer() == null)
		{
			return eobj;
		}
		return getRootNode(eobj.eContainer());
	}


	/**
	 * Collects all data lying underneath a given eObject.
	 * 
	 * @param obj
	 * @param datalist
	 */
	private void collectData(EObject obj, List<Data> datalist)
	{
		if (obj instanceof org.eclipse.jwt.meta.model.core.Package)
		{
			Package p = (Package) obj;
			EList elements = p.getElements();
			for (int i = 0; i < elements.size(); i++)
			{
				Object elem = elements.get(i);
				if (elem instanceof DataImpl)
				{
					datalist.add((Data) elem);
				}
			}
			EList subpackages = p.getSubpackages();
			for (int i = 0; i < subpackages.size(); i++)
			{
				collectData((EObject) subpackages.get(i), datalist);
			}
		}
	}


	/**
	 * Sets the specification tree, if the GUARD__SHORTDESCRIPTION has changed
	 */
	@SuppressWarnings("nls")
	private void setSpecificationTree(String oldValue, String newValue)
	{
		Guard guard = getGuard();

		if ((guard != null) && (guard.getShortdescription() != null)
				&& (!guard.getShortdescription().equals("")))
		{
			GuardSpecification rootnode = null;
			try
			{

				List<Data> datalist = new ArrayList<Data>();
				collectData(getRootNode(guard), datalist);
				StringtoTreeParser stp = new StringtoTreeParser(guard
						.getShortdescription(), datalist);
				rootnode = stp.getRootNode();
			}
			catch (Exception ex)
			{
				// workaround
				// maybe this method gets called twice. don't know if this is how
				// it's supposed to be, but just in case i left it this way and
				// display a possible error message only if the new and old
				// notification values are different
				if (oldValue == null)
				{
					oldValue = "";
				}
				if (newValue == null)
				{
					newValue = "";
				}

				if (!oldValue.equals(newValue))
				{
					String message = "";

					if (ex instanceof NotaFormulaException)
					{
						message = " (" + ((NotaFormulaException) ex).getMessage() + ")";
					}

					message = PluginProperties.guards_ParseError_message + message
							+ ".\n" + PluginProperties.guards_ParseError_message2;

					MessageDialog.openError(GeneralHelper.getActiveShell(),
							PluginProperties.guards_ParseError_title, message);

					// reset old value
					getGuard().setShortdescription(oldValue);
				}
			}
			try
			{
				GuardSpecification gs = guard.getDetailedSpecification();
				if (gs != null)
				{
					EcoreUtil.replace(gs, rootnode);
				}
				else
					guard.setDetailedSpecification(rootnode);
			}
			catch (Exception ex)
			{
				logger.debug(ex);
			}
		}
		else if (guard != null)
		{
			guard.setDetailedSpecification(null);
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jwt.we.parts.core.GraphicalModelElementEditPart#notifyChanged(org.eclipse
	 * .emf.common.notify.Notification)
	 */
	@Override
	public void notifyChanged(Notification notification)
	{
		super.notifyChanged(notification);
		try
		{
			Object obj = notification.getNotifier();
			if (obj instanceof Guard)
			{
				int featureId = notification.getFeatureID(Guard.class);
				int notificationid = notification.getEventType();
				switch (notificationid)
				{
					case Notification.ADD:
					case Notification.ADD_MANY:
					case Notification.REMOVE:
					case Notification.REMOVE_MANY:
					case Notification.SET:
					case Notification.UNSET:
						switch (featureId)
						{
							case ProcessesPackage.GUARD__SHORTDESCRIPTION:
								setSpecificationTree(notification.getOldStringValue(),
										notification.getNewStringValue());
								refreshChildren();
							case ProcessesPackage.GUARD__DETAILED_SPECIFICATION:
							case ProcessesPackage.GUARD__TEXTUALDESCRIPTION:
								refreshVisuals();
								break;
						}
						break;
				}
			}
			else if (obj instanceof GuardSpecification)
			{
				int notificationid = notification.getEventType();
				switch (notificationid)
				{
					case Notification.SET:
						refreshShortDesc();
						refreshVisuals();
				}
			}
		}
		catch (Exception ex)
		{
			logger.debug(ex);
		}
	}

}