/*
 * Copyright (c) 2006 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 <assert.h>
#include <QDebug>
#include "RideItem.h"
#include "RideFile.h"
#include "RideImportWizard.h"
#include "MainWindow.h"
#include "QuarqRideFile.h"
#include <QWaitCondition>
#include "Settings.h"
#include "Units.h"
#include "GcRideFile.h"
#include "JsonRideFile.h"
#include "TcxRideFile.h"
#include "MetricAggregator.h"


// drag and drop passes urls ... convert to a list of files and call main constructor
RideImportWizard::RideImportWizard(QList<QUrl> *urls, QDir &home, MainWindow *main, QWidget *parent) : QDialog(parent), mainWindow(main)
{
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
    QList<QString> filenames;
    for (int i=0; i<urls->count(); i++)
        filenames.append(QFileInfo(urls->value(i).toLocalFile()).absoluteFilePath());
    init(filenames, home, mainWindow);
    filenames.clear();
}

RideImportWizard::RideImportWizard(QList<QString> files, QDir &home, MainWindow *main, QWidget *parent) : QDialog(parent), mainWindow(main)
{
    init(files, home, mainWindow);
}

void
RideImportWizard::init(QList<QString> files, QDir &home, MainWindow * /*mainWindow*/)
{

    // initialise dialog box
    tableWidget = new QTableWidget(files.count(), 6, this);
    tableWidget->setItemDelegate(new RideDelegate(1)); // use a delegate for column 1 date
    tableWidget->verticalHeader()->setDefaultSectionSize(20);
    phaseLabel = new QLabel;
    progressBar = new QProgressBar();
    todayButton = new QComboBox();
    todayButton->addItem(tr("Select Date..."));
    todayButton->addItem(tr("Today"));
    todayButton->addItem(tr("Last Monday"));
    todayButton->addItem(tr("Last Tuesday"));
    todayButton->addItem(tr("Last Wednesday"));
    todayButton->addItem(tr("Last Thursday"));
    todayButton->addItem(tr("Last Friday"));
    todayButton->addItem(tr("Last Saturday"));
    todayButton->addItem(tr("Last Sunday"));
    todayButton->addItem(tr("Choose Date"));
    cancelButton = new QPushButton(tr("Cancel"));
    abortButton = new QPushButton(tr("Abort"));
    overFiles = new QCheckBox(tr("Overwrite Existing Files"));
    // initially the cancel, overwrite and today widgets are hidden
    // they only appear whilst we are asking the user for dates
    cancelButton->setHidden(true);
    todayButton->setHidden(true);
    overFiles->setHidden(true);
    overwriteFiles = false;

    aborted = false;

    // NOTE: abort button morphs into save and finish button later
    connect(abortButton, SIGNAL(clicked()), this, SLOT(abortClicked()));

    // only used when editing dates
    connect(todayButton, SIGNAL(activated(int)), this, SLOT(todayClicked(int)));
    connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
    connect(overFiles, SIGNAL(clicked()), this, SLOT(overClicked()));

    // title & headings
    setWindowTitle(tr("Import Ride Files"));
    QTableWidgetItem *filenameHeading = new QTableWidgetItem;
    filenameHeading->setText(tr("Filename"));
    tableWidget->setHorizontalHeaderItem(0, filenameHeading);

    QTableWidgetItem *dateHeading = new QTableWidgetItem;
    dateHeading->setText(tr("Date"));
    tableWidget->setHorizontalHeaderItem(1, dateHeading);

    QTableWidgetItem *timeHeading = new QTableWidgetItem;
    timeHeading->setText(tr("Time"));
    tableWidget->setHorizontalHeaderItem(2, timeHeading);

    QTableWidgetItem *durationHeading = new QTableWidgetItem;
    durationHeading->setText(tr("Duration"));
    tableWidget->setHorizontalHeaderItem(3, durationHeading);

    QTableWidgetItem *distanceHeading = new QTableWidgetItem;
    distanceHeading->setText(tr("Distance"));
    tableWidget->setHorizontalHeaderItem(4, distanceHeading);

    QTableWidgetItem *statusHeading = new QTableWidgetItem;
    statusHeading->setText(tr("Import Status"));
    tableWidget->setHorizontalHeaderItem(5, statusHeading);

    // save target dir
    this->home = home;

    // Fill in the filenames and all the textItems
    for (int i=0; i < files.count(); i++) {
        QTableWidgetItem *t;

        filenames.append(QFileInfo(files[i]).absoluteFilePath());
        blanks.append(true); // by default editable

        // Filename
        t = new QTableWidgetItem();
        t->setText(QFileInfo(files[i]).fileName());
        t->setFlags(t->flags() & (~Qt::ItemIsEditable));
        tableWidget->setItem(i,0,t);

        // Date
        t = new QTableWidgetItem();
        t->setText(tr(""));
        t->setFlags(t->flags()  | Qt::ItemIsEditable);
        t->setBackgroundColor(Qt::red);
        tableWidget->setItem(i,1,t);

        // Time
        t = new QTableWidgetItem();
        t->setText(tr(""));
        t->setFlags(t->flags() | Qt::ItemIsEditable);
        tableWidget->setItem(i,2,t);

        // Duration
        t = new QTableWidgetItem();
        t->setText(tr(""));
        t->setFlags(t->flags() & (~Qt::ItemIsEditable));
        tableWidget->setItem(i,3,t);

        // Distance
        t = new QTableWidgetItem();
        t->setText(tr(""));
        t->setFlags(t->flags() & (~Qt::ItemIsEditable));
        tableWidget->setItem(i,4,t);

        // Import Status
        t = new QTableWidgetItem();
        t->setText(tr(""));
        t->setFlags(t->flags() & (~Qt::ItemIsEditable));
        tableWidget->setItem(i,5,t);
    }

    // put into our dialog box
    // layout
    QHBoxLayout *buttons = new QHBoxLayout;
    buttons->addWidget(phaseLabel);
    buttons->addStretch();
    buttons->addWidget(todayButton);
    buttons->addStretch();
    buttons->addWidget(overFiles);
    buttons->addWidget(cancelButton);
    buttons->addWidget(abortButton);

    QVBoxLayout *contents = new QVBoxLayout(this);
    contents->addWidget(tableWidget);
    contents->addWidget(progressBar);
    contents->addLayout(buttons);
    setLayout(contents);

    // adjust all the sizes to look tidy
    tableWidget->setColumnWidth(0, 200); // filename
    tableWidget->setColumnWidth(1, 120); // date
    tableWidget->setColumnWidth(2, 120); // time
    tableWidget->setColumnWidth(3, 100); // duration
    tableWidget->setColumnWidth(4, 70); // distance
    tableWidget->setColumnWidth(5, 250); // status

    // max height for 16 items and a scrollbar on right if > 16 items
    // for some reason the window is wider for 10-16 items too.
    // someone that understands width/geometry and layouts better
    // than me should clean up the logic here, no doubt using a
    // single call to geometry/hint. But for now it looks good
    // on Leopard, no doubt not so good on Windows
    resize(920 +
           ((files.count() > 16 ? 24 : 0) +
           ((files.count() > 9 && files.count() < 17) ? 8 : 0)),
           118 + (files.count() > 16 ? 17*20 : (files.count()+1) * 20));

    tableWidget->adjustSize();

    // Refresh prior to running down the list & processing...
    this->show();
}

