//   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.tasktrees.treeimpl;

import java.util.ArrayList;

import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;

/**
 * <p>
 * this is the default implementation of the interface {@link ITaskInfo}. It
 * does not do anything fancy except implementing the interface.
 * </p>
 * 
 * @author Patrick Harms
 */
public class TaskInfo implements ITaskInfo {
    
    /**
     * <p>
     * the task to which the infos belong
     * </p>
     */
    private ITask task;
    
    /**
     * <p>
     * all available measures for the task
     * </p>
     */
    private ArrayList<Measure> measures = new ArrayList<Measure>();

    /**
     * <p>
     * initialized the task infos with the task to which they belong.
     * </p>
     * 
     * @param task
     */
    TaskInfo(ITask task) {
        this.task = task;
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo#getTask()
     */
    @Override
    public ITask getTask() {
        return task;
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo#getMeasures()
     */
    @Override
    public IMeasure[] getMeasures() {
        measures.trimToSize();
        return measures.toArray(new IMeasure[measures.size()]);
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo#getMeasureValue(java.lang.String)
     */
    @Override
    public int getMeasureValue(TaskMetric metric) {
        Measure measure = getMeasure(metric);
        
        if (measure == null) {
            throw new IllegalArgumentException("unknown metric " + metric);
        }
        
        return measure.getValue();
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public synchronized String toString() {
        return "TaskInfo(" + task + ")";
    }

    /**
     * <p>
     * must be called to indicate that new measures for the provided metric are about to
     * be calculated and added.
     * </p>
     *
     * @param metric the metric for which measures are about to be provided
     */
    void addMeasure(TaskMetric metric) {
        Measure measure = getMeasure(metric);
        
        if (measure != null) {
            throw new IllegalArgumentException("measure for metric " + metric + " already exists.");
        }
        
        measure = new Measure(metric);
        measures.add(measure);
    }

    /**
     * <p>
     * sets a specific value for a measure of a specific metric
     * </p>
     * 
     * @param metric  the metric to which the value belongs
     * @param value   the value of the measure
     */
    void setCount(TaskMetric metric, int value) {
        Measure measure = getMeasure(metric);
        
        if (measure == null) {
            throw new IllegalArgumentException("unknown metric. Please create a measure " +
                                               "for the metric before using it.");
        }
        
        measure.set(value);
    }

    /**
     * <p>
     * increases a specific value for a measure of a specific metric
     * </p>
     * 
     * @param metric    the metric to which the value belongs
     * @param increment the increment to be added to the value of the measure
     */
    void increaseCount(TaskMetric metric, int increment) {
        Measure measure = getMeasure(metric);
        
        if (measure == null) {
            throw new IllegalArgumentException("unknown metric. Please create a measure " +
                                               "for the metric before using it.");
        }
        
        measure.increase(increment);
    }

    /**
     * <p>
     * convenience method to internally determine the measure for a specific metric
     * </p>
     */
    private Measure getMeasure(TaskMetric metric) {
        for (Measure candidate : measures) {
            if (candidate.getMetric().equals(metric)) {
                return candidate;
            }
        }
        
        return null;
    }

    /**
     * <p>
     * implementation for the measure interface of the task info interface. Does nothing fancy
     * except implementing the interface
     * </p>
     * 
     * @author Patrick Harms
     */
    private static class Measure implements IMeasure {

        /**
         * <p>
         * the metric to which the measure belongs
         * </p>
         */
        private TaskMetric metric;
        
        /**
         * <p>
         * the value of the measure independent
         * </p>
         */
        private int value = 0;
        
        /**
         * <p>
         * initializes the measure with a specific metric
         * </p>
         */
        private Measure(TaskMetric metric) {
            super();
            this.metric = metric;
        }

        /* (non-Javadoc)
         * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo.IMeasure#getMetric()
         */
        @Override
        public TaskMetric getMetric() {
            return metric;
        }

        /* (non-Javadoc)
         * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo.IMeasure#getValue()
         */
        @Override
        public int getValue() {
            return value;
        }

        /**
         * <p>
         * sets the value of the measure
         * </p>
         */
        private void set(int newValue) {
            value = newValue;
        }

        /**
         * <p>
         * increases the value of the measure
         * </p>
         */
        private void increase(int increment) {
            value += increment;
        }

    }

}
