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

import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
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.IUserSession;

/**
 * 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)
    {
        System.out.println("\n\n########################################\n");
        final List<List<IEventTaskInstance>> actionInstancesInSameView = new LinkedList<>();
        
        for (IUserSession session : taskModel.getUserSessions()) {
            for (ITaskInstance instance : session) {
                final LinkedList<IEventTaskInstance> currentList = new LinkedList<>();
                
                instance.accept(new DefaultTaskInstanceTraversingVisitor() {
                    @Override
                    public void visit(IEventTaskInstance eventTaskInstance) {
                        if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) {
                            IEventTarget target = eventTaskInstance.getEvent().getTarget();
                            IGUIView currentView = ((IGUIElement) target).getView();
                            
                            IGUIView previousView = null;
                            if (currentList.size() > 0) {
                                target = currentList.getLast().getEvent().getTarget();
                                previousView = ((IGUIElement) target).getView();
                            }
                            
                            if ((previousView == currentView) ||
                                ((previousView != null) && (previousView.equals(currentView))))
                            {
                                currentList.add(eventTaskInstance);
                            }
                            else {
                                if (currentList.size() > 0) {
                                    actionInstancesInSameView.add(new ArrayList<>(currentList));
                                }
                                currentList.clear();
                                currentList.add(eventTaskInstance);
                            }
                        }
                    }
                });
                
                if (currentList.size() > 0) {
                    actionInstancesInSameView.add(new ArrayList<>(currentList));
                }
            }
        }

        
        Map<IGUIElement, List<IGUIElement>> checkBoxGroups = statistics.getCheckBoxGroups();
        
        for (Map.Entry<IGUIElement, List<IGUIElement>> group : checkBoxGroups.entrySet()) {
            IGUIView currentView = group.getKey().getView();
            int noOfEvents = 0;
            int noOfGroupUsages = 0;
            int noOfSingleCheckBoxUsages = 0;
            
            for (List<IEventTaskInstance> actionInstanceList : actionInstancesInSameView) {
                IEventTarget target = actionInstanceList.get(0).getEvent().getTarget();
                IGUIView viewOfList = ((IGUIElement) target).getView();
                
                if ((viewOfList == currentView) ||
                    ((viewOfList != null) && (viewOfList.equals(currentView))))
                {
                    noOfGroupUsages++;
                    boolean[] checkBoxUsages = new boolean[group.getValue().size()];
                    
                    for (IEventTaskInstance actionInstance : actionInstanceList) {
                        int index = 0;
                        for (IGUIElement checkBox : group.getValue()) {
                            if ((("JFC".equals(actionInstance.getEvent().getTarget().getPlatform())) &&
                                 (actionInstance.getEvent().getType() instanceof MouseClick) &&
                                 (actionInstance.getEvent().getTarget().equals(checkBox))) ||
                                ((actionInstance.getEvent().getType() instanceof ValueSelection<?>) &&
                                 (actionInstance.getEvent().getTarget().equals(checkBox))))
                            {
                                checkBoxUsages[index] = !checkBoxUsages[index];
                                noOfEvents++;
                            }
                            index++;
                        }
                    }
                    
                    int noOfCheckedBoxes = 0;
                    
                    for (int i = 0; i < checkBoxUsages.length; i++) {
                        if (checkBoxUsages[i]) {
                            noOfCheckedBoxes++;
                        }
                    }
                    
                    if (noOfCheckedBoxes == 1) {
                        noOfSingleCheckBoxUsages++;
                    }
                }
            }
            
            // get a value that is 1 if for one group usage there is on average one check box usage
            // get a value of 0 if for one group usage there is on average two check box usages
            
            int ratio = noOfGroupUsages > 0 ? 1000 * noOfSingleCheckBoxUsages / noOfGroupUsages : 0;
            
            System.out.println(currentView + "  " + ratio + "  " + noOfGroupUsages + "  " +
                               noOfSingleCheckBoxUsages);
            
            UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
                (ratio, noOfEvents, -1);
                    
            if (intensity != null) {
                Map<String, Object> parameters = new HashMap<String, Object>();
                parameters.put("allUsages", noOfGroupUsages);
                parameters.put("singleUsages", noOfSingleCheckBoxUsages);
                parameters.put("ratio", 100 * noOfSingleCheckBoxUsages / noOfGroupUsages);
                parameters.put("radioButtons", group.getValue());

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

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

    }

}
