#include "summvideoselector.h"
#include "ui_summvideoselector.h"

SummVideoSelector::SummVideoSelector(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SummVideoSelector)
{
    ui->setupUi(this);

    model = new SummVideoSelectorModel();
    connect(model, SIGNAL(item_added(SummVideoSelectorModelItem*)), SLOT(on_model_item_added(SummVideoSelectorModelItem*)));
    connect(model, SIGNAL(item_changed(SummVideoSelectorModelItem*)), SLOT(on_model_item_changed(SummVideoSelectorModelItem*)));
    connect(model, SIGNAL(item_removed(int)), SLOT(on_model_item_removed(int)));
    connect(model, SIGNAL(changed()), SLOT(on_model_changed()));

    preprocessWorkerThread = NULL;
    removeAllMode = false;

    setStateEmpty();
}

SummVideoSelector::~SummVideoSelector()
{
    stopPreprocessWorkerThread();
    delete ui;
}

void SummVideoSelector::stopPreprocessWorkerThread()
{
    if(preprocessWorkerThread != NULL)
    {
        preprocessWorkerThread->terminate();
        delete preprocessWorkerThread;
        preprocessWorkerThread = NULL;
    }
}

void SummVideoSelector::runPreprocessWorkerThread(SummVideoSelectorModelItem *item)
{
    stopPreprocessWorkerThread();
    preprocessWorkerThread = new SummVideoSelectorPreprocessWorkerThread(item, this);

    connect(preprocessWorkerThread, SIGNAL(preprocessStarted(SummVideoSelectorModelItem*)), SLOT(on_preprocessWorkerThread_preprocessStarted(SummVideoSelectorModelItem*)));
    connect(preprocessWorkerThread, SIGNAL(preprocessDone(SummVideoSelectorModelItem*)), SLOT(on_preprocessWorkerThread_preprocessDone(SummVideoSelectorModelItem*)));
    connect(preprocessWorkerThread, SIGNAL(progressChanged(SummVideoSelectorModelItem*,int)), SLOT(on_preprocessWorkerThread_progressChanged(SummVideoSelectorModelItem*,int)));

    preprocessWorkerThread->start();
}

void SummVideoSelector::on_btnImport_clicked()
{
    QFileDialog dialog(this);
    dialog.setDirectory(QDir::homePath());
    dialog.setFileMode(QFileDialog::ExistingFiles);
    dialog.setNameFilter(trUtf8("Video sequences (*.avi)"));

    if(dialog.exec())
    {
        QStringList fileNames = dialog.selectedFiles();
        for(QStringList::iterator it = fileNames.begin(); it != fileNames.end(); it++)
        {
            SummVideoSelectorModelItem *item = SummVideoSelectorModel::create(*it);
            model->addItem(item);
        }
    }
}

void SummVideoSelector::on_model_item_added(SummVideoSelectorModelItem *item)
{
    QListWidgetItem *listWidgetItem = new QListWidgetItem;
    listWidgetItem->setData(Qt::DisplayRole, item->getShortFilename());
    listWidgetItem->setData(Qt::CheckStateRole, item->isPreprocessed()? Qt::Checked : Qt::Unchecked);

    ui->list->addItem(listWidgetItem);

    if(model->getItems().size() == 1)
    {
        ui->list->setCurrentRow(0);
        ui->list->setCurrentItem(ui->list->item(0));
    }
}

void SummVideoSelector::on_model_item_changed(SummVideoSelectorModelItem *item)
{
    int row = findListRow(item);

    if(row >= 0 && row <= ui->list->count())
    {
        updateRow(row);
    }

    if(!ui->list->selectedItems().empty() && model->getItems()[row] == item)
    {
        updateState();
    }
}

void SummVideoSelector::on_model_item_removed(int idx)
{
    Q_UNUSED(idx);

    if(!removeAllMode)
    {
        delete ui->list->takeItem(idx);
    }

    if(model->getItems().size() == 0)
    {
        setStateEmpty();
    }
}

void SummVideoSelector::on_model_changed()
{
}

