//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.plugin.mfc;

import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.junit.Test;

import de.ugoe.cs.autoquest.plugin.mfc.EventGenerationRule;
import de.ugoe.cs.autoquest.plugin.mfc.EventGenerationRule.Term;
import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
import de.ugoe.cs.util.console.Console;

/**
 * @author Patrick Harms
 */
public class EventGenerationRuleTest {

    /**
     * 
     */
    Namespace rulesNamespace = null;
    
    /**
     *
     */
    @Test
    public void testRule1() {
        String ruleXML =
            "<rule name='LeftClickButton'>\n" +
            "  <msg type='&WM_LBUTTONDOWN;'>\n" +
            "    <store var='clicked'/>\n" +
            "  </msg>\n" +
            "  <msg type='&WM_LBUTTONUP;'>\n" +
            "    <equals>\n" +
            "      <constValue value='Button'/>\n" +
            "      <winInfoValue obj='this' winParam='class'/>\n" +
            "    </equals>\n" +
            "    <equals>\n" +
            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
            "      <paramValue obj='this' param='window.hwnd'/>\n" +
            "    </equals>\n" +
            "  </msg>\n" +
            "  <genMsg delay='500'>\n" +
            "    <type>\n" +
            "      <constValue value='&BM_CLICK;'/>\n" +
            "    </type>\n" +
            "    <target>\n" +
            "      <msgInfoValue obj='clicked' msgParam='target'/>\n" +
            "    </target>\n" +
            "  </genMsg>\n" +
            "</rule>";
        
        Element ruleElement = getRuleElement(ruleXML);
        
        EventGenerationRule rule = new EventGenerationRule(ruleElement, rulesNamespace);
        
        assertEquals("LeftClickButton", rule.getName());
        
        assertNotNull(rule.getMessageConditions());
        assertEquals(2, rule.getMessageConditions().size());
        
        // check condition 1
        EventGenerationRule.MessageCondition condition1 = rule.getMessageConditions().get(0);
        assertEquals(WindowsMessageType.WM_LBUTTONDOWN, condition1.getMessageType());
        assertFalse(condition1.matchMultiple());
        assertNotNull(condition1.getAttributeConditions());
        assertEquals(0, condition1.getAttributeConditions().size());
        assertNotNull(condition1.getMessagesToStore());
        assertEquals(1, condition1.getMessagesToStore().size());
        
        assertTerm(condition1.getMessagesToStore().get(0), "store", "clicked", null, null,
                   null, null, null, null, null, null);

        // check condition 2
        EventGenerationRule.MessageCondition condition2 = rule.getMessageConditions().get(1);
        assertEquals(WindowsMessageType.WM_LBUTTONUP, condition2.getMessageType());
        assertFalse(condition2.matchMultiple());
        assertNotNull(condition2.getAttributeConditions());
        assertEquals(2, condition2.getAttributeConditions().size());
        assertNotNull(condition2.getMessagesToStore());
        assertEquals(0, condition2.getMessagesToStore().size());
        
        EventGenerationRule.AttributeCondition attrCond1 =
            condition2.getAttributeConditions().get(0);
        
        assertTerm(attrCond1.getLeftHandSide(), "constValue", null, null, null,
                   null, null, null, null, "Button", null);

        assertTerm(attrCond1.getRightHandSide(), "winInfoValue", "this", null, null,
                   null, null, null, null, null, "class");

        EventGenerationRule.AttributeCondition attrCond2 =
            condition2.getAttributeConditions().get(1);
            
        assertTerm(attrCond2.getLeftHandSide(), "paramValue", "clicked", null, "window.hwnd",
                   null, null, null, null, null, null);

        assertTerm(attrCond2.getRightHandSide(), "paramValue", "this", null, "window.hwnd",
                   null, null, null, null, null, null);

        // check event parameters
        assertNotNull(rule.getEventParameters());
        assertEquals(0, rule.getEventParameters().size());
        
        // check replay specs
        assertNotNull(rule.getReplayMessageSpecifications());
        assertEquals(1, rule.getReplayMessageSpecifications().size());
        
        // check replay spec 1
        EventGenerationRule.ReplayMessageSpec replaySpec =
            rule.getReplayMessageSpecifications().get(0);
        
        assertNotNull(replaySpec);
        assertEquals(500, replaySpec.getDelay());
        assertTrue(replaySpec.generateSingleMessage());
        assertNull(replaySpec.getLparam());
        assertNull(replaySpec.getLparamHiWord());
        assertNull(replaySpec.getLparamLoWord());
        assertNull(replaySpec.getReplayObjectId());
        assertTerm(replaySpec.getTarget(), "msgInfoValue", "clicked", "target", null,
                   null, null, null, null, null, null);
        assertTerm(replaySpec.getType(), "constValue", null, null, null,
                   null, null, null, null, "" + WindowsMessageType.BM_CLICK.getNumber(), null);
        assertNull(replaySpec.getWparam());
        assertNull(replaySpec.getWparamHiWord());
        assertNull(replaySpec.getWparamLoWord());
        
    }

