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

import java.text.DecimalFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 16.07.2012$
 * @author 2012, last modified by $Author: pharms$
 */
public class RequiredScrollRule implements UsabilityEvaluationRule {

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
     */
    @Override
    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
        Map<ITask, Integer> smellingTasks = getTasksStartingWithScroll(taskModel.getTasks());

        UsabilityEvaluationResult results = new UsabilityEvaluationResult();
        analyzeTasksStartingWithScroll(smellingTasks, results);

        return results;
    }

    /**
     *
     */
    private void analyzeTasksStartingWithScroll(Map<ITask, Integer>       smellingTasks,
                                                UsabilityEvaluationResult results)
    {

        for (Map.Entry<ITask, Integer> entry : smellingTasks.entrySet()) {
            float ratio = entry.getValue() / (float) entry.getKey().getInstances().size();

            UsabilityDefectSeverity severity = null;
            if (ratio > 0.9) {
                severity = UsabilityDefectSeverity.HIGH;
            }
            else if (ratio > 0.6) {
                severity = UsabilityDefectSeverity.MEDIUM;
            }
            else if (ratio > 0.4) {
                severity = UsabilityDefectSeverity.LOW;
            }
            else if (ratio > 0.2) {
                severity = UsabilityDefectSeverity.INFO;
            }

            if (severity != null) {
                Map<String, String> parameters = new HashMap<String, String>();
                parameters.put("task", entry.getKey().toString());
                parameters.put("scrollRatio", DecimalFormat.getInstance().format(ratio * 100));

                results.addDefect(severity, UsabilityDefectDescription.SCROLL_REQUIRED, parameters);
            }
        }
    }

    /**
     * 
     */
    private Map<ITask, Integer> getTasksStartingWithScroll(Collection<ITask> tasks) {
        Map<ITask, Integer> scrollCounts = new HashMap<ITask, Integer>();
        
        for (ITask task : tasks) {
            // only sequences are important for required scrolls
            if (task instanceof ISequence) {
                int count = countInstancesStartingWithScroll(task);
                if (count > 0) {
                    scrollCounts.put(task, count);
                }
            }
        }
        
        return scrollCounts;
    }

    /**
     *
     */
    private int countInstancesStartingWithScroll(ITask task) {
        Collection<ITaskInstance> instances = task.getInstances();
        
        int counter = 0;
        for (ITaskInstance instance : instances) {
            if (startsWithScroll(instance)) {
                counter++;
            }
        }
        
        return counter;
    }

    /**
     *
     */
    private boolean startsWithScroll(ITaskInstance instance) {
        if (instance instanceof ISequenceInstance) {
            ITaskInstance firstChild = ((ISequenceInstance) instance).size() > 1 ?
                ((ISequenceInstance) instance).get(0) : null;

            if (firstChild == null) {
                throw new IllegalArgumentException
                    ("instance of a sequence must have at least two children");
            }

            if (startsWithScroll(firstChild)) {
                return true;
            }
        }
        else if (instance instanceof ISelectionInstance) {
            ITaskInstance child = ((ISelectionInstance) instance).getChild();
            
            if (child != null) {
                return startsWithScroll(child);
            }
            else {
                throw new IllegalArgumentException("instance of a selection must have a child");
            }
        }
        else if (instance instanceof IIterationInstance) {
            ITaskInstance firstChild = ((IIterationInstance) instance).size() > 0 ?
                ((IIterationInstance) instance).get(0) : null;

            if (firstChild == null) {
                throw new IllegalArgumentException
                    ("instance of an iteration must have at least one child");
            }

            if (startsWithScroll(firstChild)) {
                return true;
            }
        }
        else if (instance instanceof IOptionalInstance) {
            ITaskInstance child = ((IOptionalInstance) instance).getChild();
            
            if (child != null) {
                return startsWithScroll(child);
            }
        }
        else if (isScroll(instance)) {
            return true;
        }
        
        return false;
    }

    /**
     * @param firstChild
     * @return
     */
    private boolean isScroll(ITaskInstance instance) {
        ITaskInstance instanceToCheck = instance;
        
        if (instanceToCheck instanceof IIterationInstance) {
            instanceToCheck = ((IIterationInstance) instanceToCheck).size() > 0 ?
                ((IIterationInstance) instanceToCheck).get(0) : null;

            if (instanceToCheck == null) {
                throw new IllegalArgumentException
                    ("instance of an iteration must have at least one child");
            }
        }
        
        if (instanceToCheck instanceof IEventTaskInstance) {
            IEventType type = ((IEventTaskInstance) instanceToCheck).getEvent().getType();
            
            return (type instanceof Scroll);
        }
        
        return false;
    }

}