int
RideImportWizard::process()
{

    // set progress bar limits - for each file we
    // will make 5 passes over the files
    //         1. checking it is a file ane readable
    //         2. parsing it with the RideFileReader
    //         3. [optional] collect date/time information from user
    //         4. copy file into Library
    //         5. Process for CPI (not implemented yet)

    // So, therefore the progress bar runs from 0 to files*4. (since step 5 is not implemented yet)
    progressBar->setMinimum(0);
    progressBar->setMaximum(filenames.count()*4);

    // Pass one - Is it valid?
    phaseLabel->setText(tr("Step 1 of 4: Check file permissions"));
    for (int i=0; i < filenames.count(); i++) {

        // get fullpath name for processing
        QFileInfo thisfile(filenames[i]);

        if (thisfile.exists() && thisfile.isFile() && thisfile.isReadable()) {

            // is it one we understand ?
            QStringList suffixList = RideFileFactory::instance().suffixes();
            QRegExp suffixes(QString("^(%1)$").arg(suffixList.join("|")));
            suffixes.setCaseSensitivity(Qt::CaseInsensitive);

            if (suffixes.exactMatch(thisfile.suffix())) {

                // Woot. We know how to parse this baby
                tableWidget->item(i,5)->setText(tr("Queued"));

            } else {
                tableWidget->item(i,5)->setText(tr("Error - Unknown file type"));
            }

        } else {
            //  Cannot open
            tableWidget->item(i,5)->setText(tr("Error - Not a valid file"));
        }

        progressBar->setValue(progressBar->value()+1);

    }

    if (aborted) { done(0); }
    repaint();
    QApplication::processEvents();

    // Pass 2 - Read in with the relevant RideFileReader method

    phaseLabel->setText(tr("Step 2 of 4: Validating Files"));
   for (int i=0; i< filenames.count(); i++) {


        // does the status say Queued?
        if (!tableWidget->item(i,5)->text().startsWith(tr("Error"))) {

              QStringList errors;
              QFile thisfile(filenames[i]);

              tableWidget->item(i,5)->setText(tr("Parsing..."));
              tableWidget->setCurrentCell(i,5);
              QApplication::processEvents();

              if (aborted) { done(0); }
              this->repaint();
              QApplication::processEvents();

              QList<RideFile*> rides;
              RideFile *ride = RideFileFactory::instance().openRideFile(mainWindow, thisfile, errors, &rides);

              // is this an archive of files?
              if (rides.count() > 1) {

                 int here = i;

                 // remove current filename from state arrays and tableview
                 filenames.removeAt(here);
                 blanks.removeAt(here);
                 tableWidget->removeRow(here);

                 // resize dialog according to the number of rows we expect
                 int willhave = filenames.count() + rides.count();
                 resize(920 + ((willhave > 16 ? 24 : 0) +
                     ((willhave > 9 && willhave < 17) ? 8 : 0)),
                     118 + ((willhave > 16 ? 17*20 : (willhave+1) * 20)));


                 // ok so create a temporary file and add to the tableWidget
                 int counter = 0;
                 foreach(RideFile *extracted, rides) {

                     // write as a temporary file, using the original
                     // filename with "-n" appended
                     QString fulltarget = QDir::tempPath() + "/" + QFileInfo(thisfile).baseName() + QString("-%1.tcx").arg(counter+1);
                     TcxFileReader reader;
                     QFile target(fulltarget);
                     reader.writeRideFile(mainWindow, extracted, target);
                     deleteMe.append(fulltarget);
                     delete extracted;
                     
                     // now add each temporary file ...
                     filenames.insert(here, fulltarget);
                     blanks.insert(here, true); // by default editable
                     tableWidget->insertRow(here+counter);

                     QTableWidgetItem *t;

                     // Filename
                     t = new QTableWidgetItem();
                     t->setText(fulltarget);
                     t->setFlags(t->flags() & (~Qt::ItemIsEditable));
                     tableWidget->setItem(here+counter,0,t);

                     // Date
                     t = new QTableWidgetItem();
                     t->setText(tr(""));
                     t->setFlags(t->flags()  | Qt::ItemIsEditable);
                     t->setBackgroundColor(Qt::red);
                     tableWidget->setItem(here+counter,1,t);

                     // Time
                     t = new QTableWidgetItem();
                     t->setText(tr(""));
                     t->setFlags(t->flags() | Qt::ItemIsEditable);
                     tableWidget->setItem(here+counter,2,t);

                     // Duration
                     t = new QTableWidgetItem();
                     t->setText(tr(""));
                     t->setFlags(t->flags() & (~Qt::ItemIsEditable));
                     tableWidget->setItem(here+counter,3,t);

                     // Distance
                     t = new QTableWidgetItem();
                     t->setText(tr(""));
                     t->setFlags(t->flags() & (~Qt::ItemIsEditable));
                     tableWidget->setItem(here+counter,4,t);

                     // Import Status
                     t = new QTableWidgetItem();
                     t->setText(tr(""));
                     t->setFlags(t->flags() & (~Qt::ItemIsEditable));
                     tableWidget->setItem(here+counter,5,t);

                     counter++;

                     tableWidget->adjustSize();
                 }
                 QApplication::processEvents();


                 // progress bar needs to adjust...
                 progressBar->setMaximum(filenames.count()*4);

                 // then go back one and re-parse from there
                 rides.clear();
   
                 i--;
                 goto next; // buttugly I know, but count em across 100,000 lines of code

              }

              // did it parse ok?
              if (ride) {

                   // ride != NULL but !errors.isEmpty() means they're just warnings
                   if (errors.isEmpty())
                       tableWidget->item(i,5)->setText(tr("Validated"));
                   else
                       tableWidget->item(i,5)->setText(tr("Warning - ") + errors.join(tr(" ")));

                   // Set Date and Time
                   if (ride->startTime().isNull()) {

                       // Poo. The user needs to supply the date/time for this ride
                       blanks[i] = true;
                       tableWidget->item(i,1)->setText(tr(""));
                       tableWidget->item(i,2)->setText(tr(""));

                   } else {

                       // Cool, the date and time was extrcted from the source file
                       blanks[i] = false;
                       tableWidget->item(i,1)->setText(ride->startTime().toString(tr("dd MMM yyyy")));
                       tableWidget->item(i,2)->setText(ride->startTime().toString(tr("hh:mm:ss ap")));
                   }

                   tableWidget->item(i,1)->setTextAlignment(Qt::AlignRight); // put in the middle
                   tableWidget->item(i,2)->setTextAlignment(Qt::AlignRight); // put in the middle

                   // time and distance from tags (.gc files)
                   QMap<QString,QString> lookup;
                   lookup = ride->metricOverrides.value("total_distance");
                   double km = lookup.value("value", "0.0").toDouble();

                   lookup = ride->metricOverrides.value("workout_time");
                   int secs = lookup.value("value", "0.0").toDouble();

                   // show duration by looking at last data point
                   if (!ride->dataPoints().isEmpty() && ride->dataPoints().last() != NULL) {
                       if (!secs) secs = ride->dataPoints().last()->secs;
                       if (!km) km = ride->dataPoints().last()->km;
                   }

                   QChar zero = QLatin1Char ( '0' );
                   QString time = QString("%1:%2:%3").arg(secs/3600,2,10,zero)
                       .arg(secs%3600/60,2,10,zero)
                       .arg(secs%60,2,10,zero);
                   tableWidget->item(i,3)->setText(time);
                   tableWidget->item(i,3)->setTextAlignment(Qt::AlignHCenter); // put in the middle

                   // show distance by looking at last data point
                   QString dist = mainWindow->useMetricUnits
                       ? QString ("%1 km").arg(km, 0, 'f', 1)
                       : QString ("%1 mi").arg(km * MILES_PER_KM, 0, 'f', 1);
                   tableWidget->item(i,4)->setText(dist);
                   tableWidget->item(i,4)->setTextAlignment(Qt::AlignRight); // put in the middle

                   delete ride;
               } else {
                   // nope - can't handle this file
                   tableWidget->item(i,5)->setText(tr("Error - ") + errors.join(tr(" ")));
               }
        }
        progressBar->setValue(progressBar->value()+1);
        QApplication::processEvents();
        if (aborted) { done(0); }
        this->repaint();

        next:;
    }

    // Pass 3 - get missing date and times for imported files
    //         Actually allow us to edit date on ANY ride, we
    //         make sure that the ride date/time is set from
    //         the filename and never from the ride data
    phaseLabel->setText(tr("Step 3 of 4: Confirm Date and Time"));

    int needdates=0;
    for (int i=0; i<filenames.count(); i++) {

        // ignore errors
        QTableWidgetItem *t = tableWidget->item(i,5);
        if (t->text().startsWith(tr("Error"))) continue;

        if (blanks[i]) needdates++; // count the blanks tho -- these MUST be edited

        // does nothing for the moment
        progressBar->setValue(progressBar->value()+1);
        progressBar->repaint();
   }

   // Wait for user to press save
   abortButton->setText(tr("Save"));
   aborted = false;

   cancelButton->setHidden(false);
   todayButton->setHidden(false);
   overFiles->setHidden(false);

   if (needdates == 0) {
      // no need to wait for the user to input dates
      // nd press save if all the dates are set
      // (i.e. they got set by the file importers already)
      // do nothing for now since we need to confirm dates
      // and confirm overwrite files rather than importing
      // without user intervention

      abortButton->setDisabled(false);

      // abortClicked();
   } else {

      // de-activate Save button until the dates and times are sorted
      abortButton->setDisabled(true);
   }
   connect(tableWidget, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(activateSave()));

   return 0;
}

