//   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.usability.evaluation.rule.evaluator;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.Before;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
import de.ugoe.cs.autoquest.test.DummyGUIElement;
import de.ugoe.cs.autoquest.test.DummyInteraction;
import de.ugoe.cs.autoquest.test.DummyTextField;
import de.ugoe.cs.autoquest.usability.UsabilityDefect;
import de.ugoe.cs.autoquest.usability.UsabilityEvaluationResult;
import de.ugoe.cs.util.console.TextConsole;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 18.07.2012$
 * @author 2012, last modified by $Author: pharms$
 */
public class AbstractUsabilityEvaluationTC {

    /** */
    private ITaskTreeBuilder taskTreeBuilder = new TaskTreeBuilder();

    /** */
    private ITaskTreeNodeFactory taskTreeNodeFactory = new TaskTreeNodeFactory();

    /**
   *
   */
    @Before
    public void setUp() {
        new TextConsole(Level.FINEST);
    }

    /**
   *
   */
    protected ITaskTree createTaskTree(String spec) {
        Matcher matcher =
            Pattern.compile("(\\s*(\\w+)\\s*(\\(((\\w*\\s*)*)\\))?\\s*(\\{))|(\\})").matcher(spec);

        if (!matcher.find()) {
            if (!matcher.hitEnd()) {
                throw new IllegalArgumentException("could not parse task specification");
            }
        }

        return taskTreeNodeFactory.createTaskTree(parseTask(matcher, new int[1]));
    }

    /**
     * TODO: comment
     * 
     * @param expectedDefects
     * @param evaluateUsability
     */
    protected void assertUsabilityEvaluationResult(UsabilityDefect[] expectedDefects,
                                                   UsabilityEvaluationResult evaluationResult)
    {
        assertEquals(expectedDefects.length, evaluationResult.getAllDefects().size());

        EXPECTED_DEFECT_ITERATION: for (UsabilityDefect expectedDefect : expectedDefects) {
            for (UsabilityDefect defect : evaluationResult.getAllDefects()) {
                if (expectedDefect.equals(defect)) {
                    System.err.println(defect.getParameterizedDescription());
                    continue EXPECTED_DEFECT_ITERATION;
                }
            }

            for (UsabilityDefect defect : evaluationResult.getAllDefects()) {
                System.err.println(defect);
            }

            fail("expected defect " + expectedDefect + " not found in evaluation result");
        }
    }

    /**
   * 
   */
    private ITaskTreeNode parseTask(Matcher tokenMatcher, int[] typeNumbers) {
        String firstToken = tokenMatcher.group();

        if ("}".equals(firstToken)) {
            throw new IllegalArgumentException("found a closing bracket at an unexpected place");
        }

        ITaskTreeNode treeNode = instantiateTreeNode(tokenMatcher, typeNumbers);

        if (!tokenMatcher.find()) {
            throw new IllegalArgumentException("could not parse task specification");
        }

        firstToken = tokenMatcher.group();

        if (!"}".equals(firstToken)) {
            ITaskTreeNode child = null;

            do {
                child = parseTask(tokenMatcher, typeNumbers);

                if (child != null) {
                    addChild(treeNode, child);

                    if (!tokenMatcher.find()) {
                        throw new IllegalArgumentException("could not parse task specification");
                    }

                    firstToken = tokenMatcher.group();

                    if ("}".equals(firstToken)) {
                        break;
                    }
                }

            }
            while (child != null);

        }

        return treeNode;
    }

    /**
     * TODO: comment
     * 
     * @param group
     * @return
     */
    private ITaskTreeNode instantiateTreeNode(Matcher tokenMatcher, int[] typeNumbers) {
        String type = tokenMatcher.group(2);
        String additionalInfo = tokenMatcher.group(4);

        if ("Interaction".equals(type)) {
            return taskTreeNodeFactory.createNewEventTask(new DummyInteraction("dummy",
                                                                               typeNumbers[0]++),
                                                          new DummyGUIElement("dummy"));
        }
        else if ("Sequence".equals(type)) {
            return taskTreeNodeFactory.createNewSequence();
        }
        else if ("Iteration".equals(type)) {
            return taskTreeNodeFactory.createNewIteration();
        }
        else if ("Selection".equals(type)) {
            return taskTreeNodeFactory.createNewSelection();
        }
        else if ("TextInput".equals(type)) {
            if (additionalInfo == null) {
                fail("no simulated text provided for text input interactin task");
            }

            TextInput textInput = new TextInput(additionalInfo, new ArrayList<Event>());

            IEventTask task =
                taskTreeNodeFactory.createNewEventTask(textInput,
                                                       new DummyTextField(additionalInfo));

            return task;
        }
        else {
            fail("invalid type of task tree node: " + type);
            return null;
        }
    }

    /**
     * TODO: comment
     * 
     * @param treeNode
     * @param child
     */
    private void addChild(ITaskTreeNode parent, ITaskTreeNode child) {
        if (parent instanceof ISequence) {
            taskTreeBuilder.addChild((ISequence) parent, child);
        }
        else if (parent instanceof IIteration) {
            if (parent.getChildren().size() <= 0) {
                taskTreeBuilder.setChild((IIteration) parent, child);
            }
            else {
                fail("can not add more than one child to an iteration");
            }
        }
        else if (parent instanceof ISelection) {
            taskTreeBuilder.addChild((ISelection) parent, child);
        }
        else {
            fail("can not add children to parent task tree node of type " +
                parent.getClass().getName());
        }
    }

}
