
#include "PerformanceManagerWindow.h"
#include "MainWindow.h"
#include "Context.h"
#include "Athlete.h"
#include "PerfPlot.h"
#include "Colors.h"
#include "StressCalculator.h"
#include "RideItem.h"
#include <qwt_picker_machine.h>


PerformanceManagerWindow::PerformanceManagerWindow(Context *context) :
    GcWindow(context), context(context), active(false), isfiltered(false)
{
    setControls(NULL);

    days = count = 0;
    sc = NULL;

    QVBoxLayout *vlayout = new QVBoxLayout;

    QHBoxLayout *PMPickerLayout = new QHBoxLayout;

    QLabel *PMSTSLabel = new QLabel(appsettings->value(this, GC_STS_ACRONYM,"STS").toString() + ":", this);
    PMSTSValue = new QLineEdit("0");
    PMSTSValue->setReadOnly(true);
    PMSTSValue->setValidator(new QDoubleValidator(0,500,1,PMSTSValue));
    PMPickerLayout->addWidget(PMSTSLabel);
    PMPickerLayout->addWidget(PMSTSValue);

    QLabel *PMLTSLabel = new QLabel(appsettings->value(this, GC_LTS_ACRONYM,"LTS").toString() + ":", this);

    PMLTSValue = new QLineEdit("0");
    PMLTSValue->setReadOnly(true);
    PMLTSValue->setValidator(new QDoubleValidator(0,500,1,PMLTSValue));
    PMPickerLayout->addWidget(PMLTSLabel);
    PMPickerLayout->addWidget(PMLTSValue);

    QLabel *PMSBLabel = new QLabel(appsettings->value(this, GC_SB_ACRONYM,"SB").toString() + ":", this);

    PMSBValue = new QLineEdit("0");
    PMSBValue->setReadOnly(true);
    PMSBValue->setValidator(new QDoubleValidator(-500,500,1,PMSBValue));
    PMPickerLayout->addWidget(PMSBLabel);
    PMPickerLayout->addWidget(PMSBValue);

    QLabel *PMDayLabel = new QLabel(tr("Day:"), this);
    PMDayValue = new QLineEdit(tr("no data"));
    PMPickerLayout->addWidget(PMDayLabel);
    PMPickerLayout->addWidget(PMDayValue);

    metricCombo = new QComboBox(this);
    metricCombo->addItem(tr("Use TSS"), "coggan_tss");
    metricCombo->addItem(tr("Use Aerobic TISS"), "atiss_score");
    metricCombo->addItem(tr("Use BikeScore"), "skiba_bike_score");
    metricCombo->addItem(tr("Use DanielsPoints"), "daniels_points");
    metricCombo->addItem(tr("Use TRIMP"), "trimp_points");
    metricCombo->addItem(tr("Use TRIMP 100"), "trimp_100_points");
    metricCombo->addItem(tr("Use Trimp Zonal"), "trimp_zonal_points");
    metricCombo->addItem(tr("Use Work (Kj)"), "total_work");
    metricCombo->addItem(tr("Use W' Work (Kj)"), "skiba_wprime_exp");
    metricCombo->addItem(tr("Use Distance (km/mi)"), "total_distance");
    QString metricName =
        appsettings->value(this, GC_PERF_MAN_METRIC, "skiba_bike_score").toString();
    for (int i = 0; i < metricCombo->count(); ++i) {
        if (metricCombo->itemData(i).toString() == metricName)
            metricCombo->setCurrentIndex(i);
    }
    metric = metricCombo->itemData(metricCombo->currentIndex()).toString();
    PMPickerLayout->addWidget(metricCombo);

    // date range
    QHBoxLayout *dateRangeLayout = new QHBoxLayout;
    PMdateRangefrom = new QLineEdit("0");
    dateRangeLayout->addWidget(PMdateRangefrom);

    PMleftSlider = new QSlider(Qt::Horizontal);
    PMleftSlider->setTickPosition(QSlider::TicksBelow);
    PMleftSlider->setTickInterval(1);
    PMleftSlider->setMinimum(0);
    dateRangeLayout->addWidget(PMleftSlider);

    QLabel *dateRangeLabel = new QLabel(tr("to"),this);
    dateRangeLayout->addWidget(dateRangeLabel);

    PMrightSlider = new QSlider(Qt::Horizontal);
    PMrightSlider->setTickPosition(QSlider::TicksBelow);
    PMrightSlider->setTickInterval(1);
    PMrightSlider->setMinimum(0);
    dateRangeLayout->addWidget(PMrightSlider);

    PMdateRangeto = new QLineEdit("0");
    dateRangeLayout->addWidget(PMdateRangeto);


    perfplot = new PerfPlot();
    vlayout->addWidget(perfplot);
    vlayout->addLayout(dateRangeLayout);
    vlayout->addLayout(PMPickerLayout);
    setLayout(vlayout);

    PMpicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
                               QwtPicker::VLineRubberBand,
                               QwtPicker::AlwaysOff, perfplot->canvas());
    PMpicker->setStateMachine(new QwtPickerDragPointMachine);
    PMpicker->setRubberBandPen(GColor(CPLOTSELECT));

    connect(PMpicker, SIGNAL(moved(const QPoint &)),
           SLOT(PMpickerMoved(const QPoint &)));
    connect(metricCombo, SIGNAL(currentIndexChanged(int)),
            this, SLOT(metricChanged()));
    connect(context, SIGNAL(configChanged()), this, SLOT(configChanged()));
    connect(context, SIGNAL(configChanged()), perfplot, SLOT(configUpdate()));
    connect(context, SIGNAL(rideAdded(RideItem*)), this, SLOT(replot()));
    connect(context, SIGNAL(rideDeleted(RideItem*)), this, SLOT(replot()));
    connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected()));
