package vismon.plot;

import vismon.data.*;
import vismon.gui.*;
import vismon.util.Computations;
import vismon.util.Constants;
import vismon.util.TickMarkCalculator;
import vismon.util.Util;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContourPlot extends JPanel{

    public /*static*/ Object[] xAxisTickMarks;
    public /*static*/ Object[] yAxisTickMarks;

    protected final int leftBottomBorderWidth = 30;
    protected final int rightTopBorderWidth = 20;
    protected final int sceranioRadius = 4;

    protected final int MIN_DIM_FOR_ISOLINE_VALUES = 150;

    protected int histoWidth;

    protected MngOption xMngOption;
    protected MngOption yMngOption;
    protected Indicator indicator;

    private Map<Scenario, Point> scenarioLocations;

    private List<Map<ContourPixel, Double>> interpolatedData;

    protected JPopupMenu popup;

    protected int x;
    protected int y;
    protected int w;
    protected int h;

    protected int previousW;
    protected int previousH;

    protected int cursorLocalX;
    protected int cursorLocalY;
    public static double cursorPlotX;
    public static double cursorPlotY;

    private boolean seeMCTrials;

    private Map<Double, Integer> isolinePieceNum;

    static {
        cursorPlotX = Constants.NA;
        cursorPlotY = Constants.NA;
    }

    public ContourPlot(MngOption xMng, MngOption yMng, Indicator ind) {
        this.xMngOption = xMng;
        this.yMngOption = yMng;
        this.indicator = ind;

        x = leftBottomBorderWidth;
        y = rightTopBorderWidth;
        seeMCTrials = false;

        isolinePieceNum = new HashMap<Double, Integer>();

        scenarioLocations = new HashMap<Scenario, Point>();

        interpolatedData = new ArrayList<Map<ContourPixel, Double>>();
        for (int i = 0; i < Session.getIndicatorNum(); ++i)
            interpolatedData.add(new HashMap<ContourPixel, Double>());

        popup = new JPopupMenu();
        ActionListener menuListener = new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (event.getActionCommand().equals("Delete Plot"))
                    deletePlot();
                else if (event.getActionCommand().equals("Show in a New Window"))
                    showInANewWindow();
                else if (event.getActionCommand().equals("Create Single Scatter Plot with..."))
                    creatScatterPlot();
                ;
            }
        };

        JMenuItem delItem, showItem, scatterPlotItem;
        popup.add(delItem = new JMenuItem("Delete Plot"));
        delItem.setHorizontalTextPosition(JMenuItem.RIGHT);
        delItem.addActionListener(menuListener);

        popup.addSeparator();

        popup.add(showItem = new JMenuItem("Show in a New Window"));
        showItem.setHorizontalAlignment(JMenuItem.RIGHT);
        showItem.addActionListener(menuListener);

        popup.add(scatterPlotItem = new JMenuItem("Create Single Scatter Plot with..."));
        scatterPlotItem.setHorizontalAlignment(JMenuItem.RIGHT);
        scatterPlotItem.addActionListener(menuListener);

        popup.setBorder(new BevelBorder(BevelBorder.RAISED));
    }

    public JPopupMenu getPopup() {
        return popup;
    }

    public void deletePlot() {
        indicator.setVisible(false);
        setVisible(false);

        Vismon.getVismonInstance().updateIndicatorVisibility();
    }

    @Override
    public void paint(Graphics g) {
        if (!indicator.isVisible())
            return;

        super.paint(g);

        previousW = w;
        previousH = h;

        w = this.getWidth() - leftBottomBorderWidth - rightTopBorderWidth;
        h = this.getHeight() - leftBottomBorderWidth - rightTopBorderWidth;

        plotShader((Graphics2D) g, x, y, w, h);
        drawIndConstraints((Graphics2D) g, x, y, w, h);
        drawMngOptionConstraints((Graphics2D) g, x, y, w, h);
        drawGrids((Graphics2D) g, x, y, w, h);
        drawVisualRefLines((Graphics2D) g, x, y, w, h);
        drawProbConstraints((Graphics2D) g, x, y, w, h);

        drawBordersTickMarks((Graphics2D) g, x, y, w, h);

        // CONTOUR_LINES
        if (OptionsPanel.showContourLines)
            for (int i = 0; i < indicator.getAvgContourStepNumber(); ++i)
                drawIsoLine((Graphics2D) g, x, y, w, h, indicator.getIsolineValue(i));

        drawScenarios((Graphics2D) g, x, y, w, h);

        // CROSSHAIRS
        if (OptionsPanel.showCrosshairs) {
            calcLocalXY();
            if (cursorLocalX >= x && cursorLocalX <= (x + w) &&
                cursorLocalY >= y && cursorLocalY <= (y + h)) {
                drawCrosshairs((Graphics2D) g);
            }
        }

        // MC TRIALS HISTOGRAM
        if (OptionsPanel.seeMCTrials && (seeMCTrials || OptionsPanel.seeTrialsAllInds)) {
            calcLocalXY();
            if (cursorLocalX >= x && cursorLocalX <= (x + w) &&
                cursorLocalY >= y && cursorLocalY <= (y + h)) {
                drawHistogarms((Graphics2D) g);
            }
        }
    }

    private void plotShader(Graphics2D g, int x, int y, int w, int h) {
        if (OptionsPanel.shading) {
            if (indicator.isMaxBest()) {
                int bin = indicator.getAvgContourStepNumber() - 1;
                paintLowerBoundry(g, x, y, w, h, indicator, Constants.getCPShadingColor(ContourShadingChooser.cpShadingColorIndex, bin), indicator.getIsolineValue(0));
                --bin;
                for (int i = 0; i < indicator.getAvgContourStepNumber() - 1; ++i) {
                    Color ccc = Constants.getCPShadingColor(ContourShadingChooser.cpShadingColorIndex, bin);
                    --bin;
                    paintUpperBoundry(g, x, y, w, h, indicator, ccc, indicator.getIsolineValue(i));
                }
            } else {
                int bin = 0;
                paintLowerBoundry(g, x, y, w, h, indicator, Constants.getCPShadingColor(ContourShadingChooser.cpShadingColorIndex, bin), indicator.getIsolineValue(0));
                ++bin;
                for (int i = 0; i < indicator.getAvgContourStepNumber() - 1; ++i) {
                    Color ccc = Constants.getCPShadingColor(ContourShadingChooser.cpShadingColorIndex, bin);
                    ++bin;
                    paintUpperBoundry(g, x, y, w, h, indicator, ccc, indicator.getIsolineValue(i));
                }
            }
        }
    }

    public void drawCrosshairs(Graphics2D g) {
        g.setColor(Color.LIGHT_GRAY);
        g.drawLine(cursorLocalX - 1, y, cursorLocalX - 1, y + h);
        g.drawLine(cursorLocalX + 1, y, cursorLocalX + 1, y+h);
        g.drawLine(x, cursorLocalY - 1, x+w, cursorLocalY - 1);
        g.drawLine(x, cursorLocalY + 1, x + w, cursorLocalY + 1);

        g.setColor(Constants.MAIN_COLOR);
        g.drawLine(cursorLocalX, y, cursorLocalX, y + h);
        g.drawLine(x, cursorLocalY, x + w, cursorLocalY);
    }

    public void drawHistogarms(Graphics2D g) {
        histoWidth = (int)(0.5 * (w < h ? w : h));
        int histoPlotX = cursorLocalX;
        int histoPlotY = cursorLocalY;
        if (cursorLocalX > x + (int)(0.3*w))
            histoPlotX -=histoWidth;
        if (cursorLocalY > y + (int)(0.3*h))
            histoPlotY -=histoWidth;

        g.setColor(Color.WHITE);
        g.fillRect(histoPlotX, histoPlotY, histoWidth, histoWidth);
        g.setColor(Constants.MAIN_COLOR);
        g.drawRect(histoPlotX, histoPlotY, histoWidth, histoWidth);

        MCTrialsPlot mcTrialsPlot = new MCTrialsPlot(indicator);
        List<Integer> binHeights = mcTrialsPlot.getPDFBinHeights();
        mcTrialsPlot.drawHistogram(g, binHeights, null, histoPlotX, histoPlotY, histoWidth, histoWidth);
    }

    private void drawIndConstraints(Graphics2D g, int x, int y, int w, int h) {
        for (int i = 0; i < Session.getIndicatorNum(); ++i) {
            Indicator ithIndicator = Session.getIndicator(i);
            Color paintColor = Constants.CONSTRAINT_COLOR;

            if (ithIndicator.isMaxBest())
                paintLowerBoundry(g, x, y, w, h, ithIndicator, paintColor, ithIndicator.getConstraintValue());
            else
                paintUpperBoundry(g, x, y, w, h, ithIndicator, paintColor, ithIndicator.getConstraintValue());
        }
    }

    private void drawMngOptionConstraints(Graphics2D g, int x, int y, int w, int h) {
        double xMin = xMngOption.getMin();
        double xMax = xMngOption.getMax();
        double xMinConstraint = xMngOption.getMinConstraint();
        double xMaxConstraint = xMngOption.getMaxConstraint();
        int localXMinConstraint = (int)(w * (xMinConstraint - xMin) / (xMax - xMin));
        int localXMaxConstraint = (int)(w * (xMax - xMaxConstraint) / (xMax - xMin));
        g.fillRect(x, y, localXMinConstraint, h);
        g.fillRect(x + w - localXMaxConstraint, y, localXMaxConstraint, h);

        double yMin = yMngOption.getMin();
        double yMax = yMngOption.getMax();
        double yMinConstraint = yMngOption.getMinConstraint();
        double yMaxConstraint = yMngOption.getMaxConstraint();
        int localYMinConstraint = (int)(h * (yMinConstraint - yMin) / (yMax - yMin));
        int localYMaxConstraint = (int)(h * (yMax - yMaxConstraint) / (yMax - yMin));
        g.fillRect(x, y + h - localYMinConstraint, w, localYMinConstraint);
        g.fillRect(x, y, w, localYMaxConstraint);
    }

    private void drawProbConstraints(Graphics2D g, int x, int y, int w, int h) {
        if (!Session.showProbCrossesOnCPs)
            return;

        //if (indicator.isDefaultProbabilityValue())
        //    return;


        for (DataItem dataItem : Session.getDataItems()) {
            boolean fullfills = true;
            for (Indicator ind : Session.getIndicators()) {
                if (!dataItem.isFullFillsProbability(ind, ind.getProbabilityValue())) {
                    fullfills = false;
                    break;
                }
            }

            if (fullfills)
                continue;

            dataItem.computePDF();
            MngAction mngAction = dataItem.getMngAction();
            double xMngValue = mngAction.getMngValue(xMngOption);
            double xMin = xMngOption.getMin();
            double xMax = xMngOption.getMax();
            int xLocal = x + (int) (w * (xMngValue - xMin) / (xMax - xMin));

            double yMngValue = mngAction.getMngValue(yMngOption);
            double yMin = yMngOption.getMin();
            double yMax = yMngOption.getMax();
            int yLocal = y + h - (int) (h * (yMngValue - yMin) / (yMax - yMin));

            int extensionValue = 3;
            g.setStroke(new BasicStroke(3.0f));
            g.setColor(Color.LIGHT_GRAY);
            g.drawLine(xLocal - extensionValue, yLocal - extensionValue,
                       xLocal + extensionValue, yLocal + extensionValue);
            g.drawLine(xLocal - extensionValue, yLocal + extensionValue,
                       xLocal + extensionValue, yLocal - extensionValue);

            g.setStroke(new BasicStroke(1.0f));
            g.setColor(Constants.MAIN_COLOR);
            g.drawLine(xLocal - extensionValue, yLocal - extensionValue,
                       xLocal + extensionValue, yLocal + extensionValue);
            g.drawLine(xLocal - extensionValue, yLocal + extensionValue,
                       xLocal + extensionValue, yLocal - extensionValue);
        }
    }

    private void drawGrids(Graphics2D g, int x, int y, int w, int h) {
        if (OptionsPanel.viewGrid) {
            int xGridPoints = xMngOption.getValueNum();
            int yGridPoints = yMngOption.getValueNum();

            g.setColor(Constants.MAIN_COLOR);
            if (!OptionsPanel.viewMCUncertainty) {
                for (int i = 0; i < xGridPoints; ++i) {
                    int xLocal = x + (int) (w * (double) i / (xGridPoints-1));
                    for (int j = 0; j < yGridPoints; ++j) {
                        int yLocal = y + h - (int) (h * (double) j / (yGridPoints-1));
                        int extensionValue = 2;
                        g.drawLine(xLocal - extensionValue, yLocal, xLocal + extensionValue, yLocal);
                        g.drawLine(xLocal, yLocal - extensionValue, xLocal, yLocal + extensionValue);
                        //g.fillOval(xLocal - 2, yLocal - 2, 3, 3);
                    }
                }
            } else {
                double minError = indicator.getMin();
                double maxError = indicator.getMax();

                for (DataItem dataItem : Session.getDataItems()) {
                    dataItem.computePDF();
                    MngAction mngAction = dataItem.getMngAction();
                    double xMngValue = mngAction.getMngValue(xMngOption);
                    double xMin = xMngOption.getMin();
                    double xMax = xMngOption.getMax();
                    int xLocal = x + (int) (w * (xMngValue - xMin) / (xMax - xMin));

                    double yMngValue = mngAction.getMngValue(yMngOption);
                    double yMin = yMngOption.getMin();
                    double yMax = yMngOption.getMax();
                    int yLocal = y + h - (int) (h * (yMngValue - yMin) / (yMax - yMin));

                    double[] errors = dataItem.getErrorBar(indicator, Session.getErrorBarPercentage());
                    double scalingFactor = 4;
                    int radius0 = (int) (scalingFactor * (errors[0] - minError) / (maxError - minError));
                    int radius1 = (int) (scalingFactor * (errors[1] - minError) / (maxError - minError));
                    int totalRadius = radius0 + radius1;
                    totalRadius += 2;

                    g.fillOval(xLocal - radius0, yLocal - radius1, 2*radius0, 2*radius1);
                    g.drawLine(xLocal, yLocal, xLocal, yLocal);
                    //g.drawLine(xLocal - radius0, yLocal, xLocal + radius1, yLocal);
                    //g.drawLine(xLocal - radius0, yLocal, xLocal + radius0, yLocal);
                }
            }
        }
    }

    private void drawVisualRefLines(Graphics2D g, int x, int y, int w, int h) {
        for (VisualRefLine vrl : Session.getVisualRefLines()) {
            MngOption vrlMngOption = vrl.getMngOption();
            double value = vrl.getValue();

            g.setColor(Constants.MAIN_COLOR);
            g.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, new float[]{6f}, 0.0f));
            if (vrlMngOption.equals(xMngOption)) {
                double xMin = xMngOption.getMin();
                double xMax = xMngOption.getMax();
                int localXValue = x + (int)(w * (value - xMin) / (xMax - xMin));
                g.drawLine(localXValue, y, localXValue, y + h);
            } else {
                double yMin = yMngOption.getMin();
                double yMax = yMngOption.getMax();
                int localYValue = y + h - (int)(h * (value - yMin) / (yMax - yMin));
                g.drawLine(x, localYValue, x+w, localYValue);
            }
            g.setStroke(new BasicStroke(1f));
        }
    }

    private void drawBordersTickMarks(Graphics2D g, int x, int y, int w, int h) {
        g.setColor(Constants.MAIN_COLOR);
        g.drawRect(x, y, w, h);

        int labelAxisDistance = 4;
        int xTickNum = 6;
        int xFontSize = Constants.PLOT_FONT.getSize();
        if (xAxisTickMarks == null || previousW != w)
            xAxisTickMarks = TickMarkCalculator.getTickMarkValues(xMngOption.getMin(), xMngOption.getMax(),
                                                        xTickNum, xFontSize, w, //TickMarkCalculator.HOR,
                                                        leftBottomBorderWidth, g);
        double xMinLabel = (Double) xAxisTickMarks[0];
        double xMaxLabel = (Double) xAxisTickMarks[1];
        double xStep = (Double) xAxisTickMarks[2];
        int xFormat = (Integer) xAxisTickMarks[3];
        xFontSize = (Integer) xAxisTickMarks[4];
        int xOrientation = (Integer) xAxisTickMarks[5];

        g.setFont(Constants.getPlotFont(xFontSize));
        xTickNum = (int) ((xMaxLabel - xMinLabel) / xStep);
        for (int i = 0; i <= xTickNum; ++i) {
            String label = TickMarkCalculator.labelFormatter(xMinLabel + i*xStep, xFormat);

            int tickXPosition = x + (int) ((double)w * i /xTickNum);
            int tickYPosition = y + h + labelAxisDistance;

            g.drawLine(tickXPosition, y + h, tickXPosition, tickYPosition);
            if (xOrientation == TickMarkCalculator.HOR)
                Util.drawStringHorCentre(g, label, tickXPosition, tickYPosition + labelAxisDistance);
            else
                Util.drawStringVerTop(g, label, tickXPosition, tickYPosition);
        }

        /**********************************************************************************/

        int yTickNum = 7;
        int yFontSize = Constants.PLOT_FONT.getSize();
        if (yAxisTickMarks == null || previousH != h)
            yAxisTickMarks = TickMarkCalculator.getTickMarkValues(yMngOption.getMin(), yMngOption.getMax(),
                                                        yTickNum, yFontSize, h, //TickMarkCalculator.VER,
                                                        leftBottomBorderWidth, g);
        double yMinLabel = (Double) yAxisTickMarks[0];
        double yMaxLabel = (Double) yAxisTickMarks[1];
        double yStep = (Double) yAxisTickMarks[2];
        int yFormat = (Integer) yAxisTickMarks[3];
        yFontSize = (Integer) yAxisTickMarks[4];
        int yOrientation = (Integer) yAxisTickMarks[5];

        g.setFont(Constants.getPlotFont(yFontSize));
        yTickNum = (int) ((yMaxLabel - yMinLabel) / yStep);
        for (int i = 0; i <= yTickNum; ++i) {
            String label = TickMarkCalculator.labelFormatter(yMinLabel + i*yStep, yFormat);

            int tickXPosition = x - labelAxisDistance;
            int tickYPosition = y + h - (int) ((double)h * i /yTickNum);

            g.drawLine(x, tickYPosition, tickXPosition, tickYPosition);
            if (yOrientation == TickMarkCalculator.VER)
                Util.drawStringHorRight(g, label, tickXPosition, tickYPosition);
            else
                Util.drawStringVerCentreRight(g, label, tickXPosition - labelAxisDistance, tickYPosition);
        }
    }

    private void drawScenarios(Graphics2D g, int x, int y, int w, int h) {
        scenarioLocations.clear();
        for (Scenario scenario : Session.getScenarios()) {
            if (!scenario.isVisible())
                continue;

            int xLocal = (int)(x + w * (scenario.getMngAction().getMngValue(xMngOption) / (xMngOption.getMax() - xMngOption.getMin())));
            int yLocal = (int)(y + h - h * (scenario.getMngAction().getMngValue(yMngOption) / (yMngOption.getMax() - yMngOption.getMin())));
            scenarioLocations.put(scenario, new Point(xLocal, yLocal));

            int radius = sceranioRadius;
            if (Session.getSelectedScenario() != null && scenario.equals(Session.getSelectedScenario()))
                radius += 1;

            g.setColor(Constants.MAIN_COLOR);
            g.fillOval(xLocal - (radius+1), yLocal - (radius+1), 2*(radius+1), 2*(radius+1));

            g.setColor(scenario.getColor());
            g.fillOval(xLocal - radius, yLocal - radius, 2*radius, 2*radius);
        }
    }

    public void setSeeMCTrials(boolean seeMCTrials) {
        this.seeMCTrials = seeMCTrials;
    }

    public void setCursorLocalX(int mouseEventX) {
        cursorLocalX = mouseEventX;
    }

    public void setCursorLocalY(int mouseEventY) {
        cursorLocalY = mouseEventY;
    }

    public void calcCursorXY(MouseEvent mouseEvent) {
        double xMngMin = xMngOption.getMin();
        double xMngMax = xMngOption.getMax();
        double localX = mouseEvent.getX() - x;
        cursorPlotX = ((localX * 1.0) / w) * (xMngMax - xMngMin);
        if (!(xMngMin <= cursorPlotX && cursorPlotX <= xMngMax))
            cursorPlotX = Constants.NA;

        double yMngMin = yMngOption.getMin();
        double yMngMax = yMngOption.getMax();
        double localY = h - (mouseEvent.getY() - y);
        cursorPlotY = ((localY * 1.0) / h) * (yMngMax - yMngMin);
        if (!(yMngMin <= cursorPlotY && cursorPlotY <= yMngMax))
            cursorPlotY = Constants.NA;
    }

    public void calcLocalXY() {
        if (!Util.isNaN(cursorPlotX) && !Util.isNaN(cursorPlotY)) {
            setCursorLocalX((int)(x + w * (cursorPlotX / (xMngOption.getMax() - xMngOption.getMin()))));
            setCursorLocalY((int)(y + h - h * (cursorPlotY / (yMngOption.getMax() - yMngOption.getMin()))));
        }
    }

    public void createScenario() {
        if (!Util.isNaN(cursorPlotX) && !Util.isNaN(cursorPlotY))
            Session.createScenario(cursorPlotX, cursorPlotY);
        Vismon.getVismonInstance().updatePlots();
    }

    public void setCrossHairScenarion() {
        if (!Util.isNaN(cursorPlotX) && !Util.isNaN(cursorPlotY))
            Session.setCrosshairScenario(cursorPlotX, cursorPlotY);
        else
            Session.setCrosshairScenario(null);
    }

    public void showScenarioName() {
        String toolTipText = "";
        for (Scenario scenario : Session.getScenarios()) {
            Point scenarioPoint = scenarioLocations.get(scenario);
            if (scenarioPoint == null)
                continue;

            int xScenario = (int) scenarioPoint.getX();
            int yScenario = (int) scenarioPoint.getY();
            if (xScenario - sceranioRadius <= cursorLocalX && cursorLocalX <= xScenario + sceranioRadius &&
                yScenario - sceranioRadius <= cursorLocalY && cursorLocalY <= yScenario + sceranioRadius) {
                toolTipText += scenario.getName();
                break;
            }
        }
        setToolTipText(toolTipText);
    }

    public void paintLowerBoundry(Graphics2D g, int x, int y, int w, int h, Indicator ind, Color c, double isoLV) {
        g.setColor(c);

        //TODO
        for (Map.Entry<ContourPixel, Double> entry : interpolatedData.get(Session.getIndicatorRank(ind)).entrySet()) {
            double[][][] xyPoints = paintLowerCell(entry, ind, isoLV);
            int n1 = 0;
            int n2 = 0;
            for (int i = 0; i < 6; ++i) {
                if (xyPoints[0][0][i] != -1.0) {
                    ++n1;
                }
                if (xyPoints[1][0][i] != -1.0) {
                    ++n2;
                }
            }

            int[][] realPoints = new int[2][n1];
            for (int i = 0; i < n1; ++i) {
                realPoints[0][i] = x + (int)(w * (xyPoints[0][0][i] * 0.1));
                realPoints[1][i] = y + (int)(h * (1 - (xyPoints[0][1][i] * 0.1)));
            }
            if (n2 > 0) {
                int[][] realPoints2 = new int[2][n2];
                for (int i = 0; i < n2; ++i) {
                    realPoints2[0][i] = x + (int)(w * (xyPoints[1][0][i] * 0.1));
                    realPoints2[1][i] = y + (int)(h * (1 - (xyPoints[1][1][i] * 0.1)));
                }
                g.fillPolygon(realPoints2[0], realPoints2[1], n2);
            }

            if (n1 != 0) {
                g.fillPolygon(realPoints[0], realPoints[1], n1);
            }
        }
    }

    public void paintUpperBoundry(Graphics2D g, int x, int y, int w, int h, Indicator ind, Color c, double isoLV) {
        g.setColor(c);

        //TODO
        for (Map.Entry<ContourPixel, Double> entry : interpolatedData.get(Session.getIndicatorRank(ind)).entrySet()) {
            double[][][] xyPoints = paintUpperCell(entry, ind, isoLV);
            int n1 = 0;
            int n2 = 0;
            for (int i = 0; i < 6; ++i) {
                if (xyPoints[0][0][i] != -1.0) {
                    ++n1;
                }
                if (xyPoints[1][0][i] != -1.0) {
                    ++n2;
                }
            }

            int[][] realPoints = new int[2][n1];
            for (int i = 0; i < n1; ++i) {
                realPoints[0][i] = x + (int)(w * (xyPoints[0][0][i] * 0.1));
                realPoints[1][i] = y + (int)(h * (1 - (xyPoints[0][1][i] * 0.1)));
            }
            if (n2 > 0) {
                int[][] realPoints2 = new int[2][n2];
                for (int i = 0; i < n2; ++i) {
                    realPoints2[0][i] = x + (int)(w * (xyPoints[1][0][i] * 0.1));
                    realPoints2[1][i] = y + (int)(h * (1 - (xyPoints[1][1][i] * 0.1)));
                }
                g.fillPolygon(realPoints2[0], realPoints2[1], n2);
            }

            if (n1 != 0) {
                g.fillPolygon(realPoints[0], realPoints[1], n1);
            }

        }
    }

    private double[][][] paintLowerCell(Map.Entry<ContourPixel, Double> entry, Indicator ind, double isoLV) {

        double[][][] xyPoints = new double[2][2][6];

        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 6; ++i) {
                xyPoints[0][j][i] = -1.0;
                xyPoints[1][j][i] = -1.0;
            }
        }

        ContourPixel right = getRight(entry.getKey());
        ContourPixel up = getUp(entry.getKey());
        ContourPixel up_right = getUpRight(entry.getKey());
        if (right == null) {
            return xyPoints;
        }
        if (up == null) {
            return xyPoints;
        }

        //checking the values
        double v0 = entry.getValue();
        double v1 = getOneInterpolatedData(ind, right);
        double v2 = getOneInterpolatedData(ind, up);
        double v3 = getOneInterpolatedData(ind, up_right);

        if (Util.isNaN(v0) || Util.isNaN(v1) || Util.isNaN(v2) || Util.isNaN(v3))
            return xyPoints;

        if (v0 < isoLV && v1 < isoLV && v2 < isoLV && v3 < isoLV) {
            xyPoints[0][0][0] = entry.getKey().getX();
            xyPoints[0][1][0] = entry.getKey().getY();
            xyPoints[0][0][1] = right.getX();
            xyPoints[0][1][1] = right.getY();
            xyPoints[0][0][2] = up_right.getX();
            xyPoints[0][1][2] = up_right.getY();
            xyPoints[0][0][3] = up.getX();
            xyPoints[0][1][3] = up.getY();
            //System.out.println("\tCase1");
            return xyPoints;
        } else if (v0 >= isoLV && v1 >= isoLV && v2 >= isoLV && v3 >= isoLV) {
            //System.out.println("\tCase2");
            return xyPoints;
        } else if (v0 <= isoLV && v1 >= isoLV) {
            if (v2 > isoLV && v3 > isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX();
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][2] = entry.getKey().getX();
                xyPoints[0][1][2] = entry.getKey().getY();
                //System.out.println("\tCase3");
                return xyPoints;
            } else if (v2 <= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                //System.out.println("\tCase4");
                return xyPoints;
            } else if (v2 <= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = up.getX();
                xyPoints[0][1][3] = up.getY();
                xyPoints[0][0][4] = entry.getKey().getX();
                xyPoints[0][1][4] = entry.getKey().getY();
                //System.out.println("\tCase5");
                return xyPoints;
            } else {
                if (getOneInterpolatedData(ind, entry.getKey().getX() + 0.5, entry.getKey().getY() + 0.5) <= isoLV) {
                    //first line
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][2] = up_right.getX();
                    xyPoints[0][1][2] = up_right.getY();
                    xyPoints[0][0][3] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[0][1][3] = entry.getKey().getY() + 1.0;
                    xyPoints[0][0][4] = entry.getKey().getX();
                    xyPoints[0][1][4] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][5] = entry.getKey().getX();
                    xyPoints[0][1][5] = entry.getKey().getY();
                    //System.out.println("\tCase6.5");
                    return xyPoints;
                } else {
                    //first triangle
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX();
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][2] = entry.getKey().getX();
                    xyPoints[0][1][2] = entry.getKey().getY();
                    //second triangle
                    xyPoints[1][0][0] = entry.getKey().getX() + 1.0;
                    xyPoints[1][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[1][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[1][1][1] = entry.getKey().getY() + 1.0;
                    xyPoints[1][0][2] = up_right.getX();
                    xyPoints[1][1][2] = up_right.getY();
                    //System.out.println("\tCase6.0");
                    return xyPoints;
                }
            }
        } else if (v0 >= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 < isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX();
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = up_right.getX();
                xyPoints[0][1][3] = up_right.getY();
                xyPoints[0][0][4] = right.getX();
                xyPoints[0][1][4] = right.getY();
                //System.out.println("\tCase7");
                return xyPoints;
            } else if (v2 >= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = right.getX();
                xyPoints[0][1][3] = right.getY();
                //System.out.println("\tCase8");
                return xyPoints;
            } else if (v2 >= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = right.getX();
                xyPoints[0][1][2] = right.getY();
                //System.out.println("\tCase9");
                return xyPoints;
            } else {
                if (getOneInterpolatedData(ind, entry.getKey().getX() + 0.5, entry.getKey().getY() + 0.5) >= isoLV) {
                    //first triangle
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][2] = right.getX();
                    xyPoints[0][1][2] = right.getY();
                    //second triangle
                    xyPoints[1][0][0] = entry.getKey().getX();
                    xyPoints[1][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[1][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[1][1][1] = entry.getKey().getY() + 1.0;
                    xyPoints[1][0][2] = up.getX();
                    xyPoints[1][1][2] = up.getY();
                    //System.out.println("\tCase10.5");
                    return xyPoints;
                } else {
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX();
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][2] = up.getX();
                    xyPoints[0][1][2] = up.getY();
                    xyPoints[0][0][3] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[0][1][3] = entry.getKey().getY() + 1.0;
                    xyPoints[0][0][4] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][4] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][5] = right.getX();
                    xyPoints[0][1][5] = right.getY();
                    //System.out.println("\tCase10.0");
                    return xyPoints;
                }
            }
        } else if (v0 <= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 > isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                xyPoints[0][0][4] = right.getX();
                xyPoints[0][1][4] = right.getY();
                //System.out.println("\tCase12");
                return xyPoints;
            } else if (v2 >= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = right.getX();
                xyPoints[0][1][3] = right.getY();
                xyPoints[0][0][4] = entry.getKey().getX();
                xyPoints[0][1][4] = entry.getKey().getY();
                //System.out.println("\tCase11");
                return xyPoints;
            } else if (v2 >= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = right.getX();
                xyPoints[0][1][2] = right.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                //System.out.println("\tCase13");
                return xyPoints;
            }
        } else if (v0 >= isoLV && v1 >= isoLV) {
            if (v2 > isoLV && v3 < isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                //System.out.println("\tCase14");
                return xyPoints;
            } else if (v2 <= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                //System.out.println("\tCase15");
                return xyPoints;
            } else if (v2 <= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = up.getX();
                xyPoints[0][1][3] = up.getY();
                //System.out.println("\tCase16");
                return xyPoints;
            }
        }

        return xyPoints;

    }

    private double[][][] paintUpperCell(Map.Entry<ContourPixel, Double> entry, Indicator ind, double isoLV) {

        double[][][] xyPoints = new double[2][2][6];

        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 6; ++i) {
                xyPoints[0][j][i] = -1.0;
                xyPoints[1][j][i] = -1.0;
            }
        }

        ContourPixel right = getRight(entry.getKey());
        ContourPixel up = getUp(entry.getKey());
        ContourPixel up_right = getUpRight(entry.getKey());
        if (right == null) {
            return xyPoints;
        }
        if (up == null) {
            return xyPoints;
        }

        //checking the values
        double v0 = entry.getValue();
        double v1 = getOneInterpolatedData(ind, right);
        double v2 = getOneInterpolatedData(ind, up);
        double v3 = getOneInterpolatedData(ind, up_right);

        if (Util.isNaN(v0) || Util.isNaN(v1) || Util.isNaN(v2) || Util.isNaN(v3))
            return xyPoints;

        if (v0 <= isoLV && v1 <= isoLV && v2 <= isoLV && v3 <= isoLV) {
            //System.out.println("\tCase1");
            return xyPoints;
        } else if (v0 > isoLV && v1 > isoLV && v2 > isoLV && v3 > isoLV) {
            xyPoints[0][0][0] = entry.getKey().getX();
            xyPoints[0][1][0] = entry.getKey().getY();
            xyPoints[0][0][1] = right.getX();
            xyPoints[0][1][1] = right.getY();
            xyPoints[0][0][2] = up_right.getX();
            xyPoints[0][1][2] = up_right.getY();
            xyPoints[0][0][3] = up.getX();
            xyPoints[0][1][3] = up.getY();
            //System.out.println("\tCase2");
            return xyPoints;
        } else if (v0 <= isoLV && v1 >= isoLV) {
            if (v2 > isoLV && v3 > isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX();
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = up_right.getX();
                xyPoints[0][1][3] = up_right.getY();
                xyPoints[0][0][4] = right.getX();
                xyPoints[0][1][4] = right.getY();
                //System.out.println("\tCase3");
                return xyPoints;
            } else if (v2 <= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = right.getX();
                xyPoints[0][1][3] = right.getY();
                //System.out.println("\tCase4");
                return xyPoints;
            } else if (v2 <= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = right.getX();
                xyPoints[0][1][2] = right.getY();
                //System.out.println("\tCase5");
                return xyPoints;
            } else {
                if (getOneInterpolatedData(ind, entry.getKey().getX() + 0.5, entry.getKey().getY() + 0.5) <= isoLV) {
                    //first triangle
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][2] = right.getX();
                    xyPoints[0][1][2] = right.getY();
                    //second triangle
                    xyPoints[1][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[1][1][0] = entry.getKey().getY() + 1.0;
                    xyPoints[1][0][1] = entry.getKey().getX();
                    xyPoints[1][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[1][0][2] = up.getX();
                    xyPoints[1][1][2] = up.getY();
                    //System.out.println("\tCase6.5");
                    return xyPoints;
                } else {
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX();
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][2] = up.getX();
                    xyPoints[0][1][2] = up.getY();
                    xyPoints[0][0][3] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[0][1][3] = entry.getKey().getY() + 1.0;
                    xyPoints[0][0][4] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][4] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][5] = right.getX();
                    xyPoints[0][1][5] = right.getY();
                    //System.out.println("\tCase6.0");
                    return xyPoints;
                }
            }
        } else if (v0 >= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 < isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX();
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][2] = entry.getKey().getX();
                xyPoints[0][1][2] = entry.getKey().getY();
                //System.out.println("\tCase7");
                return xyPoints;
            } else if (v2 >= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                //System.out.println("\tCase8");
                return xyPoints;
            } else if (v2 >= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                xyPoints[0][1][0] = entry.getKey().getY();
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = up.getX();
                xyPoints[0][1][3] = up.getY();
                xyPoints[0][0][4] = entry.getKey().getX();
                xyPoints[0][1][4] = entry.getKey().getY();
                //System.out.println("\tCase9");
                return xyPoints;
            } else {
                if (getOneInterpolatedData(ind, entry.getKey().getX() + 0.5, entry.getKey().getY() + 0.5) >= isoLV) {
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[0][0][2] = up_right.getX();
                    xyPoints[0][1][2] = up_right.getY();
                    xyPoints[0][0][3] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[0][1][3] = entry.getKey().getY() + 1.0;
                    xyPoints[0][0][4] = entry.getKey().getX();
                    xyPoints[0][1][4] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][5] = entry.getKey().getX();
                    xyPoints[0][1][5] = entry.getKey().getY();
                    //System.out.println("\tCase10.5");
                    return xyPoints;
                } else {
                    //first triangle
                    xyPoints[0][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    xyPoints[0][1][0] = entry.getKey().getY();
                    xyPoints[0][0][1] = entry.getKey().getX();
                    xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    xyPoints[0][0][2] = entry.getKey().getX();
                    xyPoints[0][1][2] = entry.getKey().getY();
                    //second triangle
                    xyPoints[1][0][0] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    xyPoints[1][1][0] = entry.getKey().getY() + 1.0;
                    xyPoints[1][0][1] = entry.getKey().getX() + 1.0;
                    xyPoints[1][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    xyPoints[1][0][2] = up_right.getX();
                    xyPoints[1][1][2] = up_right.getY();
                    //System.out.println("\tCase10.0");
                    return xyPoints;
                }
            }
        } else if (v0 <= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 > isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                //System.out.println("\tCase12");
                return xyPoints;
            } else if (v2 >= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                //System.out.println("\tCase11");
                return xyPoints;
            } else if (v2 >= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = up.getX();
                xyPoints[0][1][3] = up.getY();
                //System.out.println("\tCase13");
                return xyPoints;
            }
        } else if (v0 >= isoLV && v1 >= isoLV) {
            if (v2 > isoLV && v3 < isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up.getX();
                xyPoints[0][1][2] = up.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                xyPoints[0][0][4] = right.getX();
                xyPoints[0][1][4] = right.getY();
                //System.out.println("\tCase14");
                return xyPoints;
            } else if (v2 <= isoLV && v3 >= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0;
                xyPoints[0][0][2] = up_right.getX();
                xyPoints[0][1][2] = up_right.getY();
                xyPoints[0][0][3] = right.getX();
                xyPoints[0][1][3] = right.getY();
                xyPoints[0][0][4] = entry.getKey().getX();
                xyPoints[0][1][4] = entry.getKey().getY();
                //System.out.println("\tCase15");
                return xyPoints;
            } else if (v2 <= isoLV && v3 <= isoLV) {
                xyPoints[0][0][0] = entry.getKey().getX();
                xyPoints[0][1][0] = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                xyPoints[0][0][1] = entry.getKey().getX() + 1.0;
                xyPoints[0][1][1] = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                xyPoints[0][0][2] = right.getX();
                xyPoints[0][1][2] = right.getY();
                xyPoints[0][0][3] = entry.getKey().getX();
                xyPoints[0][1][3] = entry.getKey().getY();
                //System.out.println("\tCase16");
                return xyPoints;
            }
        }

        return xyPoints;

    }

    public void drawIsoLine(Graphics2D g, int x, int y, int w, int h, double isoLV) {
        boolean pieceNumCalculated = true;
        if (!isolinePieceNum.containsKey(isoLV))
            pieceNumCalculated = false;

        int drawLabel = 0;
        int pieceNum = 0;

        for (Map.Entry<ContourPixel, Double> entry : interpolatedData.get(Session.getIndicatorRank(indicator)).entrySet()) {
            double[][] lineEndPoints = partialLine(entry, isoLV);

            double scale = 1.0 / (Math.pow(Session.getDataItems().size(), 0.5) - 1);
            double lineX0 = x + w * (lineEndPoints[0][0] * scale);
            double lineY0 = y + h * (1 - (lineEndPoints[0][1] * scale));
            double lineX1 = x + w * (lineEndPoints[0][2] * scale);
            double lineY1 = y + h * (1 - (lineEndPoints[0][3] * scale));

            g.setColor(Constants.MAIN_COLOR);
            if (lineEndPoints[0][1] == -1) {
                continue;
            } else {
                if (pieceNumCalculated && w > MIN_DIM_FOR_ISOLINE_VALUES && h > MIN_DIM_FOR_ISOLINE_VALUES
                    && drawLabel == (int) (0.5 * isolinePieceNum.get(isoLV))) {

                    double dy = lineY1 - lineY0;
                    double dx = lineX1 - lineX0;
                    double angle = Math.atan2(dy, dx);

                    String isoValueString = "";
                    if ((Math.round(indicator.getAvgContourStep()) - indicator.getAvgContourStep()) == 0)
                        isoValueString += (int) isoLV;
                    else
                        isoValueString += Math.round(isoLV * 100) / 100.0;

                    g.setFont(Constants.ISOLINE_FONT);
                    Util.drawStringCentre(g, isoValueString, Util.normalizeAngle(-angle), (int) (0.5 * (lineX0 + lineX1)), (int) (0.5 * (lineY0 + lineY1)));
                } else
                    g.drawLine((int)(lineX0), (int)(lineY0), (int)(lineX1), (int)(lineY1));
                ++pieceNum;
                ++drawLabel;
            }

            if (lineEndPoints[1][0] != -1) {
                double lineX2 = x + w * (lineEndPoints[1][0] * scale);
                double lineY2 = y + h * (1 - (lineEndPoints[1][1] * scale));
                double lineX3 = x + w * (lineEndPoints[1][2] * scale);
                double lineY3 = y + h * (1 - (lineEndPoints[1][3] * scale));
                g.drawLine((int)(lineX2), (int)(lineY2), (int)(lineX3), (int)(lineY3));
                ++pieceNum;
            }
        }
        if (!isolinePieceNum.containsKey(isoLV))
            isolinePieceNum.put(isoLV, pieceNum);

    }

    private double[][] partialLine(Map.Entry<ContourPixel, Double> entry, double isoLV) {
        double[][] output = new double[2][4];
        for (int i = 0; i < 4; ++i) {
            output[0][i] = -1.0;
            output[1][i] = -1.0;
        }

        ContourPixel right = getRight(entry.getKey());
        ContourPixel up = getUp(entry.getKey());
        ContourPixel up_right = getUpRight(entry.getKey());
        if (right == null) {
            return output;
        }
        if (up == null) {
            return output;
        }

        double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
        double x2 = -1, y2 = -1, x3 = -1, y3 = -1;

        //checking the values
        double v0 = entry.getValue();
        double v1 = getOneInterpolatedData(right);
        double v2 = getOneInterpolatedData(up);
        double v3 = getOneInterpolatedData(up_right);

        if (Util.isNaN(v0) || Util.isNaN(v1) || Util.isNaN(v2) || Util.isNaN(v3))
            return output;

        if (equalD(v0, v1)) {
            v0 += 1e-5;
        }
        if (equalD(v0, v2)) {
            v0 += 1e-5;
        }
        if (equalD(v1, v3)) {
            v1 += 1e-5;
        }
        if (equalD(v2, v3)) {
            v2 += 1e-5;
        }

        if (v0 < isoLV && v1 < isoLV && v2 < isoLV && v3 < isoLV) {
            return output;
        } else if (v0 > isoLV && v1 > isoLV && v2 > isoLV && v3 > isoLV) {
            return output;
        } else if (v0 <= isoLV && v1 >= isoLV) {
            if (v2 >= isoLV && v3 >= isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX();
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                ////System.out.println("\tCase3");
            } else if (v2 <= isoLV && v3 >= isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase4");
            } else if (v2 <= isoLV && v3 <= isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX() + 1.0 ;
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                ////System.out.println("\tCase5");
                if (!(x0 <= 0) && !(x0 >= 0)) {
                    System.out.println("NaN x0: " + v0 + ", " + v1 + " isoLV" + isoLV + " v2 " + v2 + " v3 " + v3);
                }
            } else {
                if (getOneInterpolatedData(entry.getKey().getX() + 0.5 , entry.getKey().getY() + 0.5) <= isoLV) {
                    //first line
                    x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    y0 = entry.getKey().getY();
                    x1 = entry.getKey().getX() + 1.0 ;
                    y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    //second line
                    x2 = entry.getKey().getX();
                    y2 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    x3 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    y3 = entry.getKey().getY() + 1.0 ;
                    ////System.out.println("\tCase6.5");
                } else {
                    //first line
                    x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    y0 = entry.getKey().getY();
                    x1 = entry.getKey().getX();
                    y1 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    //second line
                    x2 = entry.getKey().getX() + 1.0 ;
                    y2 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    x3 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    y3 = entry.getKey().getY() + 1.0 ;
                    ////System.out.println("\tCase6.0");
                }
            }
        } else if (v0 >= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 < isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX();
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                ////System.out.println("\tCase7");
            } else if (v2 >= isoLV && v3 <= isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase8");
            } else if (v2 >= isoLV && v3 >= isoLV) {
                x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                y0 = entry.getKey().getY();
                x1 = entry.getKey().getX() + 1.0 ;
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                ////System.out.println("\tCase9");
            } else {
                if (getOneInterpolatedData(entry.getKey().getX() + 0.5 , entry.getKey().getY() + 0.5) >= isoLV) {
                    //first line
                    x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    y0 = entry.getKey().getY();
                    x1 = entry.getKey().getX() + 1.0 ;
                    y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    //second line
                    x2 = entry.getKey().getX();
                    y2 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    x3 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    y3 = entry.getKey().getY() + 1.0 ;
                    ////System.out.println("\tCase10.5");
                } else {
                    //first line
                    x0 = entry.getKey().getX() + 1.0 * (isoLV - v0) / (v1 - v0);
                    y0 = entry.getKey().getY();
                    x1 = entry.getKey().getX();
                    y1 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                    //second line
                    x2 = entry.getKey().getX() + 1.0 ;
                    y2 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                    x3 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                    y3 = entry.getKey().getY() + 1.0 ;
                    ////System.out.println("\tCase10.0");
                }
            }
        } else if (v0 <= isoLV && v1 <= isoLV) {
            if (v2 < isoLV && v3 > isoLV) {
                x0 = entry.getKey().getX() + 1.0 ;
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase12");
            } else if (v2 >= isoLV && v3 <= isoLV) {
                x0 = entry.getKey().getX();
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase11");
            } else if (v2 >= isoLV && v3 >= isoLV) {
                x0 = entry.getKey().getX();
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                x1 = entry.getKey().getX() + 1.0 ;
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                ////System.out.println("\tCase13");
            }
        } else if (v0 >= isoLV && v1 >= isoLV) {
            if (v2 > isoLV && v3 < isoLV) {
                x0 = entry.getKey().getX() + 1.0 ;
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase14");
            } else if (v2 <= isoLV && v3 >= isoLV) {
                x0 = entry.getKey().getX();
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                x1 = entry.getKey().getX() + 1.0 * (isoLV - v2) / (v3 - v2);
                y1 = entry.getKey().getY() + 1.0 ;
                ////System.out.println("\tCase15");
            } else if (v2 <= isoLV && v3 <= isoLV) {
                x0 = entry.getKey().getX();
                y0 = entry.getKey().getY() + 1.0 * (isoLV - v0) / (v2 - v0);
                x1 = entry.getKey().getX() + 1.0 ;
                y1 = entry.getKey().getY() + 1.0 * (isoLV - v1) / (v3 - v1);
                ////System.out.println("\tCase16");
            }
        }

        output[0][0] = x0;
        output[0][1] = y0;
        output[0][2] = x1;
        output[0][3] = y1;

        output[1][0] = x2;
        output[1][1] = y2;
        output[1][2] = x3;
        output[1][3] = y3;

        return output;

    }

    public double getOneInterpolatedData(ContourPixel cp) {
        Double d = interpolatedData.get(Session.getIndicatorRank(indicator)).get(cp);
        if (d == null) {
            System.out.println("bad value at: " + cp.getX() + ", " + cp.getY());
        }
        return d == null ? 0 : d.doubleValue();
    }

    public double getOneInterpolatedData(double x, double y) {
        if (x > 10.0 || y > 10.0)
            return 0.0;

        List<Map<ContourPixel, Double>> interpolatedData;

        ContourPixel cp = new ContourPixel(x, y);
        if (this.interpolatedData.get(Session.getIndicatorRank(indicator)).containsKey(cp))
            return getOneInterpolatedData(cp);
        else
            return -1;
    }
    
    public double getOneInterpolatedData(Indicator ind, ContourPixel cp) {
        Double d = interpolatedData.get(Session.getIndicatorRank(ind)).get(cp);
        if (d == null) {
            System.out.println("bad value at: " + cp.getX() + ", " + cp.getY());
        }
        return d == null ? 0 : d.doubleValue();
    }

    public double getOneInterpolatedData(Indicator ind, double x, double y) {
        if (x > 10.0 || y > 10.0)
            return 0.0;

        ContourPixel cp = new ContourPixel(x, y);
        if (interpolatedData.get(Session.getIndicatorRank(ind)).containsKey(cp))
            return getOneInterpolatedData(ind, cp);
        else
            return -1;
    }

    private ContourPixel getRight(ContourPixel cp) {
        ContourPixel right = null;
        if ((cp.getX() + 1.0) <= 10.0) {
            right = new ContourPixel(cp.getX() + 1.0, cp.getY());
        }
        return right;
    }

    private ContourPixel getUp(ContourPixel cp) {
        ContourPixel up = null;
        if ((cp.getY() + 1.0) <= 10.0) {
            up = new ContourPixel(cp.getX(), cp.getY() + 1.0);
        }
        return up;
    }

    private ContourPixel getUpRight(ContourPixel cp) {
        ContourPixel up_right = null;
        if ((cp.getX() + 1.0) <= 10.0 && (cp.getY() + 1.0) <= 10.0) {
            up_right = new ContourPixel(cp.getX() + 1.0, cp.getY() + 1.0);
        }
        return up_right;
    }

    public boolean equalD(double d1, double d2) {
        return (d1 - d2) >= -1e-8 && (d1 - d2) <= 1e-8;
    }

    public void computeInterpolatedData() {
        for (int i = 0; i < interpolatedData.size(); ++i)
            interpolatedData.set(i, Computations.computeInterpolatedDataAsCP(xMngOption, yMngOption,
                                 Session.getIndicator(i), 1, OptionsPanel.contoursBasedOn));
    }

    public Indicator getIndicator() {
        return indicator;
    }

    private void showInANewWindow() {
        ContourPlotFrame contourPlotFrame = new ContourPlotFrame(indicator);
        contourPlotFrame.run();
    }

    private void creatScatterPlot() {
        IndicatorSelector selector = new IndicatorSelector(indicator);
        selector.run();
    }
}
