package ch.psi.plot;

import java.util.Arrays;
import java.util.logging.Logger;

/**
 * Data representation for MatrixPlot.
 */
public class MatrixPlotSeries extends PlotSeries<MatrixPlot> {

    public interface MatrixPlotSeriesListener {

        void onSeriesSetData(MatrixPlotSeries series, double[][] data);

        void onSeriesAppendData(MatrixPlotSeries series, double x, double y, double z);
    }

    private static final Logger logger = Logger.getLogger(MatrixPlotSeries.class.getName());

    private final double minX;
    private final double maxX;
    private final int numberOfBinsX;
    private final double binWidthX;

    private final double minY;
    private final double maxY;
    private final int numberOfBinsY;
    private final double binWidthY;

    public MatrixPlotSeries(String name, double minX, double maxX, int nX, double minY, double maxY, int nY) {
        super(name);

        this.minX = minX;
        this.maxX = maxX;
        this.numberOfBinsX = nX;

        this.minY = minY;
        this.maxY = maxY;
        this.numberOfBinsY = nY;

        this.binWidthX = (maxX - minX) / (double) (numberOfBinsX - 1);
        this.binWidthY = (maxY - minY) / (double) (numberOfBinsY - 1);

    }

    public void setData(double[][] data) {
        if ((data == null) || (data.length != numberOfBinsY) || (data[0].length != numberOfBinsX)) {
            data = new double[numberOfBinsY][numberOfBinsX];
            for (int i = 0; i < numberOfBinsY; i++) {
                Arrays.fill(data[i], Double.NaN);
            }
        }
        if (getPlot() != null) {
            ((MatrixPlotBase) getPlot()).seriesListener.onSeriesSetData(this, data);
        }
    }

    public void appendData(double x, double y, double z) {
        if (getPlot() != null) {
            ((MatrixPlotBase) getPlot()).seriesListener.onSeriesAppendData(this, x, y, z);
        }

    }

    public double[][] getData() {
        if (getPlot() == null) {
            return new double[0][0];
        }
        return getPlot().getSeriesData(this);
    }

    @Override
    public void clear() {
        setData(null);
    }

    /**
     * @return the binWidthX
     */
    public double getBinWidthX() {
        return binWidthX;
    }

    /**
     * @return the binWidthY
     */
    public double getBinWidthY() {
        return binWidthY;
    }

    /**
     * @return the minX
     */
    public double getMinX() {
        return minX;
    }

    /**
     * @return the maxX
     */
    public double getMaxX() {
        return maxX;
    }

    /**
     * @return the minY
     */
    public double getMinY() {
        return minY;
    }

    /**
     * @return the maxY
     */
    public double getMaxY() {
        return maxY;
    }

    /**
     * @return the numberOfBinsX
     */
    public int getNumberOfBinsX() {
        return numberOfBinsX;
    }

    /**
     * @return the numberOfBinsY
     */
    public int getNumberOfBinsY() {
        return numberOfBinsY;
    }

    //Utilities
    public boolean contains(int indexX, int indexY) {
        return ((indexX <= numberOfBinsX) && (indexX >= 0) && (indexY <= numberOfBinsY) && (indexY >= 0));

    }

    public double[] minMaxZValue() {

        double min = Double.NaN;
        double max = Double.NaN;

        double[][] data = getData();
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[0].length; j++) {
                double val=data[i][j];
                if ((!Double.isNaN(val)) && (!Double.isInfinite(val))) {
                    if (Double.isNaN(min) || (val < min)) {
                        min = val;
                    }
                    if (Double.isNaN(max) || (val > max)) {
                        max = val;
                    }
                }
            }
        }
        return new double[]{min, max};
    }

}
