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.AbstractVariable;
import de.uni_hildesheim.sse.model.varModel.DecisionVariableDeclaration;
import de.uni_hildesheim.sse.model.varModel.datatypes.Enum;
import de.uni_hildesheim.sse.model.varModel.datatypes.OclKeyWords;
import de.uni_hildesheim.sse.model.varModel.filter.DeclrationInConstraintFinder;
import de.uni_hildesheim.sse.trans.in.AbstractReader;
import de.uni_hildesheim.sse.trans.in.ParserException;
import de.uni_hildesheim.sse.utils.logger.EASyLoggerFactory;
import de.uni_hildesheim.sse.utils.logger.EASyLoggerFactory.EASyLogger;

/**
 * Represents a condition read from a "Depends" line in a .rsf file.
 * The format looks like this: <br />
 * <code>
 * Depends <i>variable</i> <i>condition</i>
 * </code> <br />
 * <i>variable</i> can only be true, if <i>condition</i> is true. <br />
 * {@link RSFDependsCondition#getPureBooleanConstraintSyntaxTree(AbstractReader, String, String)} will convert it into:
 * <br />
 * <code>
 * NOT <i>variable</i> OR <i>condition</i>
 * </code> <br />
 * If condition contains a tristate variable and <i>variable</i> is a tristate variable, the following condition will
 * be added: <br />
 * <code>
 * NOT <i>variable_MODULE</i> OR <i>condition_variable_MODULE</i>
 * </code> <br />
 * TODO: what if it contains several tristate variables?
 * 
 * @author Adam Krafczyk
 */
class RSFDependsCondition extends RSFCondition {
    private static final EASyLogger LOGGER = EASyLoggerFactory.INSTANCE.getLogger(RSFDependsCondition.class,
            "RSFDependsCondition");

    private String variable;
    private String condition;
    
    /**
     * Creates a condition with the given variable and condition as read from a "Depends" line in a .rsf file.
     * @param variable The name of the variable
     * @param condition The condition as read from the .rsf file
     */
    RSFDependsCondition(String variable, String condition) {
        this.variable = variable;
        this.condition = condition;
        // TODO error handling if only one is true?
        if (this.condition.startsWith("\"") && this.condition.endsWith("\"")) {
            this.condition = this.condition.substring(1, this.condition.length() - 1);
        }
    }
    
    @Override
    List<ConstraintSyntaxTree> toPureBooleanConstraintSyntaxTree(RSFReader reader) throws ParserException {
        DecisionVariableDeclaration varDecl = reader.getVariable(variable);
        OCLFeatureCall notVar = new OCLFeatureCall(varPool.obtainVariable(varDecl), OclKeyWords.NOT);
        
        ConstraintSyntaxTree condition = getPureBooleanConstraintSyntaxTree(reader, this.condition, variable);
        
        List<ConstraintSyntaxTree> result  = new ArrayList<ConstraintSyntaxTree>();
        
        if (condition != null) {
            result.add(new OCLFeatureCall(notVar, OclKeyWords.OR, condition));
            
            List<RSFItem> tristates = new ArrayList<RSFItem>();
            DeclrationInConstraintFinder declFinder = new DeclrationInConstraintFinder(condition);
            for (AbstractVariable var : declFinder.getDeclarations()) {
                RSFItem varItem = reader.getItemNoCreate(var.getName());
                if (varItem != null && varItem.getDatatype().equals(Datatype.TRISTATE)) {
                    tristates.add(varItem);
                }
            }
            
            if (tristates.size() > 1) {
                // TODO: what to do?
                LOGGER.debug("More than 1 tristate in Depends condition");
            } else if (tristates.size() == 1) {
                RSFItem item = reader.getItemNoCreate(variable);
                if (item.getDatatype().equals(Datatype.TRISTATE)) {
                    DecisionVariableDeclaration moduleVarDecl = reader.getVariable(variable + "_MODULE");
                    Variable moduleVar = varPool.obtainVariable(moduleVarDecl);
                    OCLFeatureCall notModuleVar = new OCLFeatureCall(moduleVar, OclKeyWords.NOT);
                    
                    DecisionVariableDeclaration conditionModuleVarDecl = reader.getVariable(tristates.get(0).getName()
                            + "_MODULE");
                    Variable conditionModuleVar = varPool.obtainVariable(conditionModuleVarDecl);
                    
                    result.add(new OCLFeatureCall(notModuleVar, OclKeyWords.OR, conditionModuleVar));
                } else {
                    // TODO: what to do?
                    LOGGER.debug("Tristate variables in Depends condition but variable is not a tristate");
                }
            }
        }
        
        return result;
    }
    
    @Override
    ConstraintSyntaxTree toNotBooleanConstraintSyntaxTree(AbstractReader reader, Enum tristate) throws ParserException {
        // TODO implement
        return null;
    }
    
}
