/*
 * Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
 *
 * 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 "LTMPopup.h"
#include "MainWindow.h"
#include "Athlete.h"
#include "Specification.h"
#include "RideCache.h"
#include "RideFileCache.h" // for RideBest
#include "Utils.h"
#include "Colors.h"

LTMPopup::LTMPopup(Context *context) : QWidget(context->mainWindow), context(context)
{
    // get application settings
    setAutoFillBackground(false);
    setContentsMargins(0,0,0,0);
    setMinimumWidth(800);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(0,0,0,0);
    mainLayout->setSpacing(10 *dpiXFactor); // some space between the vertical Scroll bars
    setStyleSheet(QString::fromUtf8(
        "border-width: 0px; \
         border-color: rgba(255,255,255,0); \
         color: white; \
         background-color: rgba(255, 255, 255, 0); \
         selection-color: rgba(255,255,255,60); \
         selection-background-color: rgba(0,0,255,60);"));

    // title
    QFont titleFont;
    titleFont.setPointSize(14);
    titleFont.setBold(true);
    title = new QLabel(tr("No activity Selected"));
    title->setFont(titleFont);
    title->setFixedHeight(30);
    mainLayout->addWidget(title);

    // ride list...
    rides = new QTableWidget(this);
#ifdef Q_OS_LINUX // QT 4.7 bug on Linux, selection-background-color is ignored (no transparency)
    QStyle *style = QStyleFactory::create("fusion");
    rides->setStyle(style);
#endif
    rides->setFrameStyle(QFrame::NoFrame);
    rides->viewport()->setAutoFillBackground(false);
    QString styleSheet = "::section {"
                     "spacing: 2px;"
                     "color: white;"
                     "background-color: rgba(255,255,255,0);"
                     "border: 0px;"
                     "margin: 0px;"
                     "font-size: 10px; }";
    rides->horizontalHeader()->setStyleSheet(styleSheet);
    rides->verticalHeader()->hide();
    rides->setShowGrid(false);
    rides->setSelectionMode(QAbstractItemView::SingleSelection);
    rides->setSelectionBehavior(QAbstractItemView::SelectRows);
    rides->viewport()->setMouseTracking(true);
    rides->viewport()->installEventFilter(this);
    QFont rideFont;
    rideFont.setPointSize(SMALLFONT);
    rides->setFont(rideFont);
    mainLayout->addWidget(rides, 0, Qt::AlignRight);

    // display the metrics summary here
    metrics = new QTextEdit(this);
    metrics->setReadOnly(true);
    metrics->setFrameStyle(QFrame::NoFrame);
    metrics->setFixedHeight(175);
    metrics->viewport()->setAutoFillBackground(false);
    QFont metricFont;
    metricFont.setPointSize(SMALLFONT);
    metrics->setFont(metricFont);
    mainLayout->addWidget(metrics);

    // display the ride notes here
    notes = new QTextEdit;
    notes->setReadOnly(true);
    notes->setFont(metricFont);
    notes->setFrameStyle(QFrame::NoFrame);
    notes->viewport()->setAutoFillBackground(false);
    mainLayout->addWidget(notes);

    connect(rides, SIGNAL(itemSelectionChanged()), this, SLOT(rideSelected()));
    connect(rides, SIGNAL(itemClicked(QTableWidgetItem*)), this, SLOT(rideOpen()));

}

void
LTMPopup::setTitle(QString s)
{
    title->setText(s);
}

void
LTMPopup::setData(Specification spec, const RideMetric *metric, QString title)
{
    // create the ride list
    int count = 0;
    rides->clear();
    selected.clear();

    // set headings
    rides->setColumnCount(2);

    // when
    QTableWidgetItem *h = new QTableWidgetItem(tr("Date & Time"), QTableWidgetItem::Type);
    rides->setHorizontalHeaderItem(0,h);

    // value & process html encoding of(TM)
    QString name(Utils::unprotect(metric->name()));
    h = new QTableWidgetItem(name, QTableWidgetItem::Type);

    rides->setHorizontalHeaderItem(1,h);

    // now add rows to the table for each entry
    foreach(RideItem *item, context->athlete->rideCache->rides()) {

        if (!spec.pass(item)) continue;

        // what rides were selected ?
        selected << item->fileName;

        // date/time
        QTableWidgetItem *t = new QTableWidgetItem(item->dateTime.toString(tr("ddd, dd MMM yy hh:mm")));
        t->setFlags(t->flags() & (~Qt::ItemIsEditable));
        t->setTextAlignment(Qt::AlignHCenter);
        rides->setRowCount(count+1);
        rides->setItem(count, 0, t);
        rides->setRowHeight(count, 14);

        // metrics
        double d = item->getForSymbol(metric->symbol());
        const_cast<RideMetric *>(metric)->setValue(d);
        QString value = metric->toString(GlobalContext::context()->useMetricUnits);

        h = new QTableWidgetItem(value,QTableWidgetItem::Type);
        h->setFlags(t->flags() & (~Qt::ItemIsEditable));
        h->setTextAlignment(Qt::AlignHCenter);
        rides->setItem(count, 1, h);

        count++;
    }

    // make em all visible! - also show rides if only 1 ride selected
    rides->resizeColumnsToContents();
    rides->setFixedHeight((count > 10 ? 10 : count) * 14 + rides->horizontalHeader()->height());
    // select the first one only if more than 1 rows exist !
    if (count > 1) {
        rides->setRangeSelected(QTableWidgetSelectionRange(0,0,0,1), true);
    }
    // show rides and metrics if at least 1 rides is selected -
    // and even if only 1 Rides (since without the Ride we have not date/time)
    // and the metrics for which the ride was selected
    if (count == 0) {
       // no ride
       rides->hide();
       metrics->hide();
       notes->hide();
       title = tr("No activity selected");
    } else {
       // one or more rides
       rides->show();
       metrics->show();
       notes->show();
    }

    setTitle(title);

    rideSelected();
}

void
LTMPopup::setData(LTMSettings &settings, QDate start, QDate end, QTime time)
{
    // set the title
    QString _title;
    switch (settings.groupBy) {
        case LTM_DAY:
            _title = start.toString(tr("dddd, d MMMM yyyy"));
            break;
        case LTM_MONTH:
            _title = start.toString(tr("MMMM yyyy"));
            break;
        case LTM_YEAR:
            _title = start.toString("yyyy");
            break;
        case LTM_WEEK:
            _title = QString(tr("%1 to %2"))
                    .arg(start.toString(tr("dd MMMM yyyy")))
                    .arg(end.toString(tr("dd MMMM yyyy")));
            break;
        case LTM_TOD:
        case LTM_ALL:
            _title = QString(tr("%1 to %2"))
                    .arg(settings.start.date().toString(tr("dd MMMM yy")))
                    .arg(settings.end.date().toString(tr("dd MMMM yy")));
            break;

    }

    // add Search/Filter info to Title
    if (context->isfiltered || context->ishomefiltered)
       _title = tr("Search/Filter: ") + _title;


    // For LTM_ALL show all Rides which are done in the active/selected Date Range
    if (settings.groupBy == LTM_ALL) {
        start = settings.start.date();
        end = settings.end.date();
    }

    // For LTM_TOD show all Rides which are done in the active/selected Date Range considering the Ride Time
    QTime end_time;
    if (settings.groupBy == LTM_TOD) {
        end_time = time.addSecs(3599); // full hour to 1 second before next (this also avoids reaching 00:00:00)
    }

    // create the ride list
    int count = 0;
    rides->clear();
    selected.clear();

    // set headings
    rides->setColumnCount(1);
    QTableWidgetItem *h = new QTableWidgetItem(tr("Date & Time"), QTableWidgetItem::Type);
    rides->setHorizontalHeaderItem(0,h);
    bool nonRideMetrics = false; // a non Ride specific metrics is shown on the Chart
    // collect the header texts (to know the number of columns)
    int column = 1;
    QList<QString> headerList;
    foreach(MetricDetail d, settings.metrics) {
         // only add columns for ride related metrics (DB, META and BEST)
         if (d.type == METRIC_DB || d.type == METRIC_META || d.type == METRIC_BEST) {
             headerList << d.uname;
             column++;
         } else {
             // there is at least one other metrics in the chart, so add info to title
             nonRideMetrics = true;
         }
    }
    // set the header texts of relevant columns exist
    if (column > 1) {
        rides->setColumnCount(column);
        int i = 1;
        foreach (QString header, headerList) {
            h = new QTableWidgetItem(header,QTableWidgetItem::Type);
            rides->setHorizontalHeaderItem(i++,h);
        }
    }

    // Summary Metrics.data is always available and thus perfect find the rides eligible for the list
    int i = 0;
    foreach(RideItem *item, context->athlete->rideCache->rides()) {

        if (!settings.specification.pass(item)) continue;

        QDate rideDate = item->dateTime.date();

          // check either RideDate (for all Date related groupBy's) or RideTime (for LTM_TOD only)
          if (((settings.groupBy != LTM_TOD) && (rideDate >= start && rideDate <= end))
            ||
            // group by time of day ?
            ((settings.groupBy == LTM_TOD) && (item->dateTime.time() >= time && item->dateTime.time() <= end_time))) {

             // we'll select it for summary display
             selected << item->fileName;

             // date/time
             QTableWidgetItem *t = new QTableWidgetItem(item->dateTime.toString(tr("ddd, dd MMM yy hh:mm")));
             t->setFlags(t->flags() & (~Qt::ItemIsEditable));
             t->setTextAlignment(Qt::AlignHCenter);
             rides->setRowCount(count+1);
             rides->setItem(count, 0, t);
             rides->setRowHeight(count, 14);

             // the column data has to be determined depending on the metrics type
             int column = 1;
             // per selected Ride find values for each Metrics
             foreach(MetricDetail d, settings.metrics) {
                 QString value;
                 if (d.type == METRIC_DB || d.type == METRIC_META) {

                     // get the value to show as a string
                     value = item->getStringForSymbol(d.symbol, GlobalContext::context()->useMetricUnits);

                 } else if (d.type == METRIC_BEST) {

                     // bests are only available if a METRIC_BEST exists
                     RideBest bests = settings.bests->at(i);

                     // and are not considered as standard metrics for rounding, so do it here with precision 0
                     double v = bests.getForSymbol(d.bestSymbol);
                     value = QString("%1").arg(v, 0, 'f', 0);
                 } else {
                     // for any other type, rides have no data / and no header column is prepared
                     continue;
                 }
                 h = new QTableWidgetItem(value,QTableWidgetItem::Type);
                 h->setTextAlignment(Qt::AlignHCenter);
                 rides->setItem(count, column++, h);
             }
             count++;
         }
         i++;
    }

    // make em all visible!
    rides->resizeColumnsToContents();
    rides->setFixedHeight((count > 10 ? 10 : count) * 14 + rides->horizontalHeader()->height());

    // select the first one only if more than 1 rows exist !
    if (count > 1) {
        rides->setRangeSelected(QTableWidgetSelectionRange(0,0,0,settings.metrics.count()), true);
    }

    // show rides and metrics if at least 1 rides is selected -
    // and even if only 1 Rides (since without the Ride we have not date/time)
    // and the metrics for which the ride was selected
    if (count == 0) {
       // no ride
       rides->hide();
       metrics->hide();
       notes->hide();
       _title = tr("No activity selected");
    } else {
       // one or more rides
       rides->show();
       metrics->show();
       notes->show();
       // give some extrat info
       if (count > 1) _title += QString(tr(" (%1 activities)")).arg(count);
    }

    if (nonRideMetrics) _title += QString(tr(" / non activity-related metrics skipped"));

    setTitle(_title);
    rideSelected();
}

QString
LTMPopup::setSummaryHTML(RideItem *item)
{
    // where we construct the text
    QString summaryText("");

    // main totals
    const QStringList totalColumn = QStringList()
        << "workout_time"
        << "time_recording"
        << "time_riding"
        << (item->isSwim ? "distance_swim" : "total_distance")
        << "total_work"
        << "skiba_wprime_exp"
        << "elevation_gain";

    const QStringList averageColumn = QStringList()
        << "average_speed"
        << "average_power"
        << "average_hr"
        << "average_cad"
        << (item->isSwim ? "pace_swim" : (item->sport=="Row" ? "pace_row" : "pace"));

    static const QStringList maximumColumn = QStringList()
        << "max_speed"
        << "max_power"
        << "max_heartrate"
        << "max_cadence"
        << "skiba_wprime_max";


    // user defined
    QString s = appsettings->value(this, GC_SETTINGS_FAVOURITE_METRICS, GC_SETTINGS_FAVOURITE_METRICS_DEFAULT).toString();

    // in case they were set tand then unset
    if (s == "") s = GC_SETTINGS_FAVOURITE_METRICS_DEFAULT;
    QStringList metricColumn = s.split(",");

    // foreach of the metrics get the ride value
    // header of summary

    summaryText = QString("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">"
                          "<html>"
                          "<head>"
                          "<title></title>"
                          "</head>"
                          "<body>"
                          "<table border= \"0\" cellspacing=\"10\">"
                          "<tr>");


    for (int i=0; i<4; i++) {

      QString aggname;
      QStringList list;

      switch(i) {
          case 0 : // Totals
              aggname = tr("Totals");
              list = totalColumn;
              break;

      case 1 :  // Averages
              aggname = tr("Averages");
              list = averageColumn;
              break;

      case 2 :  // Maximums
              aggname = tr("Maximums");
              list = maximumColumn;
              break;

      case 3 :  // User defined..
              aggname = tr("Metrics");
              list = metricColumn;
      break;

      }

      summaryText += QString("<td align=\"center\" width=\"25%\">"
                             "<table>"
                             "<tr>"
                             "<td align=\"center\" colspan=\"2\">"
                             "<b>%1</b>"
                             "</td>"
                             "</tr>").arg(aggname);

      foreach(QString metricname, list) {

        const RideMetric *metric = RideMetricFactory::instance().rideMetric(metricname);

        // use getAggregate even if it's only 1 file to have consistent value treatment
        double d = item->getForSymbol(metricname, true);
        QString value;
        if (metric) {
            const_cast<RideMetric*>(metric)->setValue(d);
            value = metric->toString(GlobalContext::context()->useMetricUnits);
        }

        // Maximum Max and Average Average looks nasty, remove from name for display
        QString s = metric ? metric->name().replace(QRegularExpression(tr("^(Average|Max) ")), "") : "unknown";

        // don't show units for time values
        if (metric && (metric->units(GlobalContext::context()->useMetricUnits) == "seconds" ||
                       metric->units(GlobalContext::context()->useMetricUnits) == tr("seconds") ||
                       metric->units(GlobalContext::context()->useMetricUnits) == "")) {

          summaryText += QString("<tr><td>%1:</td><td align=\"right\"> %2</td></tr>")
                                 .arg(s)
                                 .arg(value);

        } else {
          summaryText += QString("<tr><td>%1(%2):</td><td align=\"right\"> %3</td></tr>")
                                 .arg(s)
                                 .arg(metric ? metric->units(GlobalContext::context()->useMetricUnits) : "unknown")
                                 .arg(value);
        }
      }
      summaryText += QString("</table>"
                             "</td>");

   }

   // finish off the html document
   summaryText += QString("</tr>"
                          "</table>"
                          "</center>"
                          "</body>"
                          "</html>");


   return summaryText;
}

void
LTMPopup::rideSelected()
{
    // which ride is selected
    int index = 0;
    if (rides->selectedItems().count())
        index = rides->selectedItems().first()->row();

    // do we have any rides and is the index within bounds
    if (selected.count() > index) {

        RideItem *have = context->athlete->rideCache->getRide(selected[index]);

        if (have) {

            // update summary
            metrics->setText(""); //! stop crash (?)
            metrics->setText(setSummaryHTML(have));

            notes->setText(""); //! stop crash (?)
            // Use calendar text for more information and to allow customization
            notes->setText(have->getText("Calendar Text", ""));
        }
    }
    resizeEvent(NULL);
}

void
LTMPopup::rideOpen()
{
    // which ride is selected
    int index = 0;
    if (rides->selectedItems().count())
        index = rides->selectedItems().first()->row();

    // do we have any rides and is the index within bounds
    if (selected.count() > index) {

        RideItem *have = context->athlete->rideCache->getRide(selected[index]);

        if (have) {

            // Select Activity in Activities view
            context->notifyRideSelected(have);
        }
    }
}

void
LTMPopup::resizeEvent(QResizeEvent *)
{
    int _width = width()-10;
    int _firstWidth = 150;
    int _scrollbarWidth = 20;

    rides->setFixedWidth(_width);
    rides->horizontalHeader()->resizeSection(0, _firstWidth);
    // only resize if more than Date/Time column exists
    if (rides->horizontalHeader()->count() > 1) {
        int _sectionWidth = ((_width-_scrollbarWidth-_firstWidth) / (rides->horizontalHeader()->count()-1)) - 4;
        rides->horizontalHeader()->resizeSection(0, _firstWidth);
        for (int i=1; i < rides->horizontalHeader()->count(); i++)
           rides->horizontalHeader()->resizeSection(i, _sectionWidth);

    }
}
bool
LTMPopup::eventFilter(QObject * /*object*/, QEvent *e)
{

    //if (object != (QObject *)plot()->canvas() )
        //return false;
    //if only 1 Ride in list, ignore any mouse activities
    if (rides->rowCount() == 1) return false;

    // process mouse
    switch (e->type()) {
    case QEvent::MouseMove:
        {
            int row = rides->indexAt((static_cast<QMouseEvent*>(e))->pos()).row();

            if (row >= 0) {
                 QList<QModelIndex> selection = rides->selectionModel()->selection().indexes();
                 if (selection.count() == 0 || selection[0].row() != row) {
                    rides->clearSelection();
                    rides->setRangeSelected(QTableWidgetSelectionRange(row,0,row,rides->columnCount()-1), true);
                }
            }
            return false;
        }
        break;
    default:
        return false;
    }
    return false;
}
