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

import org.apache.commons.math3.stat.inference.ChiSquareTest;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
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.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 DefaultCursorPositioningRule implements UsabilityEvaluationRule {

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

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

        return results;
    }

    /**
     *
     */
    private void analyzeStatistics(FirstViewActionStatistics statistics,
                                   UsabilityEvaluationResult results)
    {
        for (Map.Entry<IGUIView, Map<IEventTask, Integer>> firstActions :
                statistics.getFirstViewActions().entrySet())
        {
            long[] observed = new long[firstActions.getValue().size()];
            long allObservedInView = 0;
            long maxObserved = 0;
            int i = 0;
            IEventTask mostOftenDoneFirst = null;
            
            for (Map.Entry<IEventTask, Integer> firstAction : firstActions.getValue().entrySet()) {
                observed[i++] = firstAction.getValue();
                allObservedInView += firstAction.getValue();
                
                if (maxObserved < firstAction.getValue()) {
                    maxObserved = firstAction.getValue();
                    mostOftenDoneFirst = firstAction.getKey();
                }
            }
            
            double[] expected = new double[firstActions.getValue().size()];
            double expectedFrequency =
                ((double) statistics.getViewOpenedCount(firstActions.getKey())) / expected.length;
            
            for (i = 0; i < expected.length; i++) {
                expected[i] = expectedFrequency;
            }
            
            if ((allObservedInView > 1) &&
                ((expected.length == 1) ||
                 (new ChiSquareTest().chiSquareTest(expected, observed, 0.05))))
            {
                // values are not equally distributed.
                    
                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
                    ((int) (1000 * maxObserved / allObservedInView), (int) allObservedInView, -1);
                        
                if ((intensity != null) && isCursorPositioningAction(mostOftenDoneFirst)) {
                    Map<String, Object> parameters = new HashMap<String, Object>();
                    parameters.put("view", firstActions.getKey());
                    parameters.put("textfield", getTarget(mostOftenDoneFirst));
                    parameters.put("ratio", new DecimalFormat("#.##").format
                                       (100.0 * maxObserved / allObservedInView));

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


    /**
     *
     */
    private boolean isCursorPositioningAction(IEventTask task) {
        Event event = ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent();
        return ((event.getType() instanceof MouseClick) ||
                (event.getType() instanceof MouseButtonDown)) &&
               (((MouseButtonInteraction) event.getType()).getButton() ==
                   MouseButtonInteraction.Button.LEFT) &&
               ((event.getTarget() instanceof ITextField) ||
                (event.getTarget() instanceof ITextArea));
    }

    /**
     *
     */
    private IEventTarget getTarget(IEventTask task) {
        return ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent().getTarget();
    }

    /**
     * 
     */
    private void calculateStatistics(List<IUserSession>              sessions,
                                     final FirstViewActionStatistics statistics)
    {
        final IGUIView[] currentView = new IGUIView[1];
        
        for (IUserSession session : sessions) {
            for (final ITaskInstance currentRoot : session) {
                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
                    @Override
                    public void visit(IEventTaskInstance eventTaskInstance) {
                        // ignore scrolling to get something semantically helpful
                        if ((eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) &&
                            (!(eventTaskInstance.getEvent().getType() instanceof Scroll)))
                        {
                            IGUIView view =
                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
                            
                            if (((currentView[0] == null) && (view != null)) ||
                                ((currentView[0] != null) && (!currentView[0].equals(view))))
                            {
                                currentView[0] = view;
                                statistics.addFirstViewAction
                                    (view, eventTaskInstance.getEventTask());
                            }
                        }
                    }
                });
            }
        }
    }

    /**
     *
     */
    private class FirstViewActionStatistics {
        
        /** */
        private Map<IGUIView, Map<IEventTask, Integer>> firstViewActions = new HashMap<>();
        
        /**
         *
         */
        private void addFirstViewAction(IGUIView view, IEventTask action) {
            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
            
            if (counterMap == null) {
                counterMap = new HashMap<>();
                firstViewActions.put(view, counterMap);
            }
            
            Integer counter = counterMap.get(action);
            
            if (counter == null) {
                counterMap.put(action, 1);
            }
            else {
                counterMap.put(action, counter + 1);
            }
        }

        /**
         *
         */
        private Map<IGUIView, Map<IEventTask, Integer>> getFirstViewActions() {
            return firstViewActions;
        }

        /**
         *
         */
        private int getViewOpenedCount(IGUIView view) {
            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
            int count = 0;
            
            for (Integer counter : counterMap.values()) {
                count += counter;
            }
            
            return count;
        }
    }
}