void
RideImportWizard::overClicked()
{
    overwriteFiles = overFiles->isChecked();
}

void
RideImportWizard::activateSave()
{

   for (int i=0; i<filenames.count(); i++) {

        // ignore errors
        QTableWidgetItem *t = tableWidget->item(i,5);
        if (t->text().startsWith(tr("Error"))) continue;

       // date needed?
        t = tableWidget->item(i,1);
        if (t->text() == "") return;

        // time needed?
        t = tableWidget->item(i,2);
        if (t->text() == "") return;
   }
   // if you got here then all entries that need a date have a date
   abortButton->setDisabled(false);
}

// the code here is tedious. checking for conditions about total ride time
// and if it fits in a day, then distributing them over the day, but
// only if the time is not set, and then if its being set to today
// then start must be before the current time cause you can't log
// data for future rides ... and so on.
//
// but then this feature is gonna save a lot of typing over time ...
//
void
RideImportWizard::todayClicked(int index)
{
    QDate selectedDate; // the date we're gonna apply to the row(s) highlighted

    // set the index back to 0, so we can select again
    todayButton->setCurrentIndex(0);

    // 0 = nothing selected, 1 = Today - 2 = last monday thru to 8 = last sunday
    if (index == 0) { // no selection
        return;
    } else if (index == 1) { // today
        selectedDate = QDate().currentDate();
    } else if (index == 9) { // other date - set focus on first highlighted date
        for (int i=0; i<filenames.count(); i++) {
            if (tableWidget->item(i,0)->isSelected() ||
                tableWidget->item(i,1)->isSelected() ||
                tableWidget->item(i,2)->isSelected() ||
                tableWidget->item(i,3)->isSelected() ||
                tableWidget->item(i,4)->isSelected() ||
                tableWidget->item(i,5)->isSelected()) {
                tableWidget->editItem(tableWidget->item(i,1));
                return;
            }
        }
        return;
    } else { // last mon/tue/wed/thu/fri/sat/sun
        int daysago =  (QDate().currentDate().dayOfWeek() - index + 8) % 7;
        if (!daysago) daysago=7; // e.g.last wednesday is 7 days ago if today is wednesday
        selectedDate = QDate().fromJulianDay(QDate().currentDate().toJulianDay()-daysago);
    }

    // Only apply to selected date - set time to current time - ride duration
    // pretty daft but at least it sets it to something, anything is gonna be random
    int countselected = 0;
    int totalduration = 0;
    for (int i=0; i< filenames.count(); i++) {
        if (tableWidget->item(i,0)->isSelected() ||
            tableWidget->item(i,1)->isSelected() ||
            tableWidget->item(i,2)->isSelected() ||
            tableWidget->item(i,3)->isSelected() ||
            tableWidget->item(i,4)->isSelected() ||
            tableWidget->item(i,5)->isSelected()) {
            countselected++;

            QTime duration = QTime().fromString(tableWidget->item(i,3)->text(), tr("hh:mm:ss"));
            totalduration += duration.hour() * 3600 +
                             duration.minute() * 60 +
                             duration.second();
        }
    }

    // More than a days worth of rides so can't squeeze into a single day!
    if (totalduration > (24 * 3600)) {
        QMessageBox::warning ( this, tr ( "Invalid Selection" ), tr ( "More than 24hrs of rides to fit into a day" ));
        return;
    }

    // if it is today then start from now - total rides duration
    // if that goes negative then just do the std thing
    int rstart=0;
    if (index == 1) {

        QTime timenow = QTime().currentTime();

        int now  = timenow.hour() * 3600 +
                   timenow.minute() * 60 +
                   timenow.second();

        rstart = now - totalduration;

    }

    if (rstart <= 0) { // i.e. still not set properly...
        // if it is not being set to "today" then spread them across the
        // day back to back with equal time before and after noon.
        rstart = ((24*3600) - totalduration) /2;
    }

    // zip through the rows and where highlighted set the date
    // if the start time is not set set it to rstart and increment
    // by the duration of the ride.
    for (int i=0; i< filenames.count(); i++) {
        if (tableWidget->item(i,0)->isSelected() ||
            tableWidget->item(i,1)->isSelected() ||
            tableWidget->item(i,2)->isSelected() ||
            tableWidget->item(i,3)->isSelected() ||
            tableWidget->item(i,4)->isSelected() ||
            tableWidget->item(i,5)->isSelected()) {

            // set the date to date selected
            tableWidget->item(i,1)->setText(selectedDate.toString(tr("dd MMM yyyy")));
            // look at rides with missing start time - we need to populate those

            // ride duration
            QTime duration = QTime().fromString(tableWidget->item(i,3)->text(), tr("hh:mm:ss"));

            // ride start time
            QTime time(rstart/3600, rstart%3600/60, rstart%60);
            tableWidget->item(i,2)->setText(time.toString(tr("hh:mm:ss a")));
            rstart += duration.hour() * 3600 +
                      duration.minute() * 60 +
                      duration.second();
        }
    }
    // phew! - repaint!
    QApplication::processEvents();
    tableWidget->repaint();
}

