/*
 * Copyright (c) 2009 Sean C. Rhea (srhea@srhea.net)
 *
 * 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 "MainWindow.h"
#include "AllPlotWindow.h"
#include "AllPlot.h"
#include "MainWindow.h"
#include "RideFile.h"
#include "RideItem.h"
#include "IntervalItem.h"
#include "TimeUtils.h"
#include "Settings.h"
#include "Units.h" // for MILES_PER_KM
#include <qwt_plot_panner.h>
#include <qwt_plot_zoomer.h>
#include <qwt_plot_picker.h>
#include <qwt_plot_marker.h>

AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
    QWidget(mainWindow), mainWindow(mainWindow)
{
    QVBoxLayout *vlayout = new QVBoxLayout;

    QHBoxLayout *showLayout = new QHBoxLayout;
    QLabel *showLabel = new QLabel(tr("Show:"), this);
    showLayout->addWidget(showLabel);

    QCheckBox *showGrid = new QCheckBox(tr("Grid"), this);
    showGrid->setCheckState(Qt::Checked);
    showLayout->addWidget(showGrid);

    showHr = new QCheckBox(tr("Heart Rate"), this);
    showHr->setCheckState(Qt::Checked);
    showLayout->addWidget(showHr);

    showSpeed = new QCheckBox(tr("Speed"), this);
    showSpeed->setCheckState(Qt::Checked);
    showLayout->addWidget(showSpeed);

    showCad = new QCheckBox(tr("Cadence"), this);
    showCad->setCheckState(Qt::Checked);
    showLayout->addWidget(showCad);

    showAlt = new QCheckBox(tr("Altitude"), this);
    showAlt->setCheckState(Qt::Checked);
    showLayout->addWidget(showAlt);

    showPower = new QComboBox();
    showPower->addItem(tr("Power + shade"));
    showPower->addItem(tr("Power - shade"));
    showPower->addItem(tr("No Power"));
    showLayout->addWidget(showPower);

    QHBoxLayout *smoothLayout = new QHBoxLayout;
    QComboBox *comboDistance = new QComboBox();
    comboDistance->addItem(tr("X Axis Shows Time"));
    comboDistance->addItem(tr("X Axis Shows Distance"));
    smoothLayout->addWidget(comboDistance);

    QLabel *smoothLabel = new QLabel(tr("Smoothing (secs)"), this);
    smoothLineEdit = new QLineEdit(this);
    smoothLineEdit->setFixedWidth(40);

    smoothLayout->addWidget(smoothLabel);
    smoothLayout->addWidget(smoothLineEdit);
    smoothSlider = new QSlider(Qt::Horizontal);
    smoothSlider->setTickPosition(QSlider::TicksBelow);
    smoothSlider->setTickInterval(10);
    smoothSlider->setMinimum(2);
    smoothSlider->setMaximum(600);
    smoothLineEdit->setValidator(new QIntValidator(smoothSlider->minimum(),
                                                   smoothSlider->maximum(),
                                                   smoothLineEdit));
    smoothLayout->addWidget(smoothSlider);
    allPlot = new AllPlot(this, mainWindow);
    smoothSlider->setValue(allPlot->smoothing());
    smoothLineEdit->setText(QString("%1").arg(allPlot->smoothing()));

    allZoomer = new QwtPlotZoomer(allPlot->canvas());
    allZoomer->setRubberBand(QwtPicker::RectRubberBand);
    allZoomer->setRubberBandPen(QColor(Qt::black));
    allZoomer->setSelectionFlags(QwtPicker::DragSelection
                                 | QwtPicker::CornerToCorner);
    allZoomer->setTrackerMode(QwtPicker::AlwaysOff);
    allZoomer->setEnabled(true);

    // TODO: Hack for OS X one-button mouse
    // allZoomer->initMousePattern(1);

    // RightButton: zoom out by 1
    // Ctrl+RightButton: zoom out to full size
    allZoomer->setMousePattern(QwtEventPattern::MouseSelect2,
                               Qt::RightButton, Qt::ControlModifier);
    allZoomer->setMousePattern(QwtEventPattern::MouseSelect3,
                               Qt::RightButton);

    allPanner = new QwtPlotPanner(allPlot->canvas());
    allPanner->setMouseButton(Qt::MidButton);

    // TODO: zoomer doesn't interact well with automatic axis resizing


    // Interval selection
    allPicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
                                  QwtPicker::RectSelection | QwtPicker::CornerToCorner|QwtPicker::DragSelection,
                                   QwtPicker::VLineRubberBand,
                                   QwtPicker::ActiveOnly, allPlot->canvas());
    allPicker->setRubberBandPen(QColor(Qt::blue));
    allPicker->setRubberBand(QwtPicker::CrossRubberBand);
    allPicker->setTrackerPen(QColor(Qt::blue));
    // now select rectangles
    allPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::RectSelection | QwtPicker::DragSelection);
    allPicker->setRubberBand(QwtPicker::VLineRubberBand);
    allPicker->setMousePattern(QwtEventPattern::MouseSelect1,
                                   Qt::LeftButton, Qt::ShiftModifier);

    connect(allPicker, SIGNAL(moved(const QPoint &)),
                SLOT(plotPickerMoved(const QPoint &)));
    connect(allPicker, SIGNAL(appended(const QPoint &)),
                SLOT(plotPickerSelected(const QPoint &)));

    allMarker1 = new QwtPlotMarker();
    allMarker1->setLineStyle(QwtPlotMarker::VLine);
    allMarker1->attach(allPlot);
    allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);

    allMarker2 = new QwtPlotMarker();
    allMarker2->setLineStyle(QwtPlotMarker::VLine);
    allMarker2->attach(allPlot);

    allMarker3 = new QwtPlotMarker();
    allMarker3->setLineStyle(QwtPlotMarker::VLine);
    allMarker3->attach(allPlot);

    vlayout->addWidget(allPlot);
    vlayout->addLayout(showLayout);
    vlayout->addLayout(smoothLayout);
    setLayout(vlayout);

    connect(showPower, SIGNAL(currentIndexChanged(int)),
            allPlot, SLOT(showPower(int)));
    connect(showHr, SIGNAL(stateChanged(int)),
            allPlot, SLOT(showHr(int)));
    connect(showSpeed, SIGNAL(stateChanged(int)),
            allPlot, SLOT(showSpeed(int)));
    connect(showCad, SIGNAL(stateChanged(int)),
            allPlot, SLOT(showCad(int)));
    connect(showAlt, SIGNAL(stateChanged(int)),
            allPlot, SLOT(showAlt(int)));
    connect(showGrid, SIGNAL(stateChanged(int)),
            allPlot, SLOT(showGrid(int)));
    connect(comboDistance, SIGNAL(currentIndexChanged(int)),
            allPlot, SLOT(setByDistance(int)));
    connect(smoothSlider, SIGNAL(valueChanged(int)),
            this, SLOT(setSmoothingFromSlider()));
    connect(smoothLineEdit, SIGNAL(editingFinished()),
            this, SLOT(setSmoothingFromLineEdit()));
    connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected()));
    connect(mainWindow, SIGNAL(zonesChanged()), this, SLOT(zonesChanged()));
    connect(mainWindow, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged()));
    connect(mainWindow, SIGNAL(intervalSelected()), this, SLOT(intervalSelected()));
}

void
AllPlotWindow::rideSelected()
{
    RideItem *ride = mainWindow->rideItem();
    if (!ride)
        return;
    clearSelection(); // clear any ride interval selection data
    setAllPlotWidgets(ride);
    allPlot->setData(ride);
    allZoomer->setZoomBase();
}

void
AllPlotWindow::zonesChanged()
{
    allPlot->refreshZoneLabels();
    allPlot->replot();
}

void
AllPlotWindow::intervalsChanged()
{
    allPlot->refreshIntervalMarkers();
    allPlot->replot();
}

void
AllPlotWindow::intervalSelected()
{
    hideSelection();
    allPlot->replot();
}

void
AllPlotWindow::setSmoothingFromSlider()
{
    if (allPlot->smoothing() != smoothSlider->value()) {
        allPlot->setSmoothing(smoothSlider->value());
        smoothLineEdit->setText(QString("%1").arg(allPlot->smoothing()));
    }
}

void
AllPlotWindow::setSmoothingFromLineEdit()
{
    int value = smoothLineEdit->text().toInt();
    if (value != allPlot->smoothing()) {
        allPlot->setSmoothing(value);
        smoothSlider->setValue(value);
    }
}

void
AllPlotWindow::setAllPlotWidgets(RideItem *ride)
{
    if (ride->ride() && ride->ride()->deviceType() != QString("Manual CSV")) {
	const RideFileDataPresent *dataPresent = ride->ride()->areDataPresent();
	showPower->setEnabled(dataPresent->watts);
	showHr->setEnabled(dataPresent->hr);
	showSpeed->setEnabled(dataPresent->kph);
	showCad->setEnabled(dataPresent->cad);
	showAlt->setEnabled(dataPresent->alt);
    }
    else {
	showPower->setEnabled(false);
	showHr->setEnabled(false);
	showSpeed->setEnabled(false);
	showCad->setEnabled(false);
	showAlt->setEnabled(false);
    }
}

void
AllPlotWindow::zoomInterval(IntervalItem *which)
{
    QwtDoubleRect rect;

    if (!allPlot->byDistance()) {
        rect.setLeft(which->start/60);
        rect.setRight(which->stop/60);
    } else {
        rect.setLeft(which->startKM);
        rect.setRight(which->stopKM);
    }
    rect.setTop(allPlot->wattsCurve->maxYValue()*1.1);
    rect.setBottom(0);
    allZoomer->zoom(rect);
}

void
AllPlotWindow::plotPickerSelected(const QPoint &pos)
{
    // set start of selection in xunits (minutes assumed for now)
    setStartSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x()));
}

void
AllPlotWindow::plotPickerMoved(const QPoint &pos)
{
    QString name = QString("Selection #%1 ").arg(selection);
    // set end of selection in xunits (minutes assumed for now)
    setEndSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x()), true, name);
}

void
AllPlotWindow::setStartSelection(double xValue)
{
    selection++;

    if (!allMarker1->isVisible() || allMarker1->xValue() != xValue) {

        allMarker1->hide();
        allMarker2->hide();
        allMarker3->hide();
        allMarker1->setValue(xValue, allPlot->byDistance() ? 0 : 100);
        allMarker1->show();
    }
}

void
AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
{
    bool useMetricUnits = allPlot->useMetricUnits;
    if (!allMarker2->isVisible() || allMarker2->xValue() != xValue) {
        allMarker2->setValue(xValue, allPlot->byDistance() ? 0 : 100);
        allMarker2->show();
        double x1, x2;  // time or distance depending upon mode selected

        // swap to low-high if neccessary
        if (allMarker1->xValue()>allMarker2->xValue()){
            x2 = allMarker1->xValue();
            x1 = allMarker2->xValue();
        } else {
            x1 = allMarker1->xValue();
            x2 = allMarker2->xValue();
        }
        double lwidth=allPlot->transform(QwtPlot::xBottom, x2)-allPlot->transform(QwtPlot::xBottom, x1);

        allMarker3->setValue((x2-x1)/2+x1, 100);
        QColor marker_color = QColor(Qt::blue);
        marker_color.setAlpha(64);
        allMarker3->setLinePen(QPen(QBrush(marker_color), lwidth, Qt::SolidLine)) ;
        //allMarker3->setZ(-1000.0);
        allMarker3->show();

        RideFile tmpRide = RideFile();

        QTreeWidgetItem *which = mainWindow->rideItem();
        RideItem *ride = (RideItem*)which;

        double distance1 = -1;
        double distance2 = -1;
        double duration1 = -1;
        double duration2 = -1;
        double secsMoving = 0;
        double wattsTotal = 0;
        double bpmTotal = 0;
        int arrayLength = 0;


        // if we are in distance mode then x1 and x2 are distances
        // we need to make sure they are in KM for the rest of this
        // code.
        if (allPlot->byDistance() && useMetricUnits == false) {
            x1 *= KM_PER_MILE;
            x2 *=  KM_PER_MILE;
        }

        foreach (const RideFilePoint *point, ride->ride()->dataPoints()) {
            if ((allPlot->byDistance()==true && point->km>=x1 && point->km<x2) ||
                (allPlot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {

                if (distance1 == -1) distance1 = point->km;
                distance2 = point->km;

                if (duration1 == -1) duration1 = point->secs;
                duration2 = point->secs;

                if (point->kph > 0.0)
                   secsMoving += ride->ride()->recIntSecs();
                wattsTotal += point->watts;
                bpmTotal += point->hr;
                ++arrayLength;
            }
        }
        QString s("\n%1%2 %3 %4\n%5%6 %7%8 %9%10");
        s = s.arg(useMetricUnits ? distance2-distance1 : (distance2-distance1)*MILES_PER_KM, 0, 'f', 1);
        s = s.arg((useMetricUnits? "km":"m"));
        s = s.arg(time_to_string(duration2-duration1));
        if (duration2-duration1-secsMoving>1)
            s = s.arg("("+time_to_string(secsMoving)+")");
        else
            s = s.arg("");
        s = s.arg((useMetricUnits ? 1 : MILES_PER_KM) * (distance2-distance1)/secsMoving*3600, 0, 'f', 1);
        s = s.arg((useMetricUnits? "km/h":"m/h"));
        if (wattsTotal>0) {
            s = s.arg(wattsTotal/arrayLength, 0, 'f', 1);
            s = s.arg("W");
        }
        else{
            s = s.arg("");
         s = s.arg("");
        }
        if (bpmTotal>0) {
            s = s.arg(bpmTotal/arrayLength, 0, 'f', 0);
            s = s.arg("bpm");
        }
        else {
            s = s.arg("");
            s = s.arg("");
        }

        allMarker1->setLabel(s);

        if (newInterval) {

            QTreeWidgetItem *allIntervals = mainWindow->mutableIntervalItems();
            int count = allIntervals->childCount();

            // are we adjusting an existing interval? - if so delete it and readd it
            if (count > 0) {
                IntervalItem *bottom = (IntervalItem *) allIntervals->child(count-1);
                if (bottom->text(0).startsWith(name)) delete allIntervals->takeChild(count-1);
            }

            // add average power to the end of the selection name
            name += QString("(%1 watts)").arg(round((wattsTotal && arrayLength) ? wattsTotal/arrayLength : 0));

            QTreeWidgetItem *last = new IntervalItem(ride->ride(), name, duration1, duration2, distance1, distance2);
            allIntervals->addChild(last);

            // now update the RideFileIntervals and all the plots etc
            mainWindow->updateRideFileIntervals();
        }
    }
}

void
AllPlotWindow::clearSelection()
{
    selection = 0;
    hideSelection();
}

void
AllPlotWindow::hideSelection()
{
    allMarker1->setVisible(false);
    allMarker2->setVisible(false);
    allMarker3->setVisible(false);
    allPlot->replot();
}

