package br.ufrgs.inf.prosoft.tigris.metrics;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.ehcache.sizeof.SizeOf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * The type Lightweight metrics.
 */
public class LightweightMetrics {

    private static SizeOf sizeOf = SizeOf.newInstance();
    private long occurrences;
    private SummaryStatistics timeStatistics;
    private SummaryStatistics memoryStatistics;
    private SummaryStatistics threadStatistics;
    private SummaryStatistics intervalStatistics;
    private List<Long> startTimes;
    private SummaryStatistics returnSizeStatistics;
    private Set<String> users;
    private String longName;
    private long thrownCounter;
    private StaticMetrics.StaticMetric staticMetric;

    /**
     * Instantiates a new Lightweight metrics.
     *
     * @param longName the long name
     * @throws IOException the io exception
     */
    public LightweightMetrics(String longName) throws IOException {
        this.longName = longName;
        startTimes = new ArrayList<>();
        timeStatistics = new SummaryStatistics();
        memoryStatistics = new SummaryStatistics();
        threadStatistics = new SummaryStatistics();
        returnSizeStatistics = new SummaryStatistics();
        intervalStatistics = new SummaryStatistics();
        intervalStatistics.addValue(0);
        users = new HashSet<>();
    }

    /**
     * Add time.
     *
     * @param startTime the start time
     * @param time      the time
     */
    public void addTime(long startTime, long time){
        startTimes.add(startTime);
        timeStatistics.addValue(time);
        if(startTimes.size() > 1)
            intervalStatistics.addValue(startTime - startTimes.get(startTimes.size() - 2));
    }

    public void addThreadNumber(long threads){
        threadStatistics.addValue(threads);
    }

    public void addMemoryConsumption(long memory){
        memoryStatistics.addValue(memory);
    }

    /**
     * Add return size.
     *
     * @param returnObject the return object
     */
    public void addReturnSize(Object returnObject){
        if(returnObject != null)
            returnSizeStatistics.addValue(sizeOf.sizeOf(returnObject));
        else
            returnSizeStatistics.addValue(0);
    }

    public void incThrown() {
        thrownCounter++;
    }

    /**
     * Inc occurrence.
     */
    public void incOccurrence() {
        occurrences++;
    }

    /**
     * Add user.
     *
     * @param currentUser the current user
     */
    public void addUser(String currentUser) {
        users.add(currentUser);
    }

    /**
     * Inter intervals list.
     *
     * @return the list
     */
    public List<Long> interIntervals(){
        List<Long> intervalTimes = new ArrayList<>();
        long lastTime = startTimes.get(0);
        for(Long l : startTimes){
            intervalTimes.add(l - lastTime);
            lastTime = l;
        }
        return intervalTimes;
    }

    /**
     * Gets name.
     *
     * @return the name
     */
    public String getName() {
        return longName.split(" ")[1].substring(0, longName.split(" ")[1].indexOf("("));
    }

    /**
     * Gets long name.
     *
     * @return the long name
     */
    public String getLongName() {
        return longName;
    }

    @Override
    public String toString() {
        return "LightweightMetrics{" +
                "name='" + getName() + '\'' +
                ", frequency=" + getFrequency() +
                ", expensiveness=" + getExpensiveness() +
                '}';
    }

    //Metrics below

    /**
     * Number of occurrences (calls)
     *
     * @return number of occurrences
     */
    public long getFrequency() {
        return occurrences;
    }

    /**
     * Cyclomatic complexity (Cyclomatic at understand tool)
     * @return Cyclomatic complexity from static metrics file
     */
    public long getMaintainability(){
        return staticMetric.cyclomatic;
    }

    /**
     * std classSize of the return
     *
     * @return class size
     */
    public double getChangeability(){
        return  returnSizeStatistics.getStandardDeviation();
    }

    /**
     * Mean number of different users that triggered the method
     *
     * @return number of different users
     */
    public long getUserBehavior(){
        return users.size();
    }

    /**
     * Mean Interval inter-calls
     *
     * @return inter interval times
     */
    public double getConcurrency(){
        return intervalStatistics.getMean();
    }

    /**
     * Number of constructs (if, while, for) (MaxNesting at understand tool)
     * @return MaxNesting from static metrics file
     */
    public long getErrorprone(){
        return staticMetric.maxNesting;
    }

    /**
     * Mean processing time
     *
     * @return processing times
     */
    public double getExpensiveness(){
        return timeStatistics.getMean();
    }


    /**
     * Number of static resources (variables and methods) referenced and thread creations (CountOutput at understand tool)
     * @return CountOutput from static metrics file
     */
    public long getGlobalImpact(){
        return staticMetric.countOutput;
    }

    /**
     * Throughput
     *
     * @return Throughput double
     */
    public double getLatency(){
        return (startTimes.get(startTimes.size()-1) - startTimes.get(0)) / startTimes.size();
    }

    public void setStaticMetric(StaticMetrics.StaticMetric staticMetric) {
        this.staticMetric = staticMetric;
    }
}