void
RideImportWizard::cancelClicked()
{
    done(0); // you are the weakest link, goodbye.
}

// info structure used by cpi updater
struct cpi_file_info {
    QString file, inname, outname;
};

static QStringList
findDuplicates(QString filename)
{
    // does this ride already exist?
    // either the full name is a match
    // or the same name but different
    // filetype: e.g. xxx.gc matches xxx.tcx
    QStringList duplicates;

    // get a list of possible duplicates (files we support)
    QString basename = QFileInfo(filename).baseName();
    QStringList filters;
    foreach (QString ext, RideFileFactory::instance().suffixes()) {
        QString check = basename + "." + ext;
        filters << check;
    }

    // check if any matched (case insensitive)
    QFlags<QDir::Filter> spec = QDir::Files;
#ifdef Q_OS_WIN32
    spec |= QDir::Hidden;
#endif
    // get list and convert to full path
    foreach(QString name, QFileInfo(filename).dir().entryList(filters, spec, QDir::Name)) {
        duplicates << QFileInfo(filename).dir().path() + "/" + name;
    }

    return duplicates;
}

static void
removeDuplicate(QString filename)
{
    // rename to .bak, if that already exists
    // then wipe it first
    QString backup = filename + ".bak";
    QFile(backup).remove(); // wipe it, if it is there
    QFile(filename).rename(backup);
}

