//   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.commands.usability;

import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

import de.ugoe.cs.autoquest.CommandHelpers;
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.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.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;
import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils;
import de.ugoe.cs.util.console.Command;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.GlobalDataContainer;

/**
 * <p>
 * This command provides statistical information about a given task tree.
 * </p>
 * 
 * @author Patrick Harms
 * @version 1.0
 */
public class CMDtaskTreeStatistics implements Command {

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.util.console.Command#help()
     */
    @Override
    public String help() {
        return "taskTreeStatistics <tasktree>";
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
     */
    @Override
    public void run(List<Object> parameters) {
        String tasktreeName;
        try {
            tasktreeName = (String) parameters.get(0);
        }
        catch (Exception e) {
            throw new IllegalArgumentException("must provide a task tree name");
        }

        Object dataObject = GlobalDataContainer.getInstance().getData(tasktreeName);
        if (dataObject == null) {
            CommandHelpers.objectNotFoundMessage(tasktreeName);
            return;
        }
        if (!(dataObject instanceof ITaskModel)) {
            CommandHelpers.objectNotType(tasktreeName, "ITaskModel");
            return;
        }

        ITaskModel taskModel = (ITaskModel) dataObject;

        int allEvents = 0;
        
        int noOfEventTasks = 0;
        int noOfSequences = 0;
        int noOfIterations = 0;
        int noOfSelections = 0;
        int noOfOptionals = 0;
        Set<IEventTaskInstance> coveredEvents = new HashSet<IEventTaskInstance>();
        
        for (ITask task : taskModel.getTasks()) {
            if (task instanceof IEventTask) {
                noOfEventTasks++;
                allEvents += task.getInstances().size();
            }
            else if (task instanceof ISequence) {
                noOfSequences++;
                getCoveredEvents(task, coveredEvents);
            }
            else if (task instanceof IIteration) {
                noOfIterations++;
            }
            else if (task instanceof ISelection) {
                noOfSelections++;
            }
            else if (task instanceof IOptional) {
                noOfOptionals++;
            }
        }
        
        DescriptiveStatistics stats = new DescriptiveStatistics();
        
        for (IUserSession session : taskModel.getUserSessions()) {
            if (session.size() > 0) {
                final List<IEventTaskInstance> events = new LinkedList<>();
                
                for (ITaskInstance instance : session) {
                    instance.accept(new DefaultTaskInstanceTraversingVisitor() {
                        @Override
                        public void visit(IEventTaskInstance eventTaskInstance) {
                            events.add(eventTaskInstance);
                        }
                    });
                }
                
                stats.addValue(events.size());
            }
        }
        
        Console.println(stats.getN() + " sessions");
        Console.println("average session length " + stats.getMean() + " (std dev " +
                        stats.getStandardDeviation() + ")");
        Console.println(allEvents + " events");
        
        Console.println(noOfEventTasks + " eventTasks");
        Console.println(noOfSequences + " sequences");
        Console.println(noOfIterations + " iterations");
        Console.println(noOfSelections + " selections");
        Console.println(noOfOptionals + " optionals");
        
        Set<ISequence> mostProminent =
            TaskTreeUtils.getMostProminentTasks(taskModel, taskModel.getTasks());
        
        Set<IEventTaskInstance> mpCoveredEvents = new HashSet<IEventTaskInstance>();
       
        for (ITask task : mostProminent) {
            getCoveredEvents(task, mpCoveredEvents);
        }
        
        Console.println(noOfSequences + " sequences cover " +
                        coveredEvents.size() + " of " + allEvents + " recorded events (" +
                        formatPercent(coveredEvents.size(), allEvents) + ")");
        
        Console.println(mostProminent.size() + " of " + noOfSequences + " sequences (" +
                        formatPercent(mostProminent.size(), noOfSequences) + ") cover " +
                        mpCoveredEvents.size() + " of " + allEvents + " recorded events (" +
                        formatPercent(mpCoveredEvents.size(), allEvents) + ")");
        
        Console.println("CSV: " + tasktreeName + ";" + noOfEventTasks + ";" + noOfSequences + ";" +
                        noOfIterations + ";" + noOfSelections + ";" + noOfOptionals + ";" +
                        allEvents + ";" + coveredEvents.size() + ";" +
                        formatPercent(coveredEvents.size(), allEvents) + ";" +
                        mostProminent.size() + ";" +
                        formatPercent(mostProminent.size(), noOfSequences) + ";" +
                        mpCoveredEvents.size() + ";" +
                        formatPercent(mpCoveredEvents.size(), allEvents));
    }

    /**
     * convenience method to format a percentage
     */
    private String formatPercent(int ratio, int full) {
        return new DecimalFormat("#,##0.0%").format(((double) ratio) / full);
    }

    /**
     * <p>
     * convenience method to determine the number of covered events by a task
     * </p>
     *
     * @param task          the task to count the covered events for
     * @param coveredEvents the events covered by the task (in/out parameter)
     */
    private void getCoveredEvents(ITask task, final Set<IEventTaskInstance> coveredEvents) {
        for (ITaskInstance instance : task.getInstances()) {
            instance.accept(new DefaultTaskInstanceTraversingVisitor() {
                @Override
                public void visit(IEventTaskInstance eventTaskInstance) {
                    coveredEvents.add(eventTaskInstance);
                }
            });
        }
    }

}