    /**
     *
     */
    @Test
    public void testRule2() {
        
        String ruleXML =
            "<rule name='HScroll_TrackBar'>\n" +
            "  <msg type='&WM_LBUTTONDOWN;'>\n" +
            "    <equals>\n" +
            "      <winInfoValue obj='this' winParam='class'/>\n" +
            "      <constValue value='msctls_trackbar32'/>\n" +
            "    </equals>\n" +
            "    <store var='clicked'/>\n" +
            "  </msg>\n" +
            "  <msg type='&WM_HSCROLL;' multiple='true'>\n" +
            "    <equals>\n" +
            "      <paramValue obj='this' param='scrollBarHandle'/>\n" +
            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
            "    </equals>\n" +
            "    <storeSeq varSeq='scrolls'>\n" +
            "      <resolveHwnd param='scrollBarHandle' storeParam='scrollBarTarget'/>\n" +
            "    </storeSeq>\n" +
            "  </msg>\n" +
            "  <msg type='&WM_LBUTTONUP;'>\n" +
            "    <equals>\n" +
            "      <paramValue obj='this' param='window.hwnd'/>\n" +
            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
            "    </equals>\n" +
            "  </msg>\n" +
            "  <genMsgSeq delay='50'>\n" +
            "    <type>\n" +
            "      <constValue value='&TBM_SETPOS;'/>\n" +
            "    </type>\n" +
            "    <target>\n" +
            "      <seqValue seqObj='scrolls' param='scrollBarTarget'/>\n" +
            "    </target>\n" +
            "    <LPARAM>\n" +
            "      <seqValue seqObj='scrolls' param='scrollPos'/>\n" +
            "    </LPARAM>\n" +
            "    <WPARAM>\n" +
            "      <constValue value='1'/>\n" +
            "    </WPARAM>\n" +
            "  </genMsgSeq>\n" +
            "</rule>\n";
        
        Element ruleElement = getRuleElement(ruleXML);
        
        EventGenerationRule rule = new EventGenerationRule(ruleElement, rulesNamespace);
        
        assertEquals("HScroll_TrackBar", rule.getName());
        
        assertNotNull(rule.getMessageConditions());
        assertEquals(3, rule.getMessageConditions().size());
        
        // check condition 1
        EventGenerationRule.MessageCondition condition1 = rule.getMessageConditions().get(0);
        assertEquals(WindowsMessageType.WM_LBUTTONDOWN, condition1.getMessageType());
        assertFalse(condition1.matchMultiple());
        assertNotNull(condition1.getAttributeConditions());
        assertEquals(1, condition1.getAttributeConditions().size());
        EventGenerationRule.AttributeCondition attrCond1 =
            condition1.getAttributeConditions().get(0);
            
        assertTerm(attrCond1.getLeftHandSide(), "winInfoValue", "this", null, null,
                   null, null, null, null, null, "class");

        assertTerm(attrCond1.getRightHandSide(), "constValue", null, null, null,
                   null, null, null, null, "msctls_trackbar32", null);

        assertNotNull(condition1.getMessagesToStore());
        assertEquals(1, condition1.getMessagesToStore().size());
        
        assertTerm(condition1.getMessagesToStore().get(0), "store", "clicked", null, null,
                   null, null, null, null, null, null);

        // check condition 2
        EventGenerationRule.MessageCondition condition2 = rule.getMessageConditions().get(1);
        assertEquals(WindowsMessageType.WM_HSCROLL, condition2.getMessageType());
        assertTrue(condition2.matchMultiple());
        assertNotNull(condition2.getAttributeConditions());
        assertEquals(1, condition2.getAttributeConditions().size());
        
        EventGenerationRule.AttributeCondition attrCond2 =
                condition2.getAttributeConditions().get(0);
        
        assertTerm(attrCond2.getLeftHandSide(), "paramValue", "this", null, "scrollBarHandle",
                   null, null, null, null, null, null);

        assertTerm(attrCond2.getRightHandSide(), "paramValue", "clicked", null, "window.hwnd",
                   null, null, null, null, null, null);


        assertNotNull(condition2.getMessagesToStore());
        assertEquals(1, condition2.getMessagesToStore().size());
        
        String[][] resolveHwnd = { { "scrollBarHandle",  "scrollBarTarget" } };
        assertTerm(condition2.getMessagesToStore().get(0), "storeSeq", null, null, null,
                   resolveHwnd, "scrolls", null, null, null, null);

        // check condition 3
        EventGenerationRule.MessageCondition condition3 = rule.getMessageConditions().get(2);
        assertEquals(WindowsMessageType.WM_LBUTTONUP, condition3.getMessageType());
        assertFalse(condition3.matchMultiple());
        assertNotNull(condition3.getAttributeConditions());
        assertEquals(1, condition3.getAttributeConditions().size());
        assertNotNull(condition3.getMessagesToStore());
        assertEquals(0, condition3.getMessagesToStore().size());
        
        EventGenerationRule.AttributeCondition attrCond3 =
            condition3.getAttributeConditions().get(0);
        
        assertTerm(attrCond3.getLeftHandSide(), "paramValue", "this", null, "window.hwnd",
                   null, null, null, null, null, null);

        assertTerm(attrCond3.getRightHandSide(), "paramValue", "clicked", null, "window.hwnd",
                   null, null, null, null, null, null);

        // check event parameters
        assertNotNull(rule.getEventParameters());
        assertEquals(0, rule.getEventParameters().size());
        
        // check replay specs
        assertNotNull(rule.getReplayMessageSpecifications());
        assertEquals(1, rule.getReplayMessageSpecifications().size());
        
        // check replay spec 1
        EventGenerationRule.ReplayMessageSpec replaySpec =
            rule.getReplayMessageSpecifications().get(0);
        
        assertNotNull(replaySpec);
        assertEquals(50, replaySpec.getDelay());
        assertFalse(replaySpec.generateSingleMessage());
        assertTerm(replaySpec.getLparam(), "seqValue", null, null, null,
                   null, "scrolls", "scrollPos", null, null, null);
        assertNull(replaySpec.getLparamHiWord());
        assertNull(replaySpec.getLparamLoWord());
        assertEquals("scrolls", replaySpec.getReplayObjectId());
        assertTerm(replaySpec.getTarget(), "seqValue", null, null, null,
                   null, "scrolls", "scrollBarTarget", null, null, null);
        assertTerm(replaySpec.getType(), "constValue", null, null, null,
                   null, null, null, null, "" + WindowsMessageType.TBM_SETPOS.getNumber(), null);
        assertTerm(replaySpec.getWparam(), "constValue", null, null, null,
                   null, null, null, null, "1", null);
        assertNull(replaySpec.getWparamHiWord());
        assertNull(replaySpec.getWparamLoWord());
        
    }
    
