package relationship.intra_relationship_constraints;

import java.util.List;

import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.ICreateContext;
import org.eclipse.graphiti.features.context.IDeleteContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.DeleteContext;
import org.eclipse.graphiti.features.context.impl.MultiDeleteInfo;
import org.eclipse.graphiti.mm.algorithms.Polyline;
import org.eclipse.graphiti.mm.algorithms.Text;
import org.eclipse.graphiti.mm.algorithms.styles.LineStyle;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.framed.iorm.model.Model;
import org.framed.iorm.model.OrmFactory;
import org.framed.iorm.model.Relation;
import org.framed.iorm.model.Type;
import org.framed.iorm.ui.FRaMEDShapePattern;
import org.framed.iorm.ui.UIUtil;
import org.framed.iorm.ui.editPolicy.EditPolicyService;
import org.framed.iorm.ui.palette.FeaturePaletteDescriptor;
import org.framed.iorm.ui.palette.PaletteCategory;
import org.framed.iorm.ui.palette.PaletteView;
import org.framed.iorm.ui.palette.ViewVisibility;

/**
 * This is the abstract super class of the patterns for intra relationship contraints. It collects similiar operations
 * and attributes of the patterns {@link AcyclicConstraintPattern}, {@link CyclicConstraintPattern}, 
 * {@link IrreflexiveConstraintPattern}, {@link ReflexiveConstraintPattern} and {@link TotalConstraintPattern}.
 * <p>
 * This is implemented as shape pattern instead of a connection pattern since its easier for the user to click on a relationship to
 * add a constraint. This would not be able if a connection pattern would be used. As a developer this solution is also preferred as
 * this way already provides the relationship to add the constraint to. Otherwise the relationship has to be searched and more code
 * would be needed.
 * @author Kevin Kassin
 */
public abstract class AbstractIntraRelationshipConstraintPattern extends FRaMEDShapePattern {

	/**
	 * the object to get names, ids and so on for this feature
	 */
	private final Literals literals = new Literals();
	
	/**
	 * the feature palette descriptor manages the palette visibility, see {@link FeaturePaletteDescriptor}
	 */
	private final FeaturePaletteDescriptor spec_FPD = new FeaturePaletteDescriptor(
			PaletteCategory.CONSTRAINTS_CATEGORY,
			ViewVisibility.COMPARTMENT_VIEW) {
				@Override
				public boolean featureExpression(List<String> framedFeatureNames, PaletteView paletteView) {
					return framedFeatureNames.contains("Intra_Relationship_Constraints");
			}	};
			
	/**
	 * Class constructor
	 */
	public AbstractIntraRelationshipConstraintPattern() {
		super();
		FPD = spec_FPD;
		ICON_IMG_PATH = literals.ICON_IMG_PATH;
	}			
		
	/**
	 * checks if pattern is applicable for a given business object
	 * @return true, if the business object is a {@link org.framed.iorm.model.Relation} of the right type 
	 */
	@Override
	public boolean isMainBusinessObjectApplicable(Object mainBusinessObject) {
		if(mainBusinessObject instanceof Relation) {
			Relation relation = (Relation) mainBusinessObject;
			if(relation.getType() == Type.ACYCLIC ||
			   relation.getType() == Type.CYCLIC ||
			   relation.getType() == Type.REFLEXIVE ||
			   relation.getType() == Type.IRREFLEXIVE ||
			   relation.getType() == Type.TOTAL)
			return true;	
		}
		return false;
	}

	/**
	 * checks if pattern is applicable for a given pictogram element
	 * @return true, if business object of the pictogram element is a {@link org.framed.iorm.model.Relation} of the right type 
	 */
	@Override
	protected boolean isPatternControlled(PictogramElement pictogramElement) {
		return isMainBusinessObjectApplicable(this.getBusinessObjectForPictogramElement(pictogramElement));
	}
	
	/**
	 * checks if pattern is applicable for a given pictogram element
	 * @return true, if the business object of the pictogram element is a {@link org.framed.iorm.model.Relation} of the right type
	 */
	@Override
	protected boolean isPatternRoot(PictogramElement pictogramElement) {
		return isMainBusinessObjectApplicable(this.getBusinessObjectForPictogramElement(pictogramElement));
	}
	
	//add feature
	//~~~~~~~~~~~
	/**
	 * calculates if the intra relationship constraint can be added to the relationship
	 * <p>
	 * returns true if:<br>
	 * (1) the new business object is a {@link org.framed.iorm.model.Relation} of the right type 
	 * @return if the intra relationship constraint can be added
	 */
	public boolean canAddIntraRelationshipConstraint(IAddContext addContext, Type type) {
		if(addContext.getNewObject() instanceof Relation) {
		   Relation relation = (Relation) addContext.getNewObject();
		   if(relation.getType() == type) {
			   return EditPolicyService.getHandler(this.getDiagram()).canAdd(addContext, type);
		}	}
		return false;
	}
	
