/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

// vim: expandtab

#include <qlayout.h>
#include <qpen.h>
#include <qwt_plot.h>
#include <qwt_scale_widget.h>
#include <qwt_scale_draw.h>
#include "plotmatrix.h"

class PlotMatrix::PrivateData
{
public:
    PrivateData():
        inScaleSync(false)
    {
        isAxisEnabled[QwtPlot::xBottom] = true;
        isAxisEnabled[QwtPlot::xTop] = false;
        isAxisEnabled[QwtPlot::yLeft] = true;
        isAxisEnabled[QwtPlot::yRight] = false;
    }

    bool isAxisEnabled[QwtPlot::axisCnt];
    QVector<QwtPlot *> plotWidgets;
    mutable bool inScaleSync;
};

PlotMatrix::PlotMatrix( int numRows, int numColumns, QWidget *parent):
    QFrame(parent)
{
    d_data = new PrivateData();
    d_data->plotWidgets.resize(numRows * numColumns);

    QGridLayout *layout = new QGridLayout(this);
    for ( int row = 0; row < numRows; row++ )
    {
        for ( int col = 0; col < numColumns; col++ )
        {
            QwtPlot *plot = new QwtPlot(this);
            layout->addWidget(plot, row, col);

            for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
            {
                connect(plot->axisWidget(axis),
                    SIGNAL(scaleDivChanged()), SLOT(scaleDivChanged()) );
            }
            d_data->plotWidgets[row * numColumns + col] = plot;
        }
    }

    updateLayout();
}

PlotMatrix::~PlotMatrix()
{
    delete d_data;
}

int PlotMatrix::numRows() const
{
    const QGridLayout *l = qobject_cast<const QGridLayout *>(layout());
    if ( l )
        return l->rowCount();

    return 0;
}

int PlotMatrix::numColumns() const
{
    const QGridLayout *l = qobject_cast<const QGridLayout *>(layout());
    if ( l )
        return l->columnCount();
    return 0;
}

QwtPlot* PlotMatrix::plot(int row, int column)
{
    const int index = row * numColumns() + column;
    if ( index < (int)d_data->plotWidgets.size() )
        return d_data->plotWidgets[index];

    return NULL;
}

const QwtPlot* PlotMatrix::plot(int row, int column) const
{
    const int index = row * numColumns() + column;
    if ( index < (int)d_data->plotWidgets.size() )
        return d_data->plotWidgets[index];

    return NULL;
}

void PlotMatrix::enableAxis(int axis, bool tf)
{
    if ( axis >= 0 && axis < QwtPlot::axisCnt )
    {
        if ( tf != d_data->isAxisEnabled[axis] )
        {
            d_data->isAxisEnabled[axis] = tf;
            updateLayout();
        }
    }
}

bool PlotMatrix::axisEnabled(int axis) const
{
    if ( axis >= 0 && axis < QwtPlot::axisCnt )
        return d_data->isAxisEnabled[axis];

    return false;
}

void PlotMatrix::setAxisScale(int axis, int rowOrColumn,
    double min, double max, double step)
{
    int row = 0;
    int col = 0;

    if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
        col = rowOrColumn;
    else
        row = rowOrColumn;

    QwtPlot *plt = plot(row, col);
    if ( plt )
    {
        plt->setAxisScale(axis, min, max, step);
        plt->updateAxes();
    }
}

void PlotMatrix::scaleDivChanged()
{
    if ( d_data->inScaleSync )
        return;

    d_data->inScaleSync = true;

    QwtPlot *plt = NULL;
    int axisId = -1;
    int rowOrColumn = -1;

    // find the changed axis
    for ( int row = 0; row < numRows(); row++ )
    {
        for ( int col = 0; col < numColumns(); col++ )
        {
            QwtPlot *p = plot(row, col);
            if ( p )
            {
                for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
                {
                    if ( p->axisWidget(axis) == sender() )
                    {
                        plt = p;
                        axisId = axis;
                        if ( axisId == QwtPlot::xBottom || axisId == QwtPlot::xTop )
                            rowOrColumn = col;
                        else
                            rowOrColumn = row;

                    }
                }
            }
        }
    }

    if ( plt )
    {

        // synchronize the axes
        if ( axisId == QwtPlot::xBottom || axisId == QwtPlot::xTop )
        {
            for ( int row = 0; row < numRows(); row++ )
            {
                QwtPlot *p = plot(row, rowOrColumn);
                if ( p != plt )
                    p->setAxisScaleDiv(axisId, *plt->axisScaleDiv(axisId));
            }
        }
        else
        {
            for ( int col = 0; col < numColumns(); col++ )
            {
                QwtPlot *p = plot(rowOrColumn, col);
                if ( p != plt )
                    p->setAxisScaleDiv(axisId, *plt->axisScaleDiv(axisId));
            }
        }

        updateLayout();
    }

    d_data->inScaleSync = false;
}

void PlotMatrix::updateLayout()
{
    for ( int row = 0; row < numRows(); row++ )
    {
        for ( int col = 0; col < numColumns(); col++ )
        {
            QwtPlot *p = plot(row, col);
            if ( p )
            {
                bool showAxis[QwtPlot::axisCnt];
                showAxis[QwtPlot::xBottom] =
                    axisEnabled(QwtPlot::xBottom) && row == numRows() - 1;
                showAxis[QwtPlot::xTop] =
                    axisEnabled(QwtPlot::xTop) && row == 0;
                showAxis[QwtPlot::yLeft] =
                    axisEnabled(QwtPlot::yLeft) && col == 0;
                showAxis[QwtPlot::yRight] =
                    axisEnabled(QwtPlot::yRight) && col == numColumns() - 1;

                for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
                {
                    if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
                        p->enableAxis(axis, showAxis[axis]);
                    else
                    {
                        p->enableAxis(axis, true);

                        QwtScaleDraw *sd = p->axisScaleDraw(axis);
                        sd->enableComponent(
                            QwtScaleDraw::Backbone, showAxis[axis]);
                        sd->enableComponent(
                            QwtScaleDraw::Ticks, showAxis[axis]);
                        sd->enableComponent(
                            QwtScaleDraw::Labels, showAxis[axis]);
                    }
                }
            }
        }
    }

    for ( int col = 0; col < numColumns(); col++ )
    {
        alignVAxes(col, QwtPlot::yLeft);
        alignVAxes(col, QwtPlot::yRight);
    }

    for ( int row = 0; row < numRows(); row++ )
    {
        for ( int col = 0; col < numColumns(); col++ )
        {
            QwtPlot *p = plot(row, col);
            if ( p )
                p->replot();
        }
    }
}

void PlotMatrix::alignVAxes(int col, int axis)
{
    if ( axis != QwtPlot::yLeft && axis != QwtPlot::yRight )
        return;

    int maxExtent = 0;
    for ( int row = 0; row < numRows(); row++ )
    {
        QwtPlot *p = plot(row, col);
        if ( p )
        {
            QwtScaleWidget *scaleWidget = p->axisWidget(axis);

            QwtScaleDraw *sd = scaleWidget->scaleDraw();
            sd->setMinimumExtent(0);

            const int extent = sd->extent( scaleWidget->font() );
            if ( extent > maxExtent )
                maxExtent = extent;
        }
    }
    for ( int row = 0; row < numRows(); row++ )
    {
        QwtPlot *p = plot(row, col);
        if ( p )
        {
            QwtScaleWidget *scaleWidget = p->axisWidget(axis);
            scaleWidget->scaleDraw()->setMinimumExtent(maxExtent);
        }
    }
}
