package de.uni_hildesheim.sse.trans.scenario;

import java.io.File;
import java.io.StringWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import de.uni_hildesheim.sse.model.varModel.IvmlKeyWords;
import de.uni_hildesheim.sse.trans.AllTests;
import de.uni_hildesheim.sse.trans.ModelTranslator;
import de.uni_hildesheim.sse.trans.convert.OptimizationParameter;
import de.uni_hildesheim.sse.trans.in.InputType;
import de.uni_hildesheim.sse.trans.out.DimacsWriter;
import de.uni_hildesheim.sse.trans.out.OutputType;

/**
 * Tests the translation of a RSF file into DIMACS format.
 * @author El-Sharkawy
 *
 */
public class RsfToDimacsTranslationTest {
    
    /**
     * Loads the given input file and transforms it into a DIMACS model using the given <tt>optimizations</tt>.
     * @param input The input file to load (must exist).
     * @param optimizations Specification which optimizations shall be applied to the transformed model. Will only be
     *     applied to transformations into CNF.
     * @param removePreamble <tt>true</tt> the preamble (comment, version, and a help information) will be removed
     *     from the beginning of this string). 
     * @return The translated DIMACS model.
     */
    private static String loadModel(File input, OptimizationParameter optimizations, boolean removePreamble) {
        String comment = "A comment";
        String version = "0.1";
        StringWriter sWriter = new StringWriter();
        
        // Test precondition
        Assert.assertTrue(input.exists());
        
        // Translation
        ModelTranslator.translate(input, sWriter, InputType.RSF, OutputType.DIMCAS, comment, version, optimizations);
        String result = sWriter.toString();
        
        // Test correct translation of comment, version, and help
        Assert.assertNotNull(result);
        Assert.assertTrue("Error in preamble.",
            result.startsWith(
                "c " + comment + IvmlKeyWords.LINEFEED
                + "c Version " + version + IvmlKeyWords.LINEFEED
                + DimacsWriter.HELP));
        
        if (removePreamble) {
            result = removePreamble(result);
        }
        
        return result;
    }
    
    /**
     * Removes the preamble of a already translated dimacs model.
     * @param dimacsModel The model where the preamble shall be deleted
     * @return Only the relevant information of the translated dimacs model.
     */
    private static String removePreamble(String dimacsModel) {
        int beginning = dimacsModel.indexOf(DimacsWriter.HELP);
        return dimacsModel.substring(beginning + DimacsWriter.HELP.length());
    }
    
    /**
     * Returns the DIMACs number of the given variable which is used inside the constraints.
     * @param dimacsModel The complete dimacs model
     * @param variable The variable for which the number shall be retrieved.
     * @return The number of the variable used inside the constraints.
     */
    private static int getNumberOfVariable(String dimacsModel, String variable) {
        Pattern dimacsVarDefintion = Pattern.compile("(?m)^c (\\d+) " + variable + "$");
        Matcher m = dimacsVarDefintion.matcher(dimacsModel);
        String result = null;
        if (m.find()) {
            result = m.group(1);
        }
        
        Assert.assertNotNull("Error: Variable not found.", result);
        
        
        return Integer.valueOf(result);
    }
    
    /**
     * Checks whether a given constraint is included in <tt>dimacsModel</tt>.
     * @param dimacsModel The complete dimacs model
     * @param literals positive and negative variable numbers (must not be 0).
     * @return <tt>true</tt> if the constraint is included in <tt>dimacsModel</tt>.
     */
    private static boolean containsConstraint(String dimacsModel, int... literals) {
        StringBuffer constraintDef = new StringBuffer("(?m)^(");
        constraintDef.append(literals[0]);
        constraintDef.append(" ");
        for (int i = 1; i < literals.length; i++) {
            constraintDef.append("|");
            constraintDef.append(literals[i]);
            constraintDef.append(" ");
        }
        constraintDef.append("){");
        constraintDef.append(literals.length);
        constraintDef.append("}0$");
        
        Pattern constraintDefinition = Pattern.compile(constraintDef.toString());
        Matcher m = constraintDefinition.matcher(dimacsModel);
        return m.find();
    }
    
    /**
     * Tests whether Strings are translated correctly.
     */
    @Test
    public void testEmptyStrings() {
        File input = new File(AllTests.INPUT_FOLDER, "testModel_emptyString.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();

        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = loadModel(input, noOptimization, true);
        //System.out.println(result);
        
        // Test correct translation
        // TODO SE: test correct translation of strings
    }
    
    /**
     * Tests whether ItemSelect statements are translated correctly into CNF constraints.
     */
    @Test
    public void testItemSelectStatements() {
        File input = new File(AllTests.INPUT_FOLDER, "testModel_ItemSelects.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = loadModel(input, noOptimization, true);
        System.out.println(result);
        int iwc3200top = getNumberOfVariable(result, "IWMC3200TOP") * -1;
        int fwLoader = getNumberOfVariable(result, "FW_LOADER");
        int blubb = getNumberOfVariable(result, "BLUBB") * -1;
        
        /* Test whether following constraint is included:
         * Not(IWMC3200TOP AND BLUBB) or FW_LOADER
         * -->
         * Not(IWMC3200TOP) OR NOT(BLUBB) or FW_LOADER
         */
        Assert.assertTrue("Error: Expected Constraint not included.",
            containsConstraint(result, iwc3200top, blubb, fwLoader));
    }

}
