package de.uni_hildesheim.sse.trans.in.rsf;

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

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.datatypes.Enum;
import de.uni_hildesheim.sse.model.varModel.datatypes.OclKeyWords;
import de.uni_hildesheim.sse.trans.in.AbstractReader;
import de.uni_hildesheim.sse.trans.in.ParserException;

/**
 * Represents a condition implied by the default value of a variable. Should not be added to the model if
 * the user can modify the variable (i.e. HasPrompt).<br />
 * If the variable is a boolean the pure boolean constraint will look like this:<br />
 * <code>
 * NOT condition OR variable
 * </code><br />
 * (varible will be negated if the default value is "n" instead of "y".<br />
 * If the variable is not a boolean the pure boolean constraint will lokk like:<br />
 * <code>
 * NOT condition OR variable=defaultValue
 * </code><br />
 * If the item has a prompt condition the above expression is combined with OR NOT promptCondition
 * 
 * @author Adam Krafczyk
 */
class RSFDefaultCondition extends RSFCondition {
//    private static final EASyLogger LOGGER = EASyLoggerFactory.INSTANCE.getLogger(RSFDefaultCondition.class,
//            "RSFDefaultCondition");

    private RSFItem variable;
    private String defaultValue;
    private String condition;
    
    /**
     * Creates a {@link RSFDefaultCondition} for a Default line read from a .rsf file.
     * If the variable is boolean only "y" and "n" are allowed as default values.
     * @param variable The variable with the (possible) default value.
     * @param defaultValue The default value of the variable.
     * @param condition The condition that has to be true if the default value has to be set.
     */
    RSFDefaultCondition(RSFItem variable, String defaultValue, String condition) {
        this.variable = variable;
        this.defaultValue = defaultValue;
        this.condition = condition;
    }
    
    @Override
    List<ConstraintSyntaxTree> toPureBooleanConstraintSyntaxTree(RSFReader reader) throws ParserException {
        List<ConstraintSyntaxTree> defaultVariableConditions = new ArrayList<ConstraintSyntaxTree>();
        
        createDefaultConditions(reader, defaultVariableConditions);
        
        String promptConditionString = variable.getPromptCondition();
        ConstraintSyntaxTree promptCondition = null;
        boolean hasPrompt = false;
        if (promptConditionString != null) {
            if (promptConditionString.equals("y") || promptConditionString.equals("'y'")) {
                hasPrompt = true;
            } else if (!promptConditionString.equals("n") && !promptConditionString.equals("'n'")) {
                promptCondition = getPureBooleanConstraintSyntaxTree(reader,
                        promptConditionString, variable.getName());
            }
        }
        
        List<ConstraintSyntaxTree> result = new ArrayList<ConstraintSyntaxTree>();
        
        /*
         * prompt OR NOT condition OR defaultVariableConditions
         */
        if (!hasPrompt) {
            
            ConstraintSyntaxTree condition = getPureBooleanConstraintSyntaxTree(reader, this.condition,
                    variable.getName());
            
            ConstraintSyntaxTree prependCall = null;
            
            if (promptCondition != null) {
                prependCall = promptCondition;
            }
            if (condition != null) {
                if (prependCall == null) {
                    prependCall = new OCLFeatureCall(condition, OclKeyWords.NOT);
                } else {
                    OCLFeatureCall notCondition = new OCLFeatureCall(condition, OclKeyWords.NOT);
                    prependCall = new OCLFeatureCall(prependCall, OclKeyWords.OR, notCondition);
                }
            }
            
            for (ConstraintSyntaxTree defaultVariableCondition : defaultVariableConditions) {
                if (prependCall != null) {
                    defaultVariableCondition = new OCLFeatureCall(prependCall, OclKeyWords.OR,
                            defaultVariableCondition);
                }
                result.add(defaultVariableCondition);
            }
            
        }
        
        return result;
    }

    /**
     * Will create a list of conditions for the Default values and adds them to the given list.
     * Part of the {@link #toPureBooleanConstraintSyntaxTree(RSFReader)}.
     * @param reader reader The reader to get the variables from.
     * @param defaultVariableConditions A (empty) list, where to add the created conditions.
     * @throws ParserException If the conditions contains an operation, that is not supported by the parser.
     */
    private void createDefaultConditions(RSFReader reader, List<ConstraintSyntaxTree> defaultVariableConditions)
        throws ParserException {
        
        if (variable.getDatatype().equals(Datatype.BOOLEAN)) {
            /*
             * Add (NOT variable OR defaultVariable) AND (varaible or NOT defaultVariable) to the list of conditions
             */
            ConstraintSyntaxTree defaultVariable = null;
            if (defaultValue.equals("y") || defaultValue.equals("'y'")) {
                defaultVariable = varPool.obtainVariable(reader.getVariable(variable.getName()));
                
            } else if (defaultValue.equals("n") || defaultValue.equals("'n'")) {
                defaultVariable = varPool.obtainVariable(reader.getVariable(variable.getName()));
                defaultVariable = new OCLFeatureCall(defaultVariable, OclKeyWords.NOT);
                
            } else {
                defaultVariable = getPureBooleanConstraintSyntaxTree(reader, defaultValue, variable.getName());
                if (defaultVariable == null) {
                    defaultVariable = varPool.obtainVariable(reader.getVariable(variable.getName()));
                }
            }
            
            Variable variable = varPool.obtainVariable(reader.getVariable(this.variable.getName()));
            OCLFeatureCall notVariable = new OCLFeatureCall(variable, OclKeyWords.NOT);
            OCLFeatureCall notDefaultVariable = new OCLFeatureCall(defaultVariable, OclKeyWords.NOT);
            
            defaultVariableConditions.add(new OCLFeatureCall(variable, OclKeyWords.OR, notDefaultVariable));
            defaultVariableConditions.add(new OCLFeatureCall(notVariable, OclKeyWords.OR, defaultVariable));
            
        } else {
            /*
             * If not boolean that add add string (or integer) comparison directly
             */
            defaultValue = defaultValue.replace("'", "");
            ConstraintSyntaxTree defaultTree = varPool.obtainVariable(reader.getVariable(variable.getName() + "="
                        + defaultValue));
            defaultVariableConditions.add(defaultTree);
        }
    }

    @Override
    ConstraintSyntaxTree toNotBooleanConstraintSyntaxTree(AbstractReader reader, Enum tristate) throws ParserException {
        // TODO Implement
        return null;
    }

}
