package de.uni_hildesheim.sse.trans.scenario;

import java.io.File;

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

import de.uni_hildesheim.sse.trans.AllTests;
import de.uni_hildesheim.sse.trans.DimacsTestUtils;
import de.uni_hildesheim.sse.trans.convert.OptimizationParameter;

/**
 * Tests the translation of a RSF file into DIMACS format.
 * @author El-Sharkawy
 *
 */
public class RsfToDimacsTranslationTest {
    
    /**
     * Tests whether Strings are translated correctly.
     * This tests also includes the validation of the correct
     * translation of depends constrains.
     * A depends relation "A depends B" will be translated to "Not(B) implies Not(A)". 
     */
    @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 = DimacsTestUtils.loadModel(input, noOptimization, true);
        int fwLoader = DimacsTestUtils.getNumberOfVariable(result, "FW_LOADER");
        int extraFirmware = DimacsTestUtils.getNumberOfVariable(result, "EXTRA_FIRMWARE");
        int extraFirmwareDir = DimacsTestUtils.getNumberOfVariable(result, "EXTRA_FIRMWARE_DIR");
        
        /*
         * Test whether following constraints are included:
         * - Depends constraints:
         *  ° EXTRA_FIRMWARE depends FW_LOADER
         *  ° EXTRA_FIRMWARE_DIR depends EXTRA_FIRMWARE!=''
         * - Translated DIMACS constraints
         *  ° FW_LOADER OR NOT(EXTRA_FIRMWARE)
         *  ° EXTRA_FIRMWARE OR NOT(EXTRA_FIRMWARE_DIRs)
         */
        Assert.assertTrue("Error: Expected Constraint not included.",
            DimacsTestUtils.containsConstraint(result, fwLoader, -1 * extraFirmware));
        Assert.assertTrue("Error: Expected Constraint not included.",
            DimacsTestUtils.containsConstraint(result, extraFirmware, -1 * extraFirmwareDir));
    }
    
    /**
     * 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 = DimacsTestUtils.loadModel(input, noOptimization, true);
        int iwc3200top = DimacsTestUtils.getNumberOfVariable(result, "IWMC3200TOP") * -1;
        int fwLoader = DimacsTestUtils.getNumberOfVariable(result, "FW_LOADER");
        int blubb = DimacsTestUtils.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.",
            DimacsTestUtils.containsConstraint(result, iwc3200top, blubb, fwLoader));
    }
    
    /**
     * Tests whether the combination of prompts and default values are considered. Tests:
     * <ul>
     * <li>Variables with default and no prompt will create a constraint for the default</li>
     * <li>Testing of defaults with conditions</li>
     * <li>Testing of prompts with conditions</li>
     * <li>Testing negation of string variables</li>
     * </ul>
     */
    @Test
    @Ignore("TODO")
    public void testDefaultsAndPrompts() {
        File input = new File(AllTests.INPUT_FOLDER, "testModel_DefaultAndPrompts.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = DimacsTestUtils.loadModel(input, noOptimization, true);
        System.out.println(result);
        int arch32 = DimacsTestUtils.getNumberOfVariable(result, "ARCH=x86");
        int arch64 = DimacsTestUtils.getNumberOfVariable(result, "ARCH=x86_64");
        int bit = DimacsTestUtils.getNumberOfVariable(result, "64BIT");
        
        /*
         * Test whether following 3 constraints are included:
         * - ARCH='x86_64'
         * - ARCH='x86_64' implies 64BIT
         *   -> Not(ARCH='x86_64') or 64BIT
         * - ARCH='x86' implies (64BIT or !64BIT)
         *   -> tautology -> skip this constraint
         * - (!ARCH='x86_64' and !ARCH='x86') implies !64Bit (combination of prompt and default)
         *   -> ARCH='x86_64' or ARCH='x86' or !64BIT
         */
        Assert.assertTrue("Error: ARCH='x86_64' constant not included",
            DimacsTestUtils.containsConstraint(result, arch64));
        Assert.assertTrue("Error: 64Bit default not included",
            DimacsTestUtils.containsConstraint(result, -1 * arch64, bit));
        Assert.assertTrue("Error: 64Bit default and prompt not included",
            DimacsTestUtils.containsConstraint(result, arch32, arch64, -1 * bit));
    }
    
    /**
     * Tests whether choices are translated correctly into CNF constraints.
     */
    @Test
    public void testChoices() {
        File input = new File(AllTests.INPUT_FOLDER, "testModel_Choices.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = DimacsTestUtils.loadModel(input, noOptimization, true);
        int choice12 = DimacsTestUtils.getNumberOfVariable(result, "CHOICE_12");
        int hz100 = DimacsTestUtils.getNumberOfVariable(result, "HZ_100");
        int hz250 = DimacsTestUtils.getNumberOfVariable(result, "HZ_250");
        int hz300 = DimacsTestUtils.getNumberOfVariable(result, "HZ_300");
        int hz1000 = DimacsTestUtils.getNumberOfVariable(result, "HZ_1000");
        
        /*
         * Test whether the following constraints are included:
         * NOT(CHOICE_12) OR NOT(HZ_100) OR NOT(HZ_250)
         * NOT(CHOICE_12) OR NOT(HZ_100) OR NOT(HZ_300)
         * NOT(CHOICE_12) OR NOT(HZ_100) OR NOT(HZ_1000)
         * (skip other combinations)
         * NOT(CHOICE_12) OR HZ_100 OR HZ_250 OR HZ_300 OR HZ_1000
         */
        Assert.assertTrue("Error: Missing choice constraint",
            DimacsTestUtils.containsConstraint(result, -1 * choice12, -1 * hz100, -1 * hz250));
        Assert.assertTrue("Error: Missing choice constraint",
            DimacsTestUtils.containsConstraint(result, -1 * choice12, -1 * hz100, -1 * hz300));
        Assert.assertTrue("Error: Missing choice constraint",
            DimacsTestUtils.containsConstraint(result, -1 * choice12, -1 * hz100, -1 * hz1000));
        Assert.assertTrue("Error: Missing choice constraint",
            DimacsTestUtils.containsConstraint(result, -1 * choice12, hz100, hz250, hz300, hz1000));
    }
    
    /**
     * Tests whether a variable can have more than one depends statement.
     * All depends statements must be combined using an OR expression, e.g.:
     * <pre><code>
     * A depends B
     * A depends C
     * </code></pre>
     * must be translated to
     * <pre><code>
     * A depends B OR C
     * </code></pre>
     */
    @Ignore()
    @Test
    public void testMultipleDepends() {
        File input = new File(AllTests.INPUT_FOLDER, "MultipleDependsTest.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = DimacsTestUtils.loadModel(input, noOptimization, true);
        int x32 = DimacsTestUtils.getNumberOfVariable(result, "X86_32");
        int x64 = DimacsTestUtils.getNumberOfVariable(result, "X86_64");
        int extendend = DimacsTestUtils.getNumberOfVariable(result, "X86_EXTENDED_PLATFORM");
        
        /*
         * Test whether the following constraints are included:
         * - NOT(X86_32) OR NOT(X86_64)
         * - X86_EXTENDED_PLATFORM implies X86_32 OR X86_64
         *  -> NOT(X86_EXTENDED_PLATFORM) OR X86_32 OR X86_64
         */
        Assert.assertTrue("Error, missing: NOT(X86_32) OR NOT(X86_64)",
            DimacsTestUtils.containsConstraint(result, -1 * x32, -1 * x64));
        Assert.assertFalse("Error, disallowed constraitn found: NOT(X86_EXTENDED_PLATFORM) OR X86_32",
            DimacsTestUtils.containsConstraint(result, -1 * extendend, x32));
        Assert.assertFalse("Error, disallowed constraitn found: NOT(X86_EXTENDED_PLATFORM) OR X86_64",
            DimacsTestUtils.containsConstraint(result, -1 * extendend, x64));
        Assert.assertTrue("Error, missing: NOT(X86_EXTENDED_PLATFORM) OR X86_32 OR X86_64",
            DimacsTestUtils.containsConstraint(result, -1 * extendend, x32, x64));
    }

    /**
     * Tests whether prompt and default will not lead to a constraint.
     */
    @Test
    public void testPromptAndDefault() {
        File input = new File(AllTests.INPUT_FOLDER, "PromptAndDefaultTest.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = DimacsTestUtils.loadModel(input, noOptimization, true);
        
        // Model has one variable with prompt and default -> Thus, model must not contain a constraint
        Assert.assertTrue("Error; Model must not contain any constraints!", result.contains("p CNF 1 0"));
    }
    
    /**
     * Tests whether a default value becomes true when a prompt disappears.
     */
    @Ignore("Not finished")
    @Test
    public void testConditionalPromptAndDefault() {
        File input = new File(AllTests.INPUT_FOLDER, "ConditionalPromptAndDefaultTest.rsf");
        OptimizationParameter noOptimization = new OptimizationParameter();
        
        // Test precondition
        Assert.assertFalse(noOptimization.hasAtLeastOneOption());
        
        // Translation
        String result = DimacsTestUtils.loadModel(input, noOptimization, true);
        System.out.println(result);
    }
}
