//   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.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;

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

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
     */
    @Override
    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
        ValueChangeStatistics statistics = new ValueChangeStatistics();
        
        calculateStatistics(taskModel.getTasks(), statistics);

        statistics.condenseCheckBoxGroups();
        
        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
        analyzeStatistics(statistics, taskModel, results);

        return results;
    }

    /**
     *
     */
    private void analyzeStatistics(ValueChangeStatistics     statistics,
                                   ITaskModel                taskModel,
                                   UsabilityEvaluationResult results)
    {
        Map<IGUIElement, List<IGUIElement>> checkBoxGroups = statistics.getCheckBoxGroups();
        
        CHECK_NEXT_GROUP:
        for (List<IGUIElement> group : checkBoxGroups.values()) {
            Set<ITask> tasksUsingGroup = new HashSet<>();
            
            for (IGUIElement checkBox : group) {
                Set<ITask> tasksUsingCheckBox = getTasksUsingCheckBox(checkBox, taskModel);
                
                for (ITask taskUsingCheckBox : tasksUsingCheckBox) {
                    if (tasksUsingGroup.contains(taskUsingCheckBox)) {
                        continue CHECK_NEXT_GROUP;
                    }
                    else {
                        tasksUsingGroup.add(taskUsingCheckBox);
                    }
                }
            }
            
            if (tasksUsingGroup.size() > 0) {
                int eventCoverage = 0;
                int allRecordedEvents = 0;
                
                for (ITask task : tasksUsingGroup) {
                    if (task instanceof IEventTask) {
                        eventCoverage +=
                            taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE);
                    }
                }
                
                for (ITask task : taskModel.getTasks()) {
                    if (task instanceof IEventTask) {
                        allRecordedEvents +=
                            taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE);
                    }
                }
                
                
                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
                    ((int) (1000 * eventCoverage / allRecordedEvents), eventCoverage, -1);
                    
                if (intensity != null) {
                    Map<String, Object> parameters = new HashMap<String, Object>();
                    parameters.put("radioButtons", group);

                    results.addSmell
                        (intensity, UsabilitySmellDescription.CHECK_BOX_SINGLE_SELECTION,
                         parameters);
                }
            }
        }
    }

    /**
     * 
     */
    private Set<ITask> getTasksUsingCheckBox(final IGUIElement checkBox, ITaskModel taskModel) {
        final Set<ITask> tasksUsingCheckBox = new HashSet<ITask>();
        
        for (ITask candidate : taskModel.getTasks()) {
            candidate.accept(new DefaultTaskTraversingVisitor() {
                @Override
                public void visit(IEventTask eventTask) {
                    if (!eventTask.getInstances().isEmpty()) {
                        IEventTaskInstance instance =
                            (IEventTaskInstance) eventTask.getInstances().iterator().next();
                        
                        if (checkBox.equals(instance.getEvent().getTarget())) {
                            tasksUsingCheckBox.add(eventTask);
                        }
                    }
                }
                
                @Override
                public void visit(IStructuringTemporalRelationship relationship) {
                    if (tasksUsingCheckBox.contains(relationship)) {
                        return;
                    }
                    else {
                        for (ITask child : relationship.getChildren()) {
                            if (tasksUsingCheckBox.contains(child)) {
                                tasksUsingCheckBox.add(relationship);
                                return;
                            }
                        }
                        
                        super.visit(relationship);
                        
                        for (ITask child : relationship.getChildren()) {
                            if (tasksUsingCheckBox.contains(child)) {
                                tasksUsingCheckBox.add(relationship);
                                break;
                            }
                        }
                    }
                }

                @Override
                public void visit(IMarkingTemporalRelationship relationship) {
                    if (tasksUsingCheckBox.contains(relationship)) {
                        return;
                    }
                    else {
                        if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) {
                            tasksUsingCheckBox.add(relationship);
                            return;
                        }
                        
                        super.visit(relationship);
                        
                        if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) {
                            tasksUsingCheckBox.add(relationship);
                            return;
                        }
                    }
                }
            });
        }
        
        return tasksUsingCheckBox;
    }

    /**
     * 
     */
    private void calculateStatistics(Collection<ITask>     tasks,
                                     ValueChangeStatistics statistics)
    {
        for (ITask task : tasks) {
            if (task instanceof IEventTask) {
                for (ITaskInstance taskInstance : task.getInstances()) {
                    statistics.addValueChange((IEventTaskInstance) taskInstance);
                }
            }
        }
    }

    /**
     *
     */
    private static class ValueChangeStatistics {
        
        /** */
        private Set<IGUIElement> checkBoxes = new HashSet<>();
        
        /** */
        private Map<IGUIElement, List<IGUIElement>> checkBoxGroups = new HashMap<>();

        /**
         *
         */
        private void addValueChange(IEventTaskInstance instance) {
            IGUIElement target = (IGUIElement) instance.getEvent().getTarget();
            
            if (target instanceof ICheckBox) {
                checkBoxes.add(target);
            }
        }
        
        /**
         *
         */
        private void condenseCheckBoxGroups() {
            checkBoxGroups = RuleUtils.getGroups(checkBoxes, 3);
        }

        /**
         *
         */
        private Map<IGUIElement, List<IGUIElement>> getCheckBoxGroups() {
            return checkBoxGroups;
        }

    }

}
