/***************************************************************************
    File                 : RangeSelectorTool.cpp
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2006,2007 by Ion Vasilief,
                           Tilman Benkert, Knut Franke
    Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net,
                           knut.franke*gmx.de
    Description          : Plot tool for selecting ranges on curves.

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

/***************************************************************************
 *                                                                         *
 *  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                                           *
 *                                                                         *
 ***************************************************************************/
#include "RangeSelectorTool.h"
#include "Graph.h"
#include "Plot.h"
#include "PlotCurve.h"

#include <qwt_symbol.h>
#include <QPoint>
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
#include <QLocale>

RangeSelectorTool::RangeSelectorTool(Graph *graph, const QObject *status_target,
                                     const char *status_slot)
    : QwtPlotPicker(graph->plotWidget()->canvas()), PlotToolInterface(graph)
{
    d_selected_curve = NULL;
    for (int i = d_graph->curves(); i >= 0; --i) {
        d_selected_curve = d_graph->curve(i);
        if (d_selected_curve && d_selected_curve->rtti() == QwtPlotItem::Rtti_PlotCurve
            && d_selected_curve->dataSize() > 0)
            break;
        d_selected_curve = NULL;
    }
    if (!d_selected_curve) {
        QMessageBox::critical(d_graph, tr("Warning"), tr("All the curves on this plot are empty!"));
        return;
    }

    d_active_point = 0;
    d_inactive_point = d_selected_curve->dataSize() - 1;
    int marker_size = 20;
    /* If we want this, it should also be added setSelectedCurve and pointSelected
    if (d_selected_curve->symbol().style() != QwtSymbol::NoSymbol) {
            QSize ssize = d_selected_curve->symbol().size();
            marker_size += qMax(ssize.width(), ssize.height());
    }
    */
    d_active_marker.setSymbol(QwtSymbol(QwtSymbol::Ellipse, QBrush(QColor(255, 255, 0, 128)),
                                        QPen(Qt::red, 2), QSize(marker_size, marker_size)));
    d_active_marker.setLineStyle(QwtPlotMarker::VLine);
    d_active_marker.setLinePen(QPen(Qt::red, 1, Qt::DashLine));
    d_inactive_marker.setSymbol(QwtSymbol(QwtSymbol::Ellipse, QBrush(QColor(255, 255, 0, 128)),
                                          QPen(Qt::black, 2), QSize(marker_size, marker_size)));
    d_inactive_marker.setLineStyle(QwtPlotMarker::VLine);
    d_inactive_marker.setLinePen(QPen(Qt::black, 1, Qt::DashLine));
    d_active_marker.setValue(d_selected_curve->x(d_active_point),
                             d_selected_curve->y(d_active_point));
    d_inactive_marker.setValue(d_selected_curve->x(d_inactive_point),
                               d_selected_curve->y(d_inactive_point));
    d_active_marker.attach(d_graph->plotWidget());
    d_inactive_marker.attach(d_graph->plotWidget());

    setTrackerMode(QwtPicker::AlwaysOn);
    setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection);
    d_graph->plotWidget()->canvas()->setCursor(QCursor(QPixmap(":/vizor.xpm"), -1, -1));
    d_graph->plotWidget()->canvas()->setFocus();
    d_graph->plotWidget()->replot();

    if (status_target)
        connect(this, SIGNAL(statusText(const QString &)), status_target, status_slot);
    emit statusText(
            tr("Click or use Ctrl+arrow key to select range (arrows select active cursor)!"));
}

RangeSelectorTool::~RangeSelectorTool()
{
    d_active_marker.detach();
    d_inactive_marker.detach();
    d_graph->plotWidget()->canvas()->unsetCursor();
    d_graph->plotWidget()->replot();
}

void RangeSelectorTool::pointSelected(const QPoint &pos)
{
    int dist, point;
    const int curve_key = d_graph->plotWidget()->closestCurve(pos.x(), pos.y(), dist, point);
    if (curve_key < 0 || dist >= 5) // 5 pixels tolerance
        return;
    QwtPlotCurve *curve = (QwtPlotCurve *)d_graph->plotWidget()->curve(curve_key);
    if (!curve)
        return;

    if (curve == d_selected_curve)
        setActivePoint(point);
    else {
        // try to be intelligent about selecting the inactive point
        double min_x = curve->data().boundingRect().left();
        double max_x = curve->data().boundingRect().right();
        int n = curve->dataSize();
        double second_x;
        if (curve->x(point) == min_x)
            second_x = max_x;
        else if (curve->x(point) == max_x)
            second_x = min_x;
        else if (d_active_marker.xValue() < d_inactive_marker.xValue())
            second_x = max_x;
        else
            second_x = min_x;
        if (second_x == max_x) { // start at selected point and try larger indices first
            for (int i = 0; i < n; ++i)
                if (qFuzzyCompare(curve->x((i + point) % n), max_x))
                    d_inactive_point = (i + point) % n;
        } else { // start at selected point and try smaller indices first
            for (int i = n - 1; i >= 0; --i)
                if (qFuzzyCompare(curve->x((i + point) % n), max_x))
                    d_inactive_point = (i + point) % n;
        }
        d_selected_curve = curve;
        d_inactive_marker.setValue(curve->x(d_inactive_point), curve->y(d_inactive_point));
        d_active_point = point;
        d_active_marker.setValue(d_selected_curve->x(d_active_point),
                                 d_selected_curve->y(d_active_point));
        emitStatusText();
        emit changed();
    }
    d_graph->plotWidget()->replot();
}

