//   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.tasktrees;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.StringEventType;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
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.test.DummyGUIElement;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 01.04.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class TaskTreeInstantiator {
    
    /** */
    private static Pattern taskPattern = Pattern.compile("([^{}]+)\\{|\\}");
    
    /** */
    private static Pattern taskDetailsPattern =
        Pattern.compile("\\s*(\\w*)\\s*([\\w\\(\\)\"]*)\\s*((\\w*)|(\".*\"))?");
    
    /** */
    private ITaskTreeNodeFactory taskTreeNodeFactory;
    
    /** */
    private ITaskTreeBuilder taskTreeBuilder;
    
    /** */
    Map<String, IEventTarget> targets = new HashMap<String, IEventTarget>();
    
    /**
     *
     */
    public TaskTreeInstantiator(ITaskTreeNodeFactory taskTreeNodeFactory,
                                ITaskTreeBuilder taskTreeBuilder)
    {
        super();
        this.taskTreeNodeFactory = taskTreeNodeFactory;
        this.taskTreeBuilder = taskTreeBuilder;
    }

    /**
     *
     */
    public ITaskTreeNode instantiateTaskTree(String taskTreeSpec) {
        ITaskTreeNode task = null;

        Matcher taskMatcher = taskPattern.matcher(taskTreeSpec);

        if (taskMatcher.find()) {
            task = parseTask(taskMatcher);
        }
        
        if (taskMatcher.find()) {
            throw new IllegalArgumentException("too many tasks specified");
        }
        
        return task;
    }

    /**
     * 
     */
    private ITaskTreeNode parseTask(Matcher taskMatcher) {
        if ("}".equals(taskMatcher.group(1))) {
            throw new IllegalArgumentException("invalid task specification");
        }
        
        String taskDetails = taskMatcher.group(1);
        
        Matcher matcher = taskDetailsPattern.matcher(taskDetails);
        
        if (!matcher.find()) {
            throw new IllegalArgumentException("could not parse task details");
        }

        ITaskTreeNode task;
        
        String type = matcher.group(1);
        
        String targetId = matcher.group(2);
        if ((matcher.group(4) != null) && (!"".equals(matcher.group(4).trim()))) {
            targetId += matcher.group(4).trim();
        }
        
        IEventTarget target = targets.get(targetId);
        
        if (target == null) {
            target = new DummyGUIElement(targetId);
            targets.put(targetId, target);
        }
        
        if ("Sequence".equals(type)) {
            task = taskTreeNodeFactory.createNewSequence();
        }
        else if ("Selection".equals(type)) {
            task = taskTreeNodeFactory.createNewSelection();
        }
        else if ("Iteration".equals(type)) {
            task = taskTreeNodeFactory.createNewIteration();
        }
        else if ("Optional".equals(type)) {
            task = taskTreeNodeFactory.createNewOptional();
        }
        else {
            task = taskTreeNodeFactory.createNewEventTask(new StringEventType(type), target);
        }
        
        if ((matcher.group(5) != null) && (!"".equals(matcher.group(5).trim()))) {
            taskTreeBuilder.setDescription(task, matcher.group(5).trim());
        }

        while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) {
            if (task instanceof ISequence) {
                taskTreeBuilder.addChild((ISequence) task, parseTask(taskMatcher));
            }
            else if (task instanceof ISelection) {
                taskTreeBuilder.addChild((ISelection) task, parseTask(taskMatcher));
            }
            else if (task instanceof IIteration) {
                if ((task.getChildren() == null) || (task.getChildren().size() == 0)) {
                    taskTreeBuilder.setChild((IIteration) task, parseTask(taskMatcher));
                }
                else {
                    throw new IllegalArgumentException
                        ("can not add more than one child to an iteration");
                }
            }
            else if (task instanceof IOptional) {
                if ((task.getChildren() == null) || (task.getChildren().size() == 0)) {
                    taskTreeBuilder.setChild((IOptional) task, parseTask(taskMatcher));
                }
                else {
                    throw new IllegalArgumentException
                        ("can not add more than one child to an optional");
                }
            }
            else {
                throw new IllegalArgumentException("can not add children to something that is no " +
                                                   "sequence, selection, or iteration");
            }
        }

        return task;
    }

}
