package vismon.data;

import vismon.gui.OptionsPanel;
import vismon.gui.Vismon;
import vismon.util.Computations;
import vismon.util.Constants;

import java.awt.*;
import java.util.*;
import java.util.List;


public class Session {

    private static String                   dataSetName;

    private static List<MngOption>          mngOptions;
    private static int                      mngOptionNum;   // = 2

    private static List<Indicator>          indicators;
    private static int                      indicatorNum;
    private static int                      indicatorRowNum;
    private static int                      indicatorDrawnRowNum;
    private static int                      indicatorColNum;
    private static int                      indicatorDrawnColNum;
    private static Map<Indicator, int[]>    indicatorRowColPositions;

    private static List<DataItem>           dataItems;
    private static int                      simulationNum;

    private static List<VisualRefLine>      visualRefLines;

    private static List<Scenario>           scenarios;
    private static Scenario                 selectedScenario;
    private static Scenario                 crosshairScenario;
    public static boolean showProbCrossesOnCPs;

    private static int                      errorBarPercentage;



    static {
        mngOptions = new ArrayList<MngOption>();
        indicators = new ArrayList<Indicator>();
        dataItems = new ArrayList<DataItem>();
        visualRefLines = new ArrayList<VisualRefLine>();
        scenarios = new ArrayList<Scenario>();
        indicatorRowColPositions = new HashMap<Indicator, int[]>();
        errorBarPercentage = 95;
    }

    public static String getDataSetName() {
        return dataSetName;
    }

    public static void setDataSetName(String dataSetName) {
        Session.dataSetName = dataSetName;
    }

    public static int getIndicatorNum() {
        return indicatorNum;
    }

    public static void setIndicatorNum(int indicatorNum) {
        Session.indicatorNum = indicatorNum;
    }

    public static List<Indicator> getIndicators() {
        return indicators;
    }

    public static Indicator getIndicator(int rank) {
        return indicators.get(rank);
    }

    public static void addIndicator(Indicator indicator) {
        Session.indicators.add(indicator);
    }

    public static List<Indicator> getVisibleIndicators() {
        if (Indicator.numOfVisibleIndicators == indicatorNum)
            return indicators;

        List<Indicator> visibleIndicators = new ArrayList<Indicator>();
        for (Indicator indicator : indicators)
            if (indicator.isVisible())
                visibleIndicators.add(indicator);
        return visibleIndicators;
    }

    public static int getMngOptionNum() {
        return mngOptionNum;
    }

    public static void setMngOptionNum(int mngOptionNum) {
        Session.mngOptionNum = mngOptionNum;
    }

    public static List<MngOption> getMngOptions() {
        return mngOptions;
    }

    public static MngOption getMngOption(int rank) {
        return mngOptions.get(rank);
    }

    public static void addMngOption(MngOption mngOption) {
        Session.mngOptions.add(mngOption);
    }

    public static List<VisualRefLine> getVisualRefLines() {
        return visualRefLines;
    }

    public static void addVisualRefLine(VisualRefLine visualRefLine) {
        visualRefLines.add(visualRefLine);
    }

    public static void removeVisualRefLine(int rank) {
        visualRefLines.remove(rank);
    }

    public static List<Scenario> getScenarios() {
        return scenarios;
    }

    public static Scenario getScenario(int rank) {
        return scenarios.get(rank);
    }

    public static void addScenario(Scenario scenario) {
        Session.scenarios.add(scenario);
        Constants.usedScenarioColor(scenario.getColor());

        Vismon.getVismonInstance().getScenarioPanel().addRow(scenario);
        Vismon.getVismonInstance().updatePlots();
    }

    public static void removeScenario(int rank) {
        Constants.freeScenarioColor(scenarios.get(rank).getColor());
        scenarios.remove(rank);
        --Scenario.numOfVisibleScenarios;

        Vismon.getVismonInstance().getScenarioPanel().removeRow(rank);
        Vismon.getVismonInstance().updatePlots();
    }

    public static Scenario createScenario(double xMngValue, double yMngValue) {
        MngAction mngAction = new MngAction();
        double xMngValueRounded = Math.round(xMngValue / getMngOption(0).getRoundFactor()) * getMngOption(0).getRoundFactor();
        double yMngValueRounded = Math.round(yMngValue / getMngOption(1).getRoundFactor()) * getMngOption(1).getRoundFactor();
        mngAction.addMngValue(getMngOption(0), xMngValueRounded);
        mngAction.addMngValue(getMngOption(1), yMngValueRounded);

        Scenario scenario = new Scenario(mngAction);
        Scenario.incrementIndicatorCounter();
        scenario.computeSimValues();
        scenario.doComputations();
        scenario.computePDF();

        Session.addScenario(scenario);
        return scenario;
    }