#ifdef GC_HAVE_LUCENE
    connect(context, SIGNAL(filterChanged()), this, SLOT(filterChanged()));
#endif

    // set colors
    configChanged();
}

PerformanceManagerWindow::~PerformanceManagerWindow()
{
    if (sc)
	delete sc;
}

#ifdef GC_HAVE_LUCENE
void 
PerformanceManagerWindow::filterChanged()
{
    filter = context->filters;
    isfiltered = context->isfiltered;
    days = 0; // force it
    replot();
    repaint();
}
#endif

void PerformanceManagerWindow::configChanged()
{
    if (active) {
        days = 0; // force replot
        replot();
    }
    PMpicker->setRubberBandPen(GColor(CPLOTSELECT));
    setProperty("color", GColor(CPLOTBACKGROUND));

    QPalette palette;
    palette.setBrush(QPalette::Window, QBrush(GColor(CPLOTBACKGROUND)));
    palette.setBrush(QPalette::Background, QBrush(GColor(CPLOTBACKGROUND)));
    palette.setBrush(QPalette::Base, QBrush(GColor(CPLOTBACKGROUND)));
    palette.setColor(QPalette::WindowText, GCColor::invertColor(GColor(CPLOTBACKGROUND)));
    palette.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND)));
    palette.setColor(QPalette::Normal, QPalette::Window, GCColor::invertColor(GColor(CPLOTBACKGROUND)));
    setPalette(palette);
    setStyleSheet(QString("background-color: %1; color: %2; border: %1")
                    .arg(GColor(CPLOTBACKGROUND).name())
                    .arg(GCColor::invertColor(GColor(CPLOTBACKGROUND)).name()));
}

void PerformanceManagerWindow::metricChanged()
{
    QString newMetric = metricCombo->itemData(metricCombo->currentIndex()).toString();
    appsettings->setValue(GC_PERF_MAN_METRIC, newMetric);
    replot();
}

void PerformanceManagerWindow::rideSelected()
{
    bool wasActive = active;
    active = amVisible();
    if (!wasActive && active)
        replot();
}