void
RideImportWizard::abortClicked()
{

    // if done when labelled abort we kill off this dialog
    QString label = abortButton->text();

    if (label == tr("Abort")) {
        hide();
        mainWindow->isclean = false;
        mainWindow->metricDB->refreshMetrics();
        aborted=true; // terminated. I'll be back.
        return;
    }

    if (label == tr("Finish")) {
       // phew. our work is done. -- lets force an update stats...
       hide();
       mainWindow->isclean = false;
       mainWindow->metricDB->refreshMetrics();
       done(0);
       return;
    }

   int needdates=0;
   for (int i=0; i<filenames.count(); i++) {

        QTableWidgetItem *t = tableWidget->item(i,5);
        if (t->text().startsWith(tr("Error"))) continue;
       // date needed?
        t = tableWidget->item(i,1);
        if (t->text() == "") {
            needdates++;
        }
        t->setFlags(t->flags() | (Qt::ItemIsEditable));

        // time needed?
        t = tableWidget->item(i,2);
        if (t->text() == "") {
            needdates++;
        }
        t->setFlags(t->flags() | (Qt::ItemIsEditable));
   }

   if (needdates) return; // no dice dude, we need those dates filled in!

    // if done when labelled save we copy the files and run the cpi calculator
    phaseLabel->setText(tr("Step 4 of 4: Save to Library"));

    abortButton->setText(tr("Abort"));
    aborted = false;
    cancelButton->setHidden(true);
    todayButton->setHidden(true);
    overFiles->setHidden(true);

    // now set this fields uneditable again ... yeesh.
    for (int i=0; i <filenames.count(); i++) {
            QTableWidgetItem *t = tableWidget->item(i,1);
            t->setFlags(t->flags() & (~Qt::ItemIsEditable));
            t = tableWidget->item(i,2);
            t->setFlags(t->flags() & (~Qt::ItemIsEditable));
    }

    QChar zero = QLatin1Char ( '0' );

    for (int i=0; i< filenames.count(); i++) {

        if (tableWidget->item(i,5)->text().startsWith(tr("Error"))) continue; // skip error

        tableWidget->item(i,5)->setText(tr("Saving..."));
        tableWidget->setCurrentCell(i,5);
        QApplication::processEvents();
        if (aborted) { done(0); }
        this->repaint();

        // Setup the ridetime as a QDateTime
        QDateTime ridedatetime = QDateTime(QDate().fromString(tableWidget->item(i,1)->text(), tr("dd MMM yyyy")),
                                 QTime().fromString(tableWidget->item(i,2)->text(), tr("hh:mm:ss a")));
        QString suffix = QFileInfo(filenames[i]).suffix();
        QString targetnosuffix = QString ( "%1_%2_%3_%4_%5_%6" )
                               .arg ( ridedatetime.date().year(), 4, 10, zero )
                               .arg ( ridedatetime.date().month(), 2, 10, zero )
                               .arg ( ridedatetime.date().day(), 2, 10, zero )
                               .arg ( ridedatetime.time().hour(), 2, 10, zero )
                               .arg ( ridedatetime.time().minute(), 2, 10, zero )
                               .arg ( ridedatetime.time().second(), 2, 10, zero );
        QString target = QString ("%1.%2" )
                               .arg ( targetnosuffix )
                               .arg ( suffix );
        QString fulltarget = home.absolutePath() + "/" + target;

        // if its a gc file we need to parse and serialize
        // using the ridedatetime and target filename
        if (filenames[i].endsWith(".gc", Qt::CaseInsensitive) ||
            filenames[i].endsWith(".json", Qt::CaseInsensitive)) {

            QStringList duplicates;

            // CHECK FOR DUPLICATE
            duplicates = findDuplicates(fulltarget);
            if (duplicates.count() && !overwriteFiles) {
                tableWidget->item(i,5)->setText(tr("Error - File exists"));
            } else {

                // wipe away the duplicate
                foreach(QString duplicate, duplicates) {
                    removeDuplicate(duplicate); // we do not use removeRide coz it clashes
                }

                // read the file (again)
                QStringList errors;
                QFile thisfile(filenames[i]);
                RideFile *ride(RideFileFactory::instance().openRideFile(mainWindow, thisfile, errors));

                // update ridedatetime
                ride->setStartTime(ridedatetime);

                // serialize
                if (filenames[i].endsWith(".gc")) {
                    GcFileReader reader;
                    QFile target(fulltarget);
                    reader.writeRideFile(mainWindow, ride, target);
                } else {
                    JsonFileReader reader;
                    QFile target(fulltarget);
                    reader.writeRideFile(mainWindow, ride, target);
                }

                // clear
                delete ride;

                if (duplicates.count()) {
                    tableWidget->item(i,5)->setText(tr("File Overwritten"));
                } else {
                    tableWidget->item(i,5)->setText(tr("File Saved"));
                    mainWindow->addRide(QFileInfo(fulltarget).fileName(), true);
                }
            }

        } else {
            // for native file formats the filename IS the ride date time so
            // no need to write -- we just copy

            // so now we have sourcefile in 'filenames[i]' and target file name in 'target'
            if (!fulltarget.compare(filenames[i])) { // they are the same file! so skip copy
                tableWidget->item(i,5)->setText(tr("Error - Source is Target"));

            // CHECK FOR DUPLICATE
            } else if (findDuplicates(fulltarget).count()) {
                if (overwriteFiles) {

                    // wipe away that duplicate
                    foreach(QString duplicate, findDuplicates(fulltarget)) {
                        removeDuplicate(duplicate);
                    }

                    tableWidget->item(i,5)->setText(tr("Overwriting file..."));
                    QFile source(filenames[i]);
                    QString fulltargettmp(home.absolutePath() + tr("/") + targetnosuffix + tr(".tmp"));

                    if (source.copy(fulltargettmp)) {

                        // mv tmp to target
                        QFile temp(fulltargettmp);
                        if (temp.rename(fulltarget)) {
                            tableWidget->item(i,5)->setText(tr("File Overwritten"));
                            //no need to add since its already there!
                        } else
                            tableWidget->item(i,5)->setText(tr("Error - overwrite failed"));
                    } else {
                        tableWidget->item(i,5)->setText(tr("Error - overwrite failed"));
                    }
                } else {
                    tableWidget->item(i,5)->setText(tr("Error - File exists"));
                }
            } else {
                    tableWidget->item(i,5)->setText(tr("Saving file..."));
                    QFile source(filenames[i]);
                    if (source.copy(fulltarget)) {
                        tableWidget->item(i,5)->setText(tr("File Saved"));
                        mainWindow->addRide(QFileInfo(fulltarget).fileName(), true); // add to tree view
                        // free immediately otherwise all imported rides are cached
                        // and with large imports this can lead to memory exhaustion
                        // BUT! Some charts/windows will hava snaffled away the ridefile
                        // pointer which is now invalid so once all the rides have been imported
                        // we need to select the last one... see below
                    } else
                        tableWidget->item(i,5)->setText(tr("Error - copy failed"));
            }
        }
        QApplication::processEvents();
        if (aborted) { done(0); }
        progressBar->setValue(progressBar->value()+1);
        this->repaint();
    }

    // how did we get on in the end then ...
    int completed = 0;
    for (int i=0; i< filenames.count(); i++)
        if (!tableWidget->item(i,5)->text().startsWith(tr("Error"))) {
            completed++;
    }

    tableWidget->setSortingEnabled(true); // so you can browse through errors etc
    QString donemessage = QString(tr("Import Complete. %1 of %2 successful."))
                                  .arg(completed, 1, 10, zero)
                                  .arg(filenames.count(), 1, 10, zero);
    progressBar->setValue(progressBar->maximum());
    phaseLabel->setText(donemessage);
    abortButton->setText(tr("Finish"));
    aborted = false;
}