    public static Scenario createScenario(String name, double xMngValue, double yMngValue, Color color) {
        MngAction mngAction = new MngAction();
        double xMngValueRounded = Math.round(xMngValue / getMngOption(0).getRoundFactor()) * getMngOption(0).getRoundFactor();
        double yMngValueRounded = Math.round(yMngValue / getMngOption(1).getRoundFactor()) * getMngOption(1).getRoundFactor();
        mngAction.addMngValue(getMngOption(0), xMngValueRounded);
        mngAction.addMngValue(getMngOption(1), yMngValueRounded);

        Scenario scenario = new Scenario(mngAction);
        Scenario.incrementIndicatorCounter();
        scenario.setName(name);
        scenario.setColor(color);

        scenario.computeSimValues();
        scenario.doComputations();
        scenario.computePDF();

        Session.addScenario(scenario);
        return scenario;
    }
    
    public static List<Scenario> getSortedScenarios(Indicator indicator) {
        if (indicator == null)
            return scenarios;

        List<Scenario> sortedScenarios = new ArrayList<Scenario>();
        for (Scenario scn : scenarios)
            sortedScenarios.add(scn);

        for (int i = 0; i < sortedScenarios.size(); ++i) {
            for (int j = i+1; j < sortedScenarios.size(); ++j) {
                Scenario dataItem_i = sortedScenarios.get(i);
                Scenario dataItem_j = sortedScenarios.get(j);
                if (dataItem_j.lessThan(dataItem_i, indicator)) {
                    sortedScenarios.remove(i);
                    sortedScenarios.add(i, dataItem_j);
                    sortedScenarios.remove(j);
                    sortedScenarios.add(j, dataItem_i);
                }
            }
        }
        return sortedScenarios;
    }

    public static Scenario getSelectedScenario() {
        return selectedScenario;
    }

    public static void setSelectedScenario(Scenario selectedScenario) {
        Session.selectedScenario = selectedScenario;
        Vismon.getVismonInstance().getOverviewPanel().repaint();
        Vismon.getVismonInstance().getCplomPanel().repaintAllGUIs();
        Vismon.getVismonInstance().getScenarioPanel().highlightSelectedScenario();
    }

    public static List<Scenario> getVisibleScenarios() {
        if (scenarios.size() == Scenario.numOfVisibleScenarios)
            return scenarios;

        List<Scenario> visibleScenarios = new ArrayList<Scenario>();
        for (Scenario scenario : scenarios)
            if (scenario.isVisible())
                visibleScenarios.add(scenario);
        return visibleScenarios;
    }

    public static int getErrorBarPercentage() {
        return errorBarPercentage;
    }

    public static void setErrorBarPercentage(int errorBarPercentage) {
        Session.errorBarPercentage = errorBarPercentage;
    }

    public static int getSimulationNum() {
        return simulationNum;
    }

    public static void setSimulationNum(int simulationNum) {
        Session.simulationNum = simulationNum;
    }

    public static List<DataItem> getDataItems() {
        return dataItems;
    }

    public static void addDataItem(DataItem di) {
        dataItems.add(di);
    }

    public static Scenario getCrosshairScenario() {
        return crosshairScenario;
    }

    public static void setCrosshairScenario(Scenario crosshairScenario) {
        Session.crosshairScenario = crosshairScenario;
    }

    public static void setCrosshairScenario(double xMngValue, double yMngValue) {
        if (!OptionsPanel.showCrosshairs && !OptionsPanel.seeMCTrials) {
            crosshairScenario = null;
            return;
        }

        MngAction mngAction = new MngAction();
        double xMngValueRounded = Math.round(xMngValue / getMngOption(0).getRoundFactor()) * getMngOption(0).getRoundFactor();
        double yMngValueRounded = Math.round(yMngValue / getMngOption(1).getRoundFactor()) * getMngOption(1).getRoundFactor();
        mngAction.addMngValue(getMngOption(0), xMngValueRounded);
        mngAction.addMngValue(getMngOption(1), yMngValueRounded);

        crosshairScenario = new Scenario(mngAction);
        crosshairScenario.computeSimValues();
        crosshairScenario.doComputations();
    }

    public static int getIndicatorColNum() {
        return indicatorColNum;
    }

    public static void setIndicatorColNum(int indicatorColNum) {
        Session.indicatorColNum = indicatorColNum;
        indicatorDrawnColNum = indicatorColNum;
    }

    public static int getIndicatorDrawnColNum() {
        return indicatorDrawnColNum;
    }

    public static int getIndicatorRowNum() {
        return indicatorRowNum;
    }

    public static void setIndicatorRowNum(int indicatorRowNum) {
        Session.indicatorRowNum = indicatorRowNum;
        indicatorDrawnRowNum = indicatorRowNum;
    }

    public static int getIndicatorDrawnRowNum() {
        return indicatorDrawnRowNum;
    }

    private static void setIndicatorRowColPosition(Indicator ind, int r, int c) {
        indicatorRowColPositions.put(ind, new int[]{r, c});
    }