    /**
     *
     */
    private void assertTerm(Term       term,
                            String     name,
                            String     messageId,
                            String     messageInfoName,
                            String     messageParameterName,
                            String[][] resolveHandles,
                            String     sequenceId,
                            String     sequenceParameterName,
                            String     storeParameterName,
                            String     value,
                            String     windowParameterName)
    {
        assertNotNull(term);
        
        if (name != null) {
            assertEquals("name does not match", name, term.getName());
        }
        else {
            assertNull("name is not null", term.getName());
        }
        
        if (messageId != null) {
            assertEquals("messageId does not match", messageId, term.getMessageId());
        }
        else {
            assertNull("messageId is not null", term.getMessageId());
        }
        
        if (messageInfoName != null) {
            assertEquals
                ("messageInfoName does not match", messageInfoName, term.getMessageInfoName());
        }
        else {
            assertNull("messageInfoName is not null", term.getMessageInfoName());
        }

        if (messageParameterName != null) {
            assertEquals("messageParameterName does not match", messageParameterName,
                         term.getMessageParameterName());
        }
        else {
            assertNull("messageParameterName is not null", term.getMessageParameterName());
        }
        
        if (resolveHandles != null) {
            assertEquals("resolveHandles does not match", resolveHandles.length,
                         term.getResolveHandles().size());
            
            for (int i = 0; i < resolveHandles.length; i++) {
                assertTerm(term.getResolveHandles().get(i), "resolveHwnd", null, null,
                           resolveHandles[i][0], null, null, null, resolveHandles[i][1], null, null);
            }
        }
        else {
            assertNull("resolveHandles is not null", term.getResolveHandles());
        }
        
        if (sequenceId != null) {
            assertEquals("sequenceId does not match", sequenceId, term.getSequenceId());
        }
        else {
            assertNull("sequenceId is not null", term.getSequenceId());
        }
        
        if (sequenceParameterName != null) {
            assertEquals("sequenceParameter does not match", sequenceParameterName,
                         term.getSequenceParameterName());
        }
        else {
            assertNull("sequenceParameter is not null", term.getSequenceParameterName());
        }
        
        if (storeParameterName != null) {
            assertEquals("storeParameterName does not match",
                         storeParameterName, term.getStoreParameterName());
        }
        else {
            assertNull("storeParameterName is not null", term.getStoreParameterName());
        }
        
        if (value != null) {
            assertEquals("value does not match", value, term.getValue());
        }
        else {
            assertNull("value is not null", term.getValue());
        }
        
        if (windowParameterName != null) {
            assertEquals("windowParameterName does not match", windowParameterName,
                         term.getWindowParameterName());
        }
        else {
            assertNull("windowParameterName is not null", term.getWindowParameterName());
        }
    }

