package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats;

import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.MethodInfo;
import br.ufrgs.inf.prosoft.adaptivecaching.cache.CacheInfo;

import java.util.Optional;

/**
 * The type Cacheability metrics.
 */
public class CacheabilityMetrics {

    /**
     * Sample size long.
     *
     * @param N the n
     * @param Z the z
     * @param e the e
     * @return the long
     */
//total da população
    public static long sampleSize(long N, double Z, double e) {
        //Nível de confiança 90% -> Z=1.645
        //Nível de confiança 95% -> Z=1.96
        //Nível de confiança 99% -> Z=2.575
        //Qual o nível de certeza que precisa ter de que a amostra retrata com precisão a sua população?
        //double Z = 2.575;

        //e = É a margem de erro máximo que eu quero admitir (p.e. 5%)
        //Qual o nível de certeza que precisa ter de que os traces refletem as execuções da sua aplicação?
        //double e = 0.03;

        //p = É a proporção que esperamos encontrar. Este parâmetro tende confundir bastante à primeira vista:
        double p = 0.5;

        long n = (long) ((N * Math.pow(Z, 2) * p * (1 - p)) /
                ((N - 1) * Math.pow(e, 2) + Math.pow(Z, 2) * p * (1 - p)));

        //simplified without the population
        //int nn = (int) ((Math.pow(Z,2) * p * (1-p)) / Math.pow(e,2));
        return n;
    }

    /**
     * Is static data optional.
     *
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @param population  the population
     * @return the optional
     */
    public static Optional<Boolean> isStaticData(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {

        //executions of a method should represent a good portion of total logs in order to avoid 1 occur == 100% hit
//        if (!(methodStats.getNumberOfSameOccurrences() >= sampleSize(methodStats.getNumberOfOccurrences(), 1.645, 0.05)
//                && methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05))) {
//            return Optional.empty();
//        }
        if (methodStats.getNumberOfSameOccurrences() < workflow.frequencyThreshold(0)) {
            return Optional.empty();
        }
        if (methodStats.hitRatio() == 100.0)
            return Optional.of(true);
        else
            return Optional.of(false);
    }

    /**
     * Change more than used optional.
     *
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @return the optional
     */
    public static Optional<Boolean> changeMoreThanUsed(MethodStats methodStats, FlowchartWorkFlow workflow) {
        //+/- k sds
        if (methodStats.missRatio() > workflow.missThreshold(0))
            return Optional.of(true);
        else
            return Optional.of(false);
    }

    /**
     * Used by many requests optional.
     *
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @param population  the population
     * @return the optional
     */
    public static Optional<Boolean> usedByManyRequests(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {

        //same executions of a method should represent a good portion of total execution of such method
        if (methodStats.getNumberOfSameOccurrences() >= workflow.frequencyThreshold(0)) {
//        if (methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05)) {
            return Optional.of(true);
        } else return Optional.of(false);
    }

    /**
     * Is user specific optional.
     *
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @return the optional
     */
    public static Optional<Boolean> isUserSpecific(MethodStats methodStats, FlowchartWorkFlow workflow) {

        if (methodStats.getAmountOfIdentifiedSameOccurences() == 0)
            return Optional.empty();

        //the less shareable, the more user specific
        if (methodStats.shareability() < workflow.shareabilityThreshold(0))
            return Optional.of(true);
        else
            return Optional.of(false);
    }

    /**
     * Is cache size large optional.
     *
     * @param cacheInfo the cache info
     * @return the optional
     */
    public static Optional<Boolean> isCacheSizeLarge(CacheInfo cacheInfo) {
        //concept considered while caching
        return Optional.of(true);
    }

    /**
     * Is data size large optional.
     *
     * @param cacheInfo  the cache info
     * @param methodInfo the method info
     * @return the optional
     */
    public static Optional<Boolean> isDataSizeLarge(CacheInfo cacheInfo, MethodInfo methodInfo) {
        //concept considered while caching
        return Optional.of(false);
    }

    /**
     * Is expensive optional.
     *
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @return the optional
     */
    public static Optional<Boolean> isExpensive(MethodStats methodStats, FlowchartWorkFlow workflow) {
        if (methodStats.getSameOccurrencesAverageExecutionTime() >= workflow.expensivenessThreshold(0))
            return Optional.of(true);
        else
            return Optional.of(false);
    }

    /**
     * All metrics to string string.
     *
     * @param cacheInfo   the cache info
     * @param methodInfo  the method info
     * @param methodStats the method stats
     * @param workflow    the workflow
     * @param population  the population
     * @return the string
     */
    public static String allMetricsToString(CacheInfo cacheInfo, MethodInfo methodInfo, MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
        //isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive
        return isStaticData(methodStats, workflow, population) +
                "," + changeMoreThanUsed(methodStats, workflow) +
                "," + usedByManyRequests(methodStats, workflow, population) +
                "," + isUserSpecific(methodStats, workflow) +
                "," + isCacheSizeLarge(cacheInfo) +
                "," + isDataSizeLarge(cacheInfo, methodInfo) +
                "," + isExpensive(methodStats, workflow)
                ;
    }
}