    public static void setIndicatorDefaultRowColPositions() {
        for (Indicator indicator : indicators) {
            int rank = getIndicatorRank(indicator);
            int r = rank/indicatorColNum;
            int c = rank%indicatorColNum;
            setIndicatorRowColPosition(indicator,  r, c);
        }
    }

    public static void calculateDrawnRowCalNum() {
        if (Indicator.numOfVisibleIndicators == 1) {
            indicatorDrawnRowNum = 1;
            indicatorDrawnColNum = 1;
            setIndicatorRowColPosition(getVisibleIndicators().get(0), 0, 0);
            return;
        }

        if (indicatorNum == Indicator.numOfVisibleIndicators) {
            indicatorDrawnRowNum = indicatorRowNum;
            indicatorDrawnColNum = indicatorColNum;
            setIndicatorDefaultRowColPositions();
            return;
        }

        // calc the number of visible rows
        Map<Integer, Integer> rowIndNum = new HashMap<Integer, Integer>();
        for (int r = 0; r < indicatorRowNum; ++r)
            rowIndNum.put(r, 0);
        for (Indicator indicator : indicators)
            if (indicator.isVisible()) {
                int rank = Session.getIndicatorRank(indicator);
                int r = rank/indicatorColNum;
                int n = rowIndNum.get(r) + 1;
                rowIndNum.put(r, n);
            }

        indicatorDrawnRowNum = 0;
        for (Map.Entry<Integer, Integer> e : rowIndNum.entrySet())
            if (e.getValue() != 0)
                ++indicatorDrawnRowNum;

        // calc the number of visible cols
        Map<Integer, Integer> colIndNum = new HashMap<Integer, Integer>();
        for (int c = 0; c < indicatorColNum; ++c)
            colIndNum.put(c, 0);
        for (Indicator indicator : indicators)
            if (indicator.isVisible()) {
                int rank = Session.getIndicatorRank(indicator);
                int c = rank%indicatorColNum;
                int n = colIndNum.get(c) + 1;
                colIndNum.put(c, n);
            }

        indicatorDrawnColNum = 0;
        for (Map.Entry<Integer, Integer> e : colIndNum.entrySet())
            if (e.getValue() != 0)
                ++indicatorDrawnColNum;

        for (int i = 0; i < indicatorNum; ++i) {
            Indicator ithIndicator = indicators.get(i);
            int newR = 0;
            int newC = 0;
            if (!ithIndicator.isVisible()) {
                newR = -1;
                newC = -1;
            } else {
                int r = i/indicatorColNum;
                int c = i%indicatorColNum;

                for (int rIndex = 0; rIndex < r; ++rIndex)
                    if (rowIndNum.get(rIndex) != 0)
                        ++newR;
                for (int cIndex = 0; cIndex < c; ++cIndex)
                    if (colIndNum.get(cIndex) != 0)
                        ++newC;
            }
            setIndicatorRowColPosition(ithIndicator, newR, newC);
            //System.out.println(ithIndicator.getName() + " @(" + newR + ", " + newC + ")");
        }

        return;
    }

    public static int[] getIndicatorRowColPosition(Indicator indicator) {
        return indicatorRowColPositions.get(indicator);
    }

    public static int getIndicatorRank(Indicator ind) {
        for (int i = 0; i < indicatorNum; ++i)
            if (getIndicator(i).equals(ind))
                return i;
        return -1;
    }

    public static DataItem getDataItem(MngAction action) {
        for (DataItem di : dataItems)
            if (di.hasMngAction(action))
                return di;
        return null;
    }

    public static void calcIndicatorContourStep(Indicator ind) {
        double[] dataItemAvgs = new double[dataItems.size()];
        double avgMin = Double.MAX_VALUE;
        double avgMax = Double.MIN_VALUE;

        for (int i = 0; i < dataItemAvgs.length; ++i) {
            dataItemAvgs[i] = dataItems.get(i).getAverageValue(ind);
            if (dataItemAvgs[i] < avgMin)
                avgMin = dataItemAvgs[i];
            if (dataItemAvgs[i] > avgMax)
                avgMax = dataItemAvgs[i];
        }

        Computations.computeIndAvgContourStep(ind, avgMin, avgMax);
        Computations.computeIndAvgContourStepNumber(ind, avgMin, avgMax);
        Computations.computeIndAvgIsolineValues(ind, avgMin, avgMax);
    }

    public static int getScenarioRank(Scenario scenario) {
        for (int i = 0; i < scenarios.size(); ++i) {
            if (scenario.equals(scenarios.get(i)))
                return i;
        }
        return -1;
    }

    public static void checkIfShowProbCrossesOnCPs() {
        boolean show = false;
        for (Indicator ind : indicators) {
            if (!ind.isDefaultProbabilityValue()) {
                show = true;
                break;
            }
        }

        showProbCrossesOnCPs = show;
    }
}