void RangeSelectorTool::setSelectedCurve(QwtPlotCurve *curve)
{
    if (!curve || d_selected_curve == curve)
        return;
    d_selected_curve = curve;
    d_active_point = 0;
    d_inactive_point = d_selected_curve->dataSize() - 1;
    d_active_marker.setValue(d_selected_curve->x(d_active_point),
                             d_selected_curve->y(d_active_point));
    d_inactive_marker.setValue(d_selected_curve->x(d_inactive_point),
                               d_selected_curve->y(d_inactive_point));
    emitStatusText();
    emit changed();
}

void RangeSelectorTool::setActivePoint(int point)
{
    if (point == d_active_point)
        return;
    d_active_point = point;
    d_active_marker.setValue(d_selected_curve->x(d_active_point),
                             d_selected_curve->y(d_active_point));
    emitStatusText();
    emit changed();
}

void RangeSelectorTool::emitStatusText()
{
    if (((PlotCurve *)d_selected_curve)->type() == Graph::Function) {
        emit statusText(
                QString("%1 <=> %2[%3]: x=%4; y=%5")
                        .arg(d_active_marker.xValue() > d_inactive_marker.xValue() ? tr("Right")
                                                                                   : tr("Left"))
                        .arg(d_selected_curve->title().text())
                        .arg(d_active_point + 1)
                        .arg(QLocale().toString(d_selected_curve->x(d_active_point), 'G', 15))
                        .arg(QLocale().toString(d_selected_curve->y(d_active_point), 'G', 15)));
    } else {
        Table *t = ((DataCurve *)d_selected_curve)->table();
        if (!t)
            return;

        int row = ((DataCurve *)d_selected_curve)->tableRow(d_active_point);

        emit statusText(
                QString("%1 <=> %2[%3]: x=%4; y=%5")
                        .arg(d_active_marker.xValue() > d_inactive_marker.xValue() ? tr("Right")
                                                                                   : tr("Left"))
                        .arg(d_selected_curve->title().text())
                        .arg(row + 1)
                        .arg(t->text(row,
                                     t->colIndex(((DataCurve *)d_selected_curve)->xColumnName())))
                        .arg(t->text(row, t->colIndex(d_selected_curve->title().text()))));
    }
}

void RangeSelectorTool::switchActiveMarker()
{
    QwtDoublePoint tmp = d_active_marker.value();
    d_active_marker.setValue(d_inactive_marker.value());
    d_inactive_marker.setValue(tmp);
    int tmp2 = d_active_point;
    d_active_point = d_inactive_point;
    d_inactive_point = tmp2;
    d_graph->plotWidget()->replot();

    emitStatusText();
}

bool RangeSelectorTool::eventFilter(QObject *obj, QEvent *event)
{
    switch (event->type()) {
    case QEvent::KeyPress:
        if (keyEventFilter((QKeyEvent *)event))
            return true;
        break;
    default:
        break;
    }
    return QwtPlotPicker::eventFilter(obj, event);
}

bool RangeSelectorTool::keyEventFilter(QKeyEvent *ke)
{
    switch (ke->key()) {
    case Qt::Key_Up: {
        int n_curves = d_graph->curves();
        int start = d_graph->curveIndex(d_selected_curve) + 1;
        for (int i = start; i < start + n_curves; ++i)
            if (d_graph->curve(i % n_curves)->dataSize() > 0) {
                setSelectedCurve(d_graph->curve(i % n_curves));
                break;
            }
        d_graph->plotWidget()->replot();
        return true;
    }
    case Qt::Key_Down: {
        int n_curves = d_graph->curves();
        int start = d_graph->curveIndex(d_selected_curve) + n_curves - 1;
        for (int i = start; i > start - n_curves; --i)
            if (d_graph->curve(i % n_curves)->dataSize() > 0) {
                setSelectedCurve(d_graph->curve(i % n_curves));
                break;
            }
        d_graph->plotWidget()->replot();
        return true;
    }
    case Qt::Key_Right:
    case Qt::Key_Plus: {
        if (ke->modifiers() & Qt::ControlModifier) {
            int n_points = d_selected_curve->dataSize();
            setActivePoint((d_active_point + 1) % n_points);
            d_graph->plotWidget()->replot();
        } else
            switchActiveMarker();
        return true;
    }
    case Qt::Key_Left:
    case Qt::Key_Minus: {
        if (ke->modifiers() & Qt::ControlModifier) {
            int n_points = d_selected_curve->dataSize();
            setActivePoint((d_active_point - 1 + n_points) % n_points);
            d_graph->plotWidget()->replot();
        } else
            switchActiveMarker();
        return true;
    }
    default:
        break;
    }
    return false;
}