void PerformanceManagerWindow::replot()
{
    const QTreeWidgetItem *allRides = context->athlete->allRideItems();

    int newdays, rightIndex, endIndex;
    RideItem *firstRideItem = NULL;
    RideItem *lastRideItem = NULL;
    QDateTime now;


    // calculate the number of days to look at... for now
    // use first ride in allRides to today.  When Season stuff is hooked
    // up, maybe use that, or will allRides reflect only current season?
    if (allRides->childCount()) {
        firstRideItem =  (RideItem*) allRides->child(0);
        lastRideItem =  (RideItem*) allRides->child(allRides->childCount()-1);
    }

    if (firstRideItem) {
        int lookahead = (appsettings->cvalue(context->athlete->cyclist, GC_STS_DAYS,7)).toInt();
        QDateTime endTime = std::max(lastRideItem->dateTime, now.currentDateTime());
        endTime = endTime.addDays( lookahead );
        newdays = firstRideItem->dateTime.daysTo(endTime);
        QString newMetric = metricCombo->itemData(metricCombo->currentIndex()).toString();

        if (newdays != days || allRides->childCount() != count || newMetric != metric) {

	    // days in allRides changed, so recalculate stress
	    //
	    bool firstrun = false;

	    /*
	    fprintf(stderr,
		    "PerformanceManagerWindow::replot: %d days from %s to %s\n",
		    newdays,firstRideItem->dateTime.toString().toAscii().data(),
		    now.currentDateTime().toString().toAscii().data());
		*/

	    if (days == 0)
		firstrun = true;

	    if (sc) delete sc;

	    sc = new StressCalculator(
            context->athlete->cyclist,
		    firstRideItem->dateTime,
		    endTime,
		    (appsettings->cvalue(context->athlete->cyclist, GC_STS_DAYS,7)).toInt(),
		    (appsettings->cvalue(context->athlete->cyclist, GC_LTS_DAYS,42)).toInt());
#ifdef GC_HAVE_LUCENE
            sc->calculateStress(context,context->athlete->home.absolutePath(),newMetric,isfiltered,filter);
#else
            sc->calculateStress(context,context->athlete->home.absolutePath(),newMetric);
#endif

	    perfplot->setStressCalculator(sc);

	    endIndex  = sc->n();
        rightIndex = std::max(0,endIndex-lookahead);


	    PMleftSlider->setMaximum(endIndex);

	    PMrightSlider->setMaximum(endIndex);

	    PMdateRangefrom->setValidator(new QIntValidator(PMleftSlider->minimum(),
			PMleftSlider->maximum(),PMleftSlider));
	    PMdateRangeto->setValidator(new QIntValidator(PMrightSlider->minimum(),
			PMrightSlider->maximum(),PMrightSlider));


	    setPMSliderDates();


	    if (firstrun) {
		// connect slider change handlers only once
		connect(PMleftSlider, SIGNAL(valueChanged(int)),
			this, SLOT(setPMSizeFromSlider()));
		connect(PMrightSlider, SIGNAL(valueChanged(int)),
			this, SLOT(setPMSizeFromSlider()));

		// set slider values  only on the first time
		// set left slider to last 6 months
		if (newdays > 182)
		    PMleftSlider->setValue(newdays - 182);
		else
		    PMleftSlider->setValue(0);

		PMrightSlider->setValue(rightIndex);
	    }

            perfplot->resize(PMleftSlider->value(),PMrightSlider->value());
	    days = newdays;
            metric = newMetric;
	    count = allRides->childCount();
	}
	perfplot->plot();
    }
    repaint();
}

// performance manager picker callback
void
PerformanceManagerWindow::PMpickerMoved(const QPoint &pos)
{
    double day = perfplot->invTransform(QwtPlot::xBottom, pos.x());
    QDateTime date;
    double sts, lts, sb;

    if (day >= perfplot->min() && day < perfplot->max()) {
	// set the date string
	PMDayValue->setText(perfplot->getStartDate().addDays(day).toString(tr("MMM d yyyy")));


	sts = perfplot->getSTS(day);
	QString STSlabel = QString("%1").arg(sts,0,'f',1,0);
	PMSTSValue->setText(STSlabel);

	lts = perfplot->getLTS(day);
	QString LTSlabel = QString("%1").arg(lts,0,'f',1,0);
	PMLTSValue->setText(LTSlabel);

	sb = perfplot->getSB(day);
	QString SBlabel = QString("%1").arg(sb,0,'f',1,0);
	PMSBValue->setText(SBlabel);
    }

}


void
PerformanceManagerWindow::setPMSizeFromSlider()
{
    perfplot->resize(PMleftSlider->value(),PMrightSlider->value());
    setPMSliderDates();
}

void
PerformanceManagerWindow::setPMSliderDates()
{
    PMdateRangefrom->setText(
	perfplot->getStartDate().addDays(PMleftSlider->value()).toString(tr("MMM d yyyy")));
    PMdateRangeto->setText(
	perfplot->getEndDate().addDays(PMrightSlider->value() - perfplot->n()).toString(tr("MMM d yyyy")));
}