	/**
	 * adds the graphical representation of an intra relationship constraint in the relationship and changes the visual
	 * appearance of the relationship if it didn't had an intra relationship constraint before 
	 * <p>
	 * @param addContext the context which has a reference to the relationship to add the constraint to
	 * @param type the type of the constraint to add
	 * @return the connection decorator with the constraints name added
	 */
	public PictogramElement addIntraRelationshipConstraint(IAddContext addContext, Type type) {
		Connection targetConnection = addContext.getTargetConnection();
		Relation targetRelation = (Relation) getBusinessObjectForPictogramElement(targetConnection);
		Polyline poyline = (Polyline) targetConnection.getGraphicsAlgorithm();
		((Relation) addContext.getNewObject()).getReferencedRelation().add(targetRelation);
		int numberOfReferencedRelations = targetRelation.getReferencedRelation().size();
		poyline.setLineStyle(LineStyle.DASH);
		ConnectionDecorator constraintName = 
			pictogramElementCreateService.createConnectionDecorator(targetConnection, true, 0.5, true); 
		Text nameText = graphicAlgorithmService.createText(constraintName, type.getName().toLowerCase());
		nameText.setForeground(manageColor(literals.COLOR_CONSTRAINT_TEXT));
		graphicAlgorithmService.setLocation(nameText, literals.DISTANCE_FROM_CONNECTION_LINE, (numberOfReferencedRelations-1)*literals.HEIGHT_INTRAREL_CONSTRAINT);
		UIUtil.setShape_IdValue(constraintName, literals.SHAPE_ID_INTRA_REL_CON_NAME_DECORATOR);
		link(constraintName, addContext.getNewObject());
		return constraintName;
	}	
		
	//create feature
	//~~~~~~~~~~~~~~
	/**
	 * calculates if a intra relationship constraint can be created
	 * <p>
	 * returns true if the clicked on pictogram element belongs to a relationship
	 * @return if inheritance can be added
	 */
	@Override
	public boolean canCreate(ICreateContext createContext) {
		Connection targetConnection = createContext.getTargetConnection();
		if(targetConnection != null &&
		   getBusinessObjectForPictogramElement(targetConnection) instanceof Relation) {
			Relation relation = (Relation) getBusinessObjectForPictogramElement(targetConnection);
			return EditPolicyService.getHandler(this.getDiagram()).canCreate(createContext, this.getModelType());
		}
	    return false;
	}
	
	/**
	 * creates the business object of an intra relationship constraint of the given type 
	 * @param createContext the context which has a reference to the relationship to add the constraint to 
	 * @param type the type of the constraint to add to
	 * @param aircp the sub class calling this operation
	 * @return the created business object
	 */
	public Object[] createIntraRelationshipConstraint(ICreateContext createContext, Type type, AbstractIntraRelationshipConstraintPattern aircp) { //TODO: Ask Kevin why this fails
		Connection targetConnection = createContext.getTargetConnection();
		Relation targetRelation = (Relation) getBusinessObjectForPictogramElement(targetConnection);
		Anchor sourceAnchor = targetConnection.getStart(),
			   targetAnchor = targetConnection.getEnd();
		Relation newIntraRelCon = OrmFactory.eINSTANCE.createRelation();
	    newIntraRelCon.setType(type); 
	    Model model = targetRelation.getContainer();
		model.getElements().add(newIntraRelCon);
		newIntraRelCon.setContainer(model);
		newIntraRelCon.setSource(UIUtil.getModelElementForAnchor(sourceAnchor));
		newIntraRelCon.setTarget(UIUtil.getModelElementForAnchor(targetAnchor)); 
		targetRelation.getReferencedRelation().add(newIntraRelCon);
		AddContext addContext = new AddContext();
	    addContext.setNewObject(newIntraRelCon);
	    addContext.setTargetConnection(targetConnection);
	    if(aircp.canAdd(addContext)) {
	    	aircp.add(addContext); //FIXME: ?
	    }
		return new Object[] { newIntraRelCon }; 
	}
	
	//delete feature
	//~~~~~~~~~~~~~~
	/**
	 * disables the "Are you sure?" message when intra relationship constraints and changes the visual appearance of the 
	 * relation if it does not longer has a intra relationship constraint after the execution of delete
	 */
	@Override
	public void delete(IDeleteContext deleteContext) {
		Connection targetConnection = ((ConnectionDecorator) deleteContext.getPictogramElement()).getConnection();
		Relation targetRelation = (Relation) getBusinessObjectForPictogramElement(targetConnection);
		if(targetRelation.getReferencedRelation().size() == 1) {
			Polyline poyline = (Polyline) targetConnection.getGraphicsAlgorithm();
			poyline.setLineStyle(LineStyle.SOLID);
		}	
		((DeleteContext) deleteContext).setMultiDeleteInfo(new MultiDeleteInfo(false, false, 0));
		super.delete(deleteContext);
	}
}