// clean up files
RideImportWizard::~RideImportWizard()
{
    foreach(QString name, deleteMe) QFile(name).remove();
}


// Below is the code to implement a custom ItemDelegate to support
// editing of the date and time of the Ride inside a QTableWidget
// the ItemDelegate is registered for every cell in the table
// and only kicks into life for the columns in which the date and
// time are stored, and only when the DateTime for that row is 0.

// construct the delegate - save away the column containing the date
//                          bearing in mind the time is in the next
//                          column.
RideDelegate::RideDelegate(int dateColumn, QObject *parent) : QItemDelegate(parent)
{
    this->dateColumn = dateColumn;
}


// paint the cells for date and time - pass through for all the
// other cells. Uses the model data to display.
void RideDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                         const QModelIndex &index) const
{
    if (index.column() == dateColumn) {

        QString value = index.model()->data(index, Qt::DisplayRole).toString();
        // display with angles to show its doing something
        QString text = QString("%1").arg(value);

        QStyleOptionViewItem myOption = option;
        myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
        drawDisplay(painter, myOption, myOption.rect, text);
        drawFocus(painter, myOption, myOption.rect);

    } else if (index.column() == dateColumn+1) {

        QString value = index.model()->data(index, Qt::DisplayRole).toString();
        // display with angles to show its doing something
        QString text = QString("%1").arg(value);

        QStyleOptionViewItem myOption = option;
        myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
        drawDisplay(painter, myOption, myOption.rect, text);
        drawFocus(painter, myOption, myOption.rect);

    } else {

        QItemDelegate::paint(painter, option, index);

    }
}