    /**
     *
     */
    private Element getRuleElement(String ruleXML) {
        String fullXML =
            "<?xml version='1.0' encoding='utf-8'?>\n" +
            "<!DOCTYPE rules SYSTEM 'data/ruleDoctype.dtd'>\n" +
            "<rules xmlns='ul:rules' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
            "                        xsi:schemaLocation='ul:rules ruleSchema.xsd'>\n";
        
        fullXML += ruleXML;
        fullXML += "\n</rules>\n";

        SAXBuilder builder = new SAXBuilder();
        Document doc = null;

        try {
            ByteArrayInputStream in = new ByteArrayInputStream(fullXML.getBytes("UTF-8"));
            doc = builder.build(in);
            rulesNamespace = Namespace.getNamespace("ul:rules");
        }
        catch (JDOMException e) {
            Console.printerrln("Invalid rules file.");
            e.printStackTrace();
        }
        catch (IOException e) {
            Console.printerrln("Invalid rules file.");
            e.printStackTrace();
        }

        Element rulesRoot = doc.getRootElement();
        
        @SuppressWarnings("unchecked")
        List<Element> ruleElements = rulesRoot.getChildren("rule", rulesNamespace);
        
        assertEquals(1, ruleElements.size());

        return ruleElements.get(0);
    }

}
