/***************************************************************************
        File                 : Spectrogram.h
        Project              : SciDAVis
--------------------------------------------------------------------
        Copyright            : (C) 2006 by Ion Vasilief
        Email (use @ for *)  : ion_vasilief*yahoo.fr
        Description          : SciDAVis's Spectrogram Class
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/

#ifndef SPECTROGRAM_H
#define SPECTROGRAM_H

#include "Matrix.h"
#include <qwt_raster_data.h>
#include <qwt_plot.h>
#include <qwt_plot_spectrogram.h>
#include <qwt_color_map.h>
#include <cmath>
#include <limits>

class MatrixData;

class Spectrogram : public QwtPlotSpectrogram
{
public:
    Spectrogram();
    Spectrogram(Matrix *m);

    enum ColorMapPolicy { GrayScale, Default, Custom };

    Spectrogram *copy();
    Matrix *matrix() { return d_matrix; };

    int levels() { return (int)contourLevels().size() + 1; };
    void setLevelsNumber(int levels);

    bool hasColorScale();
    int colorScaleAxis() { return color_axis; };
    void showColorScale(int axis, bool on = true);

    int colorBarWidth();
    void setColorBarWidth(int width);

    void setGrayScale();
    void setDefaultColorMap();
    static QwtLinearColorMap defaultColorMap();

    void setCustomColorMap(const QwtLinearColorMap &map);
    void updateData(Matrix *m);

    //! Used when saving a project file
    QString saveToString();

    ColorMapPolicy colorMapPolicy() { return color_map_policy; };

protected:
    //! Pointer to the source data matrix
    Matrix *d_matrix;

    //! Axis used to display the color scale
    int color_axis;

    //! Flags
    ColorMapPolicy color_map_policy;

    QwtLinearColorMap color_map;
};

class MatrixData : public QwtRasterData
{
public:
    MatrixData(Matrix *m) : QwtRasterData(m->boundingRect()), d_matrix(m)
    {
        n_rows = d_matrix->numRows();
        n_cols = d_matrix->numCols();

        d_m = new double *[n_rows];
        for (int l = 0; l < n_rows; ++l)
            d_m[l] = new double[n_cols];

        min_z = std::numeric_limits<double>::max();
        max_z = -std::numeric_limits<double>::max();
        for (int i = 0; i < n_rows; i++)
            for (int j = 0; j < n_cols; j++) {
                // replace NaNs and Infs with average of neighbours
                if (std::isfinite(d_matrix->cell(i, j)))
                    d_m[i][j] = d_matrix->cell(i, j);
                else {
                    double av = 0;
                    unsigned cnt = 0;
                    for (int ii = -1; ii <= 1; ii += 2)
                        for (int jj = -1; jj <= 1; jj += 2)
                            if (std::isfinite(d_matrix->cell(i + ii, j + jj))) {
                                av += d_matrix->cell(i + ii, j + jj);
                                cnt++;
                            }
                    if (cnt > 0)
                        av /= cnt;
                    d_m[i][j] = av;
                }
                min_z = std::min(min_z, d_m[i][j]);
                max_z = std::max(max_z, d_m[i][j]);
            }

        // m->range(&min_z, &max_z);

        x_start = d_matrix->xStart();
        dx = (d_matrix->xEnd() - x_start) / (double)n_cols;

        y_start = d_matrix->yStart();
        dy = (d_matrix->yEnd() - y_start) / (double)n_rows;
    }

    ~MatrixData()
    {
        for (int i = 0; i < n_rows; i++)
            delete[] d_m[i];

        delete[] d_m;
    };

    virtual QwtRasterData *copy() const { return new MatrixData(d_matrix); }

    virtual QwtDoubleInterval range() const { return QwtDoubleInterval(min_z, max_z); }

    virtual QSize rasterHint(const QwtDoubleRect &) const { return QSize(n_cols, n_rows); }

    virtual double value(double x, double y) const;

private:
    //! Pointer to the source data matrix
    Matrix *d_matrix;

    //! Vector used to store in memory the data from the source matrix window
    double **d_m;

    //! Data size
    int n_rows, n_cols;

    //! Min and max values in the source data matrix
    double min_z, max_z;

    //! Data resolution in x(columns) and y(rows)
    double dx, dy;

    //! X axis left value in the data matrix
    double x_start;

    //! Y axis bottom value in the data matrix
    double y_start;
};

#endif