// setup editor for edit of field!!
QWidget *RideDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (index.column() == dateColumn) {

        // edit that date!
        QDateEdit *dateEdit = new QDateEdit(parent);
        dateEdit->setDisplayFormat(tr("dd MMM yyyy"));
        connect(dateEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseDateEditor()));
        return dateEdit;
    } else if (index.column() == dateColumn+1) {

        // edit that time
        QTimeEdit *timeEdit = new QTimeEdit(parent);
        timeEdit->setDisplayFormat("hh:mm:ss a");
        connect(timeEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseTimeEditor()));
        return timeEdit;
    } else {
        return QItemDelegate::createEditor(parent, option, index);
    }
}

// user hit tab or return so save away the data to our model
void RideDelegate::commitAndCloseDateEditor()
{
    QDateEdit *editor = qobject_cast<QDateEdit *>(sender());
    emit commitData(editor);
    emit closeEditor(editor);
}

// user hit tab or return so save away the data to our model
void RideDelegate::commitAndCloseTimeEditor()
{
    QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender());
    emit commitData(editor);
    emit closeEditor(editor);
}

// We don't set anything because the data is saved within the view not the model!
void RideDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
   // stored as text field
    if (index.column() == dateColumn) {
        QDateEdit *dateEdit = qobject_cast<QDateEdit *>(editor);
        QDate date = QDate().fromString(index.model()->data(index, Qt::DisplayRole).toString(), tr("dd MMM yyyy"));
        dateEdit->setDate(date);
    } else if (index.column() == dateColumn+1) {
        QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
        QTime time = QTime().fromString(index.model()->data(index, Qt::DisplayRole).toString(), "hh:mm:ss a");;
        timeEdit->setTime(time);
    }
}

// We don't set anything because the data is saved within the view not the model!
void RideDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{

    // stored as text field
    if (index.column() == dateColumn) {
        QDateEdit *dateEdit = qobject_cast<QDateEdit *>(editor);
        QString value = dateEdit->date().toString(tr("dd MMM yyyy"));
        // Place in the view
        model->setData(index, value, Qt::DisplayRole);
    } else if (index.column() == dateColumn+1) {
        QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
        QString value = timeEdit->time().toString("hh:mm:ss a");
        model->setData(index, value, Qt::DisplayRole);
    }
}