void SummVideoSelector::on_btnDelete_clicked()
{
    if(!ui->list->selectedItems().empty())
    {
        int idx = ui->list->row(ui->list->selectedItems().first());
        model->removeItem(idx);
    }
}

void SummVideoSelector::on_btnClear_clicked()
{
    removeAllMode = true;

    model->clear();
    ui->list->clear();

    removeAllMode = false;
}

int SummVideoSelector::findListRow(SummVideoSelectorModelItem *item)
{
    for(int i = 0; i < (signed)model->getItems().size(); i++)
    {
        if(item == model->getItems()[i])
        {
            return i;
        }
    }

    return -1;
}

void SummVideoSelector::updateState()
{
    int currentRow = ui->list->currentRow();

    if(currentRow >= 0 && currentRow < (signed)model->getItems().size())
    {
        setState(model->getItems()[currentRow]);
    }
    else if(model->getItems().empty())
    {
        setStateEmpty();
    }
    else
    {
        ui->list->setCurrentRow(0);
    }
}

void SummVideoSelector::updateRow(int idx)
{
    SummVideoSelectorModelItem *item = model->getItems()[idx];
    ui->list->item(idx)->setData(Qt::CheckStateRole, item->isPreprocessed()? Qt::Checked : Qt::Unchecked);
}

void SummVideoSelector::setStateEmpty()
{
    ui->btnClear->setEnabled(false);
    ui->btnDelete->setEnabled(false);
    ui->btnExport->setEnabled(false);
    ui->btnProcess->setEnabled(false);
    ui->list->setEnabled(false);
    ui->progressBar->setValue(0);

    ui->btnProcess->setVisible(true);
    ui->btnCancelProcess->setVisible(false);
    ui->btnDeleteProcessedData->setVisible(false);

    emit itemSelected(NULL);
}

void SummVideoSelector::setStateReadyItemSelected(SummVideoSelectorModelItem *item)
{
    Q_UNUSED(item);

    ui->btnClear->setEnabled(true);
    ui->btnDelete->setEnabled(true);
    ui->btnExport->setEnabled(true);
    ui->btnProcess->setEnabled(true);
    ui->list->setEnabled(true);

    ui->btnProcess->setVisible(false);
    ui->btnCancelProcess->setVisible(false);
    ui->btnDeleteProcessedData->setVisible(true);

    ui->progressBar->setValue(100);

    emit itemSelected(item);
}

void SummVideoSelector::setStateNotReadyItemSelected(SummVideoSelectorModelItem *item)
{
    Q_UNUSED(item);

    ui->btnClear->setEnabled(true);
    ui->btnDelete->setEnabled(true);
    ui->btnExport->setEnabled(false);
    ui->btnProcess->setEnabled(true);
    ui->list->setEnabled(true);

    ui->btnProcess->setVisible(true);
    ui->btnCancelProcess->setVisible(false);
    ui->btnDeleteProcessedData->setVisible(false);

    ui->progressBar->setValue(0);

    emit itemSelected(item);
}

void SummVideoSelector::setState(SummVideoSelectorModelItem *item)
{
    if(item->isPreprocessed())
    {
        setStateReadyItemSelected(item);
    }
    else
    {
        setStateNotReadyItemSelected(item);
    }
}

void SummVideoSelector::on_list_currentRowChanged(int currentRow)
{
    Q_UNUSED(currentRow);

    updateState();

    SummVideoSelectorModelItem *item = NULL;
    if(!ui->list->selectedItems().empty() && model->getItems().size() > 0)
    {
        item = model->getItems()[ui->list->currentRow()];
    }
}

void SummVideoSelector::on_btnDeleteProcessedData_clicked()
{
    SummVideoSelectorModelItem *item = model->getItems()[ui->list->currentRow()];

    if(!item->deletePreprocessedData())
    {
        QMessageBox box(QMessageBox::Icon::Warning, "An error occured", "Cannot delete file: " + item->getPreprocessedDataFilename());
        box.show();
    }

    updateState();
}

void SummVideoSelector::on_btnProcess_clicked()
{
    assert(!ui->list->selectedItems().empty());

    SummVideoSelectorModelItem *item = model->getItems()[ui->list->currentRow()];
    runPreprocessWorkerThread(item);
}

