package de.uni_hildesheim.sse.trans.convert;

import org.junit.Assert;
import org.junit.Test;

import de.uni_hildesheim.sse.model.cst.CSTSemanticException;
import de.uni_hildesheim.sse.model.cst.ConstraintSyntaxTree;
import de.uni_hildesheim.sse.model.cst.OCLFeatureCall;
import de.uni_hildesheim.sse.model.cst.Variable;
import de.uni_hildesheim.sse.model.varModel.Constraint;
import de.uni_hildesheim.sse.model.varModel.DecisionVariableDeclaration;
import de.uni_hildesheim.sse.model.varModel.Project;
import de.uni_hildesheim.sse.model.varModel.datatypes.BooleanType;
import de.uni_hildesheim.sse.model.varModel.datatypes.OclKeyWords;
import de.uni_hildesheim.sse.model.varModel.filter.ConstraintFinder;
import de.uni_hildesheim.sse.model.varModel.filter.DeclarationFinder;
import de.uni_hildesheim.sse.model.varModel.filter.DeclarationFinder.VisibilityType;
import de.uni_hildesheim.sse.model.varModel.filter.FilterType;

/**
 * Tests the {@link ModelOptimizer}.
 * 
 * @author Adam Krafczyk
 */
public class ModelOptimizerTest {

    /**
     * Tests the removeUnusedVariables() method.
     * 
     * @throws CSTSemanticException can't happen.
     */
    @Test
    public void testRemoveUnusedVariables() throws CSTSemanticException {
        Project model = new Project("TestModel");
        
        DecisionVariableDeclaration declA = new DecisionVariableDeclaration("a", BooleanType.TYPE, model);
        DecisionVariableDeclaration declB = new DecisionVariableDeclaration("b", BooleanType.TYPE, model);
        DecisionVariableDeclaration declC = new DecisionVariableDeclaration("c", BooleanType.TYPE, model);
        DecisionVariableDeclaration declD = new DecisionVariableDeclaration("d", BooleanType.TYPE, model);
        
        model.add(declA);
        model.add(declB);
        model.add(declC);
        model.add(declD);
        
        Variable varA = new Variable(declA);
        Variable varB = new Variable(declB);
        
        OCLFeatureCall term = new OCLFeatureCall(varA, OclKeyWords.OR, varB);
        
        Constraint constraint = new Constraint(model);
        constraint.setConsSyntax(term);
        model.add(constraint);
        
        ModelOptimizer optimizer = new ModelOptimizer(model);
        int removed = optimizer.removeUnusedVariables();
        
        /*
         * c and d should be removed
         */
        
        Assert.assertEquals(2, removed);
        
        DeclarationFinder declarationFinder = new DeclarationFinder(model, FilterType.ALL, null);
        Assert.assertEquals(2, declarationFinder.getVariableDeclarations(VisibilityType.ALL).size());
    }
    
    /**
     * Tests the handleConstantVariables() method.
     * 
     * @throws CSTSemanticException can't happen.
     */
    @Test
    public void testHandleConstantVariables() throws CSTSemanticException {
        Project model = new Project("TestModel");
        
        DecisionVariableDeclaration declA = new DecisionVariableDeclaration("a", BooleanType.TYPE, null);
        DecisionVariableDeclaration declB = new DecisionVariableDeclaration("b", BooleanType.TYPE, null);
        DecisionVariableDeclaration declC = new DecisionVariableDeclaration("c", BooleanType.TYPE, null);
        DecisionVariableDeclaration declD = new DecisionVariableDeclaration("d", BooleanType.TYPE, null);
        
        model.add(declA);
        model.add(declB);
        model.add(declC);
        model.add(declD);
        
        Variable varA = new Variable(declA);
        Variable varB = new Variable(declB);
        Variable varC = new Variable(declC);
        Variable varD = new Variable(declD);
        
        ConstraintSyntaxTree[] terms = new ConstraintSyntaxTree[6];
        terms[0] = varA;
        terms[1] = new OCLFeatureCall(varA, OclKeyWords.OR, varB);
        terms[2] = new OCLFeatureCall(new OCLFeatureCall(varA, OclKeyWords.NOT), OclKeyWords.OR, varB);
        
        terms[3] = new OCLFeatureCall(varC, OclKeyWords.NOT);
        terms[4] = new OCLFeatureCall(terms[3], OclKeyWords.OR, varD);
        terms[5] = new OCLFeatureCall(varC, OclKeyWords.OR, varD);
        
        for (ConstraintSyntaxTree tree : terms) {
            Constraint constraint = new Constraint(model);
            constraint.setConsSyntax(tree);
            model.add(constraint);
        }
        
        ModelOptimizer optimizer = new ModelOptimizer(model);
        int removed = optimizer.handleConstantVariables();
        
        /*
         * terms [1] and [4] should be removed.
         */
        
        Assert.assertEquals(2, removed);
        
        ConstraintFinder cFinder = new ConstraintFinder(model);
        Assert.assertEquals(4, cFinder.getConstraints().size());
    }
    
    /**
     * Tests the removeDuplicatedConstraints() method.
     * 
     * @throws CSTSemanticException can't happen.
     */
    @Test
    public void testRemoveDuplicatedConstraints() throws CSTSemanticException {
        Project model = new Project("TestModel");
        
        DecisionVariableDeclaration declA = new DecisionVariableDeclaration("a", BooleanType.TYPE, null);
        DecisionVariableDeclaration declB = new DecisionVariableDeclaration("b", BooleanType.TYPE, null);
        
        model.add(declA);
        model.add(declB);
        
        ConstraintSyntaxTree[] terms = new ConstraintSyntaxTree[3];
        
        Variable varA = new Variable(declA);
        Variable varB = new Variable(declB);
        
        terms[0] = new OCLFeatureCall(varA, OclKeyWords.OR, varB);
        terms[1] = new OCLFeatureCall(varB, OclKeyWords.OR, varA);
        terms[2] = new OCLFeatureCall(varA, OclKeyWords.OR, new OCLFeatureCall(varB, OclKeyWords.NOT));
        
        for (ConstraintSyntaxTree tree : terms) {
            Constraint constraint = new Constraint(model);
            constraint.setConsSyntax(tree);
            model.add(constraint);
        }
        
        ModelOptimizer optimizer = new ModelOptimizer(model);
        int removed = optimizer.removeDuplicatedConstraints();
        
        /*
         * term [1] should be removed
         */
        
        Assert.assertEquals(1, removed);
        
        ConstraintFinder cFinder = new ConstraintFinder(model);
        Assert.assertEquals(2, cFinder.getConstraints().size());
    }
    
}
