package oclastview.visitors.arithSimp;

import java.util.Collection;
import java.util.List;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionLiteralExp;
import org.eclipse.ocl.expressions.CollectionLiteralPart;
import org.eclipse.ocl.expressions.CollectionRange;
import org.eclipse.ocl.expressions.EnumLiteralExp;
import org.eclipse.ocl.expressions.IfExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.InvalidLiteralExp;
import org.eclipse.ocl.expressions.IterateExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.LetExp;
import org.eclipse.ocl.expressions.MessageExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StateExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralPart;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.ocl.utilities.Visitable;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.ValueSpecification;

public class OCLCloner<C, O, P, EL, PM, S, COA, SSA, CT> extends
    AbstractVisitor<Visitable, C, O, P, EL, PM, S, COA, SSA, CT> {

  Environment<?, C, O, P, EL, PM, S, COA, SSA, CT, ?, ?> _env = null;

  public static OCLCloner<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> getEcoreVersion() {
    Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> auxEnv = EcoreEnvironmentFactory.INSTANCE
        .createEnvironment();
    OCLCloner<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> res = new OCLCloner<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>(
        auxEnv);
    return res;
  }

  public static OCLCloner<Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, org.eclipse.uml2.uml.Constraint> getUML2Version() {
    org.eclipse.ocl.uml.OCL umlocl = org.eclipse.ocl.uml.OCL.newInstance();
    Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, org.eclipse.uml2.uml.CallOperationAction, org.eclipse.uml2.uml.SendSignalAction, org.eclipse.uml2.uml.Constraint, Class, EObject> auxEnv = umlocl
        .getEnvironment();
    OCLCloner<Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, org.eclipse.uml2.uml.Constraint> res = new OCLCloner(
        auxEnv);
    return res;
  }

  private final UMLReflection<?, C, O, P, EL, PM, S, COA, SSA, CT> uml;

  /**
   * Initializes me with my environment.
   
   @param env
   *            my environment
   */
  protected OCLCloner(
      Environment<?, C, O, P, EL, PM, S, COA, SSA, CT, ?, ?> env) {
    _env = env;
    this.uml = (env == nullnull : env.getUMLReflection();
  }

  @Override
  public TypeExp<C> visitTypeExp(TypeExp<C> t) {
    TypeExp<C> res = (TypeExp<C>EcoreUtil.copy(t);
    return res;
  }

  @Override
  public Variable<C, PM> visitVariable(Variable<C, PM> variable) {
    Variable<C, PM> res = (Variable<C, PM>EcoreUtil.copy(variable);
    return res;
  }

  @Override
  public StateExp<C, S> visitStateExp(StateExp<C, S> stateExp) {
    StateExp<C, S> res = (StateExp<C, S>EcoreUtil.copy(stateExp);
    return res;
  }

  @Override
  public IntegerLiteralExp<C> visitIntegerLiteralExp(
      IntegerLiteralExp<C> literalExp) {
    IntegerLiteralExp<C> res = (IntegerLiteralExp<C>EcoreUtil
        .copy(literalExp);
    return res;
  }

  @Override
  public RealLiteralExp<C> visitRealLiteralExp(RealLiteralExp<C> literalExp) {
    RealLiteralExp<C> res = (RealLiteralExp<C>EcoreUtil.copy(literalExp);
    return res;
  }

  @Override
  public StringLiteralExp<C> visitStringLiteralExp(
      StringLiteralExp<C> literalExp) {
    StringLiteralExp<C> res = (StringLiteralExp<C>EcoreUtil
        .copy(literalExp);
    return res;
  }

  @Override
  public BooleanLiteralExp<C> visitBooleanLiteralExp(
      BooleanLiteralExp<C> literalExp) {
    BooleanLiteralExp<C> res = (BooleanLiteralExp<C>EcoreUtil
        .copy(literalExp);
    return res;
  }

  @Override
  public NullLiteralExp<C> visitNullLiteralExp(NullLiteralExp<C> literalExp) {
    NullLiteralExp<C> res = (NullLiteralExp<C>EcoreUtil.copy(literalExp);
    return res;
  }

  @Override
  public InvalidLiteralExp<C> visitInvalidLiteralExp(
      InvalidLiteralExp<C> literalExp) {
    InvalidLiteralExp<C> res = (InvalidLiteralExp<C>EcoreUtil
        .copy(literalExp);
    return res;
  }

  @Override
  public EnumLiteralExp<C, EL> visitEnumLiteralExp(
      EnumLiteralExp<C, EL> literalExp) {
    EnumLiteralExp<C, EL> res = (EnumLiteralExp<C, EL>EcoreUtil
        .copy(literalExp);
    return res;
  }

  @Override
  public UnlimitedNaturalLiteralExp<C> visitUnlimitedNaturalLiteralExp(
      UnlimitedNaturalLiteralExp<C> literalExp) {
    UnlimitedNaturalLiteralExp<C> res = (UnlimitedNaturalLiteralExp<C>EcoreUtil
        .copy(literalExp);
    return res;
  }

  // handlers

  @Override
  protected Visitable handleAssociationClassCallExp(
      AssociationClassCallExp<C, P> callExp, Visitable sourceResult,
      List<Visitable> qualifierResults) {
    AssociationClassCallExp<C, P> res = (AssociationClassCallExp<C, P>EcoreUtil
        .copy(callExp);
    res.setSource((OCLExpression<C>sourceResult);
    res.getQualifier().clear();
    for (Visitable oq : qualifierResults) {
      res.getQualifier().add((OCLExpression<C>oq);
    }
    return res;

  }

  @Override
  protected Visitable handleCollectionItem(CollectionItem<C> item,
      Visitable itemResult) {
    CollectionItem<C> res = (CollectionItem<C>EcoreUtil.copy(item);
    res.setItem((OCLExpression<C>itemResult);
    return res;
  }

  @Override
  protected Visitable handleCollectionLiteralExp(
      CollectionLiteralExp<C> literalExp, List<Visitable> partResults) {
    CollectionLiteralExp<C> res = (CollectionLiteralExp<C>EcoreUtil
        .copy(literalExp);
    res.getPart().clear();
    res.getPart().addAll(
        (Collection<? extends CollectionLiteralPart<C>>partResults);
    return res;
  }

  @Override
  protected Visitable handleCollectionRange(CollectionRange<C> range,
      Visitable firstResult, Visitable lastResult) {
    CollectionRange<C> res = (CollectionRange<C>EcoreUtil.copy(range);
    res.setFirst((OCLExpression<C>firstResult);
    res.setLast((OCLExpression<C>lastResult);
    return res;
  }

  @Override
  protected Visitable handleConstraint(CT constraint,
      Visitable specificationResult) {
    CT res = (CTEcoreUtil.copy((EObjectconstraint);
    if (constraint instanceof org.eclipse.ocl.ecore.Constraint) {
      org.eclipse.ocl.ecore.Constraint ct = (org.eclipse.ocl.ecore.Constraintconstraint;
      ct
          .setSpecification((ExpressionInOCL<EClassifier, EParameter>specificationResult);
    }
    if (constraint instanceof org.eclipse.uml2.uml.Constraint) {
      org.eclipse.uml2.uml.Constraint ct = (org.eclipse.uml2.uml.Constraintconstraint;
      ct.setSpecification((ValueSpecificationspecificationResult);
    }
    return (Visitableres;
  }

  @Override
  protected Visitable handleExpressionInOCL(ExpressionInOCL<C, PM> callExp,
      Visitable contextResult, Visitable resultResult,
      List<Visitable> parameterResults, Visitable bodyResult) {
    ExpressionInOCL<C, PM> res = (ExpressionInOCL<C, PM>EcoreUtil
        .copy(callExp);
    res.setBodyExpression((OCLExpression<C>bodyResult);
    res.setContextVariable((Variable<C, PM>contextResult);
    res.setResultVariable((Variable<C, PM>resultResult);
    return res;
  }

  @Override
  protected Visitable handleIfExp(IfExp<C> ifExp, Visitable conditionResult,
      Visitable thenResult, Visitable elseResult) {
    IfExp<C> res = (IfExp<C>EcoreUtil.copy(ifExp);
    res.setCondition((OCLExpression<C>conditionResult);
    res.setThenExpression((OCLExpression<C>thenResult);
    res.setElseExpression((OCLExpression<C>elseResult);
    return res;
  }

  @Override
  protected Visitable handleIterateExp(IterateExp<C, PM> callExp,
      Visitable sourceResult, List<Visitable> variableResults,
      Visitable resultResult, Visitable bodyResult) {
    IterateExp<C, PM> res = (IterateExp<C, PM>EcoreUtil.copy(callExp);
    res.setSource((OCLExpression<C>sourceResult);
    res.setBody((OCLExpression<C>bodyResult);
    res.setResult((Variable<C, PM>resultResult);
    res.getIterator().clear();
    res.getIterator().addAll(
        (Collection<? extends Variable<C, PM>>variableResults);
    return res;
  }

  @Override
  protected Visitable handleIteratorExp(IteratorExp<C, PM> callExp,
      Visitable sourceResult, List<Visitable> variableResults,
      Visitable bodyResult) {
    IteratorExp<C, PM> res = (IteratorExp<C, PM>EcoreUtil.copy(callExp);
    res.setSource((OCLExpression<C>sourceResult);
    res.setBody((OCLExpression<C>bodyResult);
    res.getIterator().clear();
    res.getIterator().addAll(
        (Collection<? extends Variable<C, PM>>variableResults);
    return res;
  }

  @Override
  protected Visitable handleLetExp(LetExp<C, PM> letExp,
      Visitable variableResult, Visitable inResult) {
    LetExp<C, PM> res = (LetExp<C, PM>EcoreUtil.copy(letExp);
    res.setVariable((Variable<C, PM>variableResult);
    return res;
  }

  @Override
  protected Visitable handleMessageExp(MessageExp<C, COA, SSA> messageExp,
      Visitable targetResult, List<Visitable> argumentResults) {
    MessageExp<C, COA, SSA> res = (MessageExp<C, COA, SSA>EcoreUtil
        .copy(messageExp);
    res.setTarget((OCLExpression<C>targetResult);
    res.getArgument().clear();
    res.getArgument().addAll(
        (Collection<? extends OCLExpression<C>>argumentResults);
    return res;
  }

  @Override
  protected Visitable handleOperationCallExp(OperationCallExp<C, O> callExp,
      Visitable sourceResult, List<Visitable> argumentResults) {
    OperationCallExp<C, O> res = (OperationCallExp<C, O>EcoreUtil
        .copy(callExp);
    res.setSource((OCLExpression<C>sourceResult);
    res.getArgument().clear();
    res.getArgument().addAll(
        (Collection<? extends OCLExpression<C>>argumentResults);
    return res;
  }

  @Override
  protected Visitable handlePropertyCallExp(PropertyCallExp<C, P> callExp,
      Visitable sourceResult, List<Visitable> qualifierResults) {
    PropertyCallExp<C, P> res = (PropertyCallExp<C, P>EcoreUtil
        .copy(callExp);
    res.setSource((OCLExpression<C>sourceResult);
    res.getQualifier().clear();
    res.getQualifier().addAll(
        (Collection<? extends OCLExpression<C>>qualifierResults);
    return res;
  }

  @Override
  protected Visitable handleTupleLiteralExp(TupleLiteralExp<C, P> literalExp,
      List<Visitable> partResults) {
    TupleLiteralExp<C, P> res = (TupleLiteralExp<C, P>EcoreUtil
        .copy(literalExp);
    res.getPart().clear();
    res.getPart().addAll(
        (Collection<? extends TupleLiteralPart<C, P>>partResults);
    return res;
  }

  @Override
  protected Visitable handleTupleLiteralPart(TupleLiteralPart<C, P> part,
      Visitable valueResult) {
    TupleLiteralPart<C, P> res = (TupleLiteralPart<C, P>EcoreUtil
        .copy(part);
    res.setValue((OCLExpression<C>valueResult);
    return res;
  }

  @Override
  protected Visitable handleVariable(Variable<C, PM> variable,
      Visitable initResult) {
    Variable<C, PM> res = (Variable<C, PM>EcoreUtil.copy(variable);
    res.setInitExpression((OCLExpression<C>initResult);
    return res;
  }

}