void SummVideoSelector::on_btnCancelProcess_clicked()
{
    stopPreprocessWorkerThread();
    ui->btnProcess->setVisible(true);
    ui->btnCancelProcess->setVisible(false);
    ui->progressBar->setValue(0);
    ui->progressBar->setEnabled(false);
}

void SummVideoSelector::on_preprocessWorkerThread_preprocessStarted(SummVideoSelectorModelItem *item)
{
    Q_UNUSED(item);

    ui->btnProcess->setVisible(false);
    ui->btnCancelProcess->setVisible(true);
    ui->progressBar->setEnabled(true);
}

void SummVideoSelector::on_preprocessWorkerThread_preprocessDone(SummVideoSelectorModelItem *item)
{
    Q_UNUSED(item);

    ui->btnProcess->setVisible(true);
    ui->btnCancelProcess->setVisible(false);
    ui->progressBar->setEnabled(false);

    updateState();
}

void SummVideoSelector::on_preprocessWorkerThread_progressChanged(SummVideoSelectorModelItem *item, int progress)
{
    Q_UNUSED(item);

    ui->progressBar->setValue((ui->btnCancelProcess->isEnabled())? progress : 0);
}

void SummVideoSelector::on_btnExport_clicked()
{
    emit exportRequested();
}

void SummVideoSelector::enableExport()
{
    ui->btnExport->setEnabled(true);
}

SummVideoSelectorModelItem::Status SummVideoSelectorModelItem::determineCurrentStatus()
{
    bool videoExists = QFile::exists(getFilename());
    bool dataExists = QFile::exists(getPreprocessedDataFilename());

    if(videoExists && dataExists)
    {
        return Ready;
    }
    else if(videoExists && !dataExists)
    {
        return PreprocessNeeded;
    }
    else
    {
        return Error;
    }
}

void SummVideoSelectorModelItem::setStatus(Status newStatus)
{
    status = newStatus;
    emit changed(this);
}

void SummVideoSelectorModelItem::updateStatus()
{
    setStatus(determineCurrentStatus());
}

SummVideoSelectorModelItem::SummVideoSelectorModelItem(QString filename) : QObject()
{
    this->filename = filename;
    this->status = determineCurrentStatus();
    this->userData = NULL;
}

QString SummVideoSelectorModelItem::getFilename() const
{
    return filename;
}

QString SummVideoSelectorModelItem::getShortFilename() const
{
    QFileInfo info(getFilename());
    return info.fileName();
}

QString SummVideoSelectorModelItem::getPreprocessedDataFilename() const
{
    return filename + ".features.csv";
}

bool SummVideoSelectorModelItem::isPreprocessed() const
{
    return QFile::exists(getPreprocessedDataFilename());
}

SummVideoSelectorModelItem::Status SummVideoSelectorModelItem::getStatus() const
{
    return status;
}

void *SummVideoSelectorModelItem::getUserData() const
{
    return userData;
}

void SummVideoSelectorModelItem::setUserData(void *data)
{
    userData = data;
}

