package vismon.util;

import vismon.data.Indicator;
import vismon.data.MngAction;
import vismon.data.MngOption;
import vismon.data.Session;
import vismon.plot.ContourPixel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Computations {

    public static Map<ContourPixel, Double> computeInterpolatedDataAsCP(MngOption xMng, MngOption yMng, Indicator ind, int precision, int avgMedian) {
        int hX = xMng.getValueNum();
        int hY = yMng.getValueNum();

        Map<ContourPixel, Double> interpolatedData = new HashMap<ContourPixel, Double>();
        
        for (int j = 0; j < hY - 1; ++j) {
            for (int i = 0; i < hX - 1; ++i) {
                //CALCULATING THE VALUES OF INDEP GRIDS
                double x0 = xMng.getValue(i);
                double x1 = xMng.getNextValue(x0);
                double y0 = yMng.getValue(j);
                double y1 = yMng.getNextValue(y0);

                //CALCULATING THE VALUE OF DEP DIM AT 4 GRIDS
                Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
                indepMap00.put(xMng, x0);
                indepMap00.put(yMng, y0);
                MngAction ic00 = new MngAction(indepMap00);
                double p00 = Session.getDataItem(ic00).getAverageValue(ind);
                if (avgMedian == 1)
                    p00 = Session.getDataItem(ic00).getMedianValue(ind);

                /*Map<MngOption, Double> indepMap01 = new HashMap<MngOption, Double>();
                indepMap01.put(xMng, x1);
                indepMap01.put(yMng, y0);
                MngAction ic01 = new MngAction(indepMap01);
                double p01 = Session.getDataItem(ic01).getAverageValue(ind);

                Map<MngOption, Double> indepMap10 = new HashMap<MngOption, Double>();
                indepMap10.put(xMng, x0);
                indepMap10.put(yMng, y1);
                MngAction ic10 = new MngAction(indepMap10);
                double p10 = Session.getDataItem(ic10).getAverageValue(ind);

                Map<MngOption, Double> indepMap11 = new HashMap<MngOption, Double>();
                indepMap11.put(xMng, x1);
                indepMap11.put(yMng, y1);
                MngAction ic11 = new MngAction(indepMap11);
                double p11 = Session.getDataItem(ic11).getAverageValue(ind);*/

                /*for (int n = 0; n < precision; ++n) {
                    for (int m = 0; m < precision; ++m) {
                        double x = 1.0 * m / precision;
                        double y = 1.0 * n / precision;

                        //CALCULATING VALUE OF DATA
                        double data = (1 - x) * (y * p10 + (1 - y) * p00) + x * (y * p11 + (1 - y) * p01);
                        ContourPixel cp = new ContourPixel(i + x, j + y);
                        interpolatedData.put(cp, data);
                    }
                }*/
                double data = p00;
                ContourPixel cp = new ContourPixel(i, j);
                interpolatedData.put(cp, data);
            }
        }

        //TOP ROW
        for (int i = 0; i < hX - 1; ++i) {
            //CALCULATING THE VALUES OF INDEP GRIDS
            double x0 = xMng.getValue(i);
            double x1 = xMng.getNextValue(x0);
            double y0 = yMng.getMax();

            //CALCULATING THE VALUE OF DEP DIM AT 2 GRIDS
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);
            double p00 = Session.getDataItem(ic00).getAverageValue(ind);
            if (avgMedian == 1)
                    p00 = Session.getDataItem(ic00).getMedianValue(ind);

            /*Map<MngOption, Double> indepMap01 = new HashMap<MngOption, Double>();
            indepMap01.put(xMng, x1);
            indepMap01.put(yMng, y0);
            MngAction ic01 = new MngAction(indepMap01);
            double p01 = Session.getDataItem(ic01).getAverageValue(ind);*/

            /*for (int m = 0; m < precision; ++m) {
                double x = 1.0 * m / precision;
                double y = hY - 1;

                //CALCULATING VALUE OF DATA
                double data = (1 - x) * p00 + x * p01;
                ContourPixel cp = new ContourPixel(i + x, y);
                interpolatedData.put(cp, data);
            }*/
            double data = p00;
            ContourPixel cp = new ContourPixel(i, hY - 1);
            interpolatedData.put(cp, data);
        }

        for (int j = 0; j < hY - 1; ++j) {
            //CALCULATING THE VALUES OF INDEP GRIDS
            double x0 = xMng.getMax();
            double y0 = yMng.getValue(j);
            double y1 = yMng.getNextValue(y0);

            //CALCULATING THE VALUE OF DEP DIM AT 4 GRIDS
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);
            double p00 = Session.getDataItem(ic00).getAverageValue(ind);
            if (avgMedian == 1)
                    p00 = Session.getDataItem(ic00).getMedianValue(ind);

            /*Map<MngOption, Double> indepMap10 = new HashMap<MngOption, Double>();
            indepMap10.put(xMng, x0);
            indepMap10.put(yMng, y1);
            MngAction ic10 = new MngAction(indepMap10);
            double p10 = Session.getDataItem(ic10).getAverageValue(ind);*/

            /*for (int n = 0; n < precision; ++n) {
                double x = hX - 1;
                double y = 1.0 * n / precision;

                //CALCULATING VALUE OF DATA
                double data = (1 - y) * p00 + y * p10;
                ContourPixel cp = new ContourPixel(x, j + y);
                interpolatedData.put(cp, data);
            }*/
            double data = p00;
            ContourPixel cp = new ContourPixel(hX - 1, j);
            interpolatedData.put(cp, data);
        }

        //TOP-RIGHT DATAITEM
        Map<MngOption, Double> indepMap11 = new HashMap<MngOption, Double>();
        indepMap11.put(xMng, xMng.getMax());
        indepMap11.put(yMng, yMng.getMax());
        MngAction ic11 = new MngAction(indepMap11);
        double p11 = Session.getDataItem(ic11).getAverageValue(ind);
        if (avgMedian == 1)
            p11 = Session.getDataItem(ic11).getMedianValue(ind);

        ContourPixel cp = new ContourPixel(hX - 1, hY - 1);
        interpolatedData.put(cp, p11);
        return interpolatedData;
    }

    public static void computeIndAvgContourStep(Indicator ind, double min, double max) {
        double contourStep = (max - min) / Constants.contourNum;

        for (int i = 0; i < Constants.contourStepValues.length; ++i) {
            double stepValue = Constants.getContourStepValue(i);
            if (contourStep <= stepValue) {
                contourStep = stepValue;
                break;
            }
        }

        ind.setAvgContourStep(contourStep);
    }

    public static void computeIndAvgContourStepNumber(Indicator ind, double min, double max) {
        double contourStep = ind.getAvgContourStep();
        int contourStepNumber = 1;
        double firstContourValue = contourStep;
        while (firstContourValue < min)
            firstContourValue += contourStep;

        for (double i = firstContourValue; i <= max; i += contourStep)
            ++contourStepNumber;

        if (contourStepNumber > Constants.contourNum) {
            contourStep = Constants.getNextCommonStepValue(contourStep);
            ind.setAvgContourStep(contourStep);
            computeIndAvgContourStepNumber(ind, min, max);
            return;
        }

        ind.setAvgContourStepNumber(contourStepNumber);
    }

    public static void computeIndAvgIsolineValues(Indicator ind, double min, double max) {
        double[] isolineValues = new double[ind.getAvgContourStepNumber()];
        double contourStep = ind.getAvgContourStep();

        double firstContourValue = contourStep;
        while (firstContourValue < min)
            firstContourValue += contourStep;
        isolineValues[0] = firstContourValue;

        for (int i = 1; i < ind.getAvgContourStepNumber(); ++i)
            isolineValues[i] = firstContourValue + (i) * contourStep;

        ind.setAvgIsolineValues(isolineValues);
    }
    
    public static List<Double> getSimValuesOfPoint(MngOption xMng, MngOption yMng, Indicator ind, double xCoord, double yCoord) {
        if (xCoord == -1 || yCoord == -1)
            return null;

        List<Double> simValues = new ArrayList<Double>();

        double x0 = xMng.getNearestPrevValue(xCoord);
        double x1 = xMng.getNextValue(x0);
        double y0 = yMng.getNearestPrevValue(yCoord);
        double y1 = yMng.getNextValue(y0);

        double a = (xCoord - x0) / (x1 - x0);
        double b = (yCoord - y0) / (y1 - y0);

        double[] values = new double[Session.getSimulationNum()];

        if (!Util.isNaN(x1) && !Util.isNaN(y1)) {
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);

            Map<MngOption, Double> indepMap01 = new HashMap<MngOption, Double>();
            indepMap01.put(xMng, x1);
            indepMap01.put(yMng, y0);
            MngAction ic01 = new MngAction(indepMap01);

            Map<MngOption, Double> indepMap10 = new HashMap<MngOption, Double>();
            indepMap10.put(xMng, x0);
            indepMap10.put(yMng, y1);
            MngAction ic10 = new MngAction(indepMap10);

            Map<MngOption, Double> indepMap11 = new HashMap<MngOption, Double>();
            indepMap11.put(xMng, x1);
            indepMap11.put(yMng, y1);
            MngAction ic11 = new MngAction(indepMap11);

            for (int m = 0; m < values.length; ++m) {
                double p00 = Session.getDataItem(ic00).getValue(m, ind);
                double p01 = Session.getDataItem(ic01).getValue(m, ind);
                double p10 = Session.getDataItem(ic10).getValue(m, ind);
                double p11 = Session.getDataItem(ic11).getValue(m, ind);

                values[m] = (1 - a) * ((1 - b) * p00 + b * p10) + a * ((1 - b) * p01 + b * p11);
            }

        } else if (!Util.isNaN(x1) && Util.isNaN(y1)) {
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);

            Map<MngOption, Double> indepMap01 = new HashMap<MngOption, Double>();
            indepMap01.put(xMng, x1);
            indepMap01.put(yMng, y0);
            MngAction ic01 = new MngAction(indepMap01);

            for (int m = 0; m < values.length; ++m) {
                double p00 = Session.getDataItem(ic00).getValue(m, ind);
                double p01 = Session.getDataItem(ic01).getValue(m, ind);

                values[m] = (1 - a) * p00 + a * p01;
            }

        } else if (Util.isNaN(x1) && !Util.isNaN(y1)) {
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);

            Map<MngOption, Double> indepMap10 = new HashMap<MngOption, Double>();
            indepMap10.put(xMng, x0);
            indepMap10.put(yMng, y1);
            MngAction ic10 = new MngAction(indepMap10);

            for (int m = 0; m < values.length; ++m) {
                double p00 = Session.getDataItem(ic00).getValue(m, ind);
                double p10 = Session.getDataItem(ic10).getValue(m, ind);

                values[m] = (1 - b) * p00 + b * p10;
            }

        } else {
            Map<MngOption, Double> indepMap00 = new HashMap<MngOption, Double>();
            indepMap00.put(xMng, x0);
            indepMap00.put(yMng, y0);
            MngAction ic00 = new MngAction(indepMap00);

            for (int m = 0; m < values.length; ++m) {
                double p00 = Session.getDataItem(ic00).getValue(m, ind);

                values[m] = p00;
            }
        }

        for (int m = 0; m < values.length; ++m)
            simValues.add(values[m]);
        return simValues;
    }
}