bool SummVideoSelectorModelItem::deletePreprocessedData()
{
    QFile file(getPreprocessedDataFilename());

    if(file.exists())
    {
        if(file.remove())
        {
            updateStatus();
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return true;
    }
}

SummVideoSelectorModel::SummVideoSelectorModel() : QObject()
{
}

void SummVideoSelectorModel::addItem(SummVideoSelectorModelItem *item)
{
    connect(item, SIGNAL(changed(SummVideoSelectorModelItem *)), SLOT(on_item_changed(SummVideoSelectorModelItem*)));

    items.push_back(item);
    emit item_added(item);
    emit changed();
}

void SummVideoSelectorModel::removeItem(int idx)
{
    SummVideoSelectorModelItem *item = items.at(idx);

    items.erase(items.begin()+idx, items.begin()+idx+1);
    delete item;

    emit item_removed(idx);
    emit changed();
}

void SummVideoSelectorModel::clear()
{
    int size = items.size();
    for(int i = 0; i < size; i++)
    {
        removeItem(0);
    }
}

SummVideoSelectorModel::Items & SummVideoSelectorModel::getItems()
{
    return items;
}

SummVideoSelectorModelItem *SummVideoSelectorModel::create(QString filename)
{
    return new SummVideoSelectorModelItem(filename);
}

void SummVideoSelectorModel::on_item_changed(SummVideoSelectorModelItem *item)
{
    emit item_changed(item);
}

SummVideoSelectorPreprocessWatcherThread::SummVideoSelectorPreprocessWatcherThread(VideoSequence *item, int interval, QObject *parent) : QThread(parent)
{
    setItem(item);
    setInterval(interval);
}

void SummVideoSelectorPreprocessWatcherThread::setItem(VideoSequence *item)
{
    this->item = item;
}

void SummVideoSelectorPreprocessWatcherThread::setInterval(int interval)
{
    this->interval = interval;
}

void SummVideoSelectorPreprocessWatcherThread::run()
{
    this->progress = -1;

    while(true)
    {
        assert(item != NULL);
        assert((item)->GetProperty(CV_CAP_PROP_FRAME_COUNT) > 0);

        double posFrames = (item)->GetProperty(CV_CAP_PROP_POS_FRAMES);
        double frameCount = (item)->GetProperty(CV_CAP_PROP_FRAME_COUNT);
        int newProgress = (int)(100.0*posFrames/frameCount);

        if(newProgress != progress)
        {
            emit progressChanged(newProgress);
            progress = newProgress;
        }

        if(progress == 100)
        {
            return;
        }

        msleep(interval);
    }
}

SummVideoSelectorPreprocessWorkerThread::SummVideoSelectorPreprocessWorkerThread(SummVideoSelectorModelItem *item, QObject *parent) : QThread(parent)
{
    this->item = item;

    watcherThread = new SummVideoSelectorPreprocessWatcherThread(NULL, 500, parent);
    connect(watcherThread, SIGNAL(progressChanged(int)), SLOT(on_watcher_progressChanged(int)));
}

SummVideoSelectorPreprocessWorkerThread::~SummVideoSelectorPreprocessWorkerThread()
{
    terminateWatcherThread();
}

void SummVideoSelectorPreprocessWorkerThread::run()
{
    //terminateWatcherThread();

    emit preprocessStarted(item);

    VideoSequencePtr sequence = NULL;
    try
    {
        sequence = new VideoSequence(item->getFilename().toStdString());
        sequence->forceCreateCapture();

        try
        {
            watcherThread->setItem(sequence);
            watcherThread->start();

            VideoSequenceProcessor::FeatureExtractorsPtr extractors = new VideoSequenceProcessor::FeatureExtractors();
            extractors->push_back(new SimpleSubtractionFeature::MultiVectorExtractor());

            VideoSequenceProcessorPtr processor = new VideoSequenceProcessor(extractors);

            VideoSequenceProcessor::TubeFeaturesPtr features = processor->ProcessVideoSequence(sequence);
            cv::Mat featureData = (*features)[0]->GetData();

            MatrixCsvDataSource<double> dataSource(item->getPreprocessedDataFilename().toStdString());
            dataSource.Save(&featureData);

            terminateWatcherThread();
            item->updateStatus();
        }
        catch(...)
        {
            terminateWatcherThread();
            throw;
        }
    }
    catch(...)
    {
        item->setStatus(SummVideoSelectorModelItem::Error);
    }

    emit preprocessDone(item);
}

void SummVideoSelectorPreprocessWorkerThread::terminateWatcherThread()
{
    if(watcherThread != NULL)
    {
        if(watcherThread->isRunning())
        {


        }
        while(watcherThread->isRunning()) {watcherThread->terminate();}
        //delete watcherThread;
        //watcherThread = NULL;
    }

}

void SummVideoSelectorPreprocessWorkerThread::on_watcher_progressChanged(int progress)
{
    emit progressChanged(item, progress);
}

SummVideoSelectorModelItem *SummVideoSelector::itemgetCurrentItem()
{
    return model->getItems()[ui->list->currentRow()];
}
