#include "mainwindow.h"
#include "ui_mainwindow.h"

QColor MainWindow::colorSegmentSumm = QColor(0,255,0);
QColor MainWindow::colorSegmentOther = QColor(255,0,0);

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

    workerThread = NULL;
    currentState = NULL;

    progressIndicator = new ProgressIndicator(centralWidget());

    playerThread = new PlayerThread(this);
    connect(playerThread, SIGNAL(playbackStarted(PlayerThread *)), SLOT(on_playerThread_playbackStarted(PlayerThread *)));
    connect(playerThread, SIGNAL(playbackStopped(PlayerThread *)), SLOT(on_playerThread_playbackStopped(PlayerThread *)));
    connect(playerThread, SIGNAL(nextFrameRequested(PlayerThread *, bool *)), SLOT(on_playerThread_nextFrameRequested(PlayerThread *, bool *)));    

    ui->methodSelector->addMethod(new FixedLengthIntervalLmAlgorithmSummMethodSelectorItem());
    ui->methodSelector->addMethod(new CoverageDrivenLmAlgorithmSummMethodSelectorItem());
    ui->methodSelector->setMethod(1);
    ui->methodSelector->setAutoRecompute(true);

    setStateAllClosed();


}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setStateAllClosed()
{
    currentState = NULL;

    ui->videoPlayer->close();
    ui->segmentSelector->setSegments(SegmentsTimeline::Segments());
    ui->videoControls->setEnabled(false);
    ui->methodSelector->setEnabled(false);
}

void MainWindow::setStateSourceVideoOpened(QString video)
{
    currentState->openedVideoFilename = video;
    ui->videoPlayer->open(video);

    SummarizationFramework::Model::VideoSequencePtr sequence = new SummarizationFramework::Model::VideoSequence(video.toStdString()); // TODO zbytecne neotvirat kvuli zjisteni delky
    currentState->openedVideoLength = sequence->GetLength();
    currentState->openedVideoFps = sequence->GetFps();

    ui->segmentSelector->setSegments(SegmentsTimeline::Segments());

    ui->videoControls->setEnabled(false);
    ui->methodSelector->setEnabled(false);

    currentState->playerSchema.clear();
}

void MainWindow::setStateSummarizedVideoOpened(QString video, SummarizationFramework::Model::TubeCollectionPtr tubes)
{
    currentState->playerSchema = tubesToPlaySchema(tubes);
    currentState->tubes = tubes;

    currentState->openedVideoFilename = video;
    ui->videoPlayer->open(video);

    SummarizationFramework::Model::VideoSequence sequence(video.toStdString()); // TODO zbytecne neotvirat kvuli zjisteni delky
    currentState->openedVideoLength = sequence.GetLength();
    currentState->openedVideoFps = sequence.GetFps();

    SegmentsTimeline::Segments segments = tubesToSegments(tubes, currentState->openedVideoLength);
    ui->segmentSelector->setSegments(segments);

    ui->videoControls->setEnabled(true);
    ui->methodSelector->setEnabled(true);

    ui->videoSelector->enableExport();
    ui->videoControls->setSequence(VideoControls::SEQ_SUMMARIZED);
}

void MainWindow::on_videoSelector_itemSelected(SummVideoSelectorModelItem *item)
{
    if(item != NULL)
    {
        acceptOrCreateState(item);
        currentState->openedVideoFilename = item->getFilename();

        if(currentState->tubes != NULL)
        {
            if(item->isPreprocessed())
            {
                setStateSummarizedVideoOpened(currentState->openedVideoFilename, currentState->tubes);
            }
            else
            {
                currentState->clear();
                setStateSourceVideoOpened(item->getFilename());
            }
        }
        else
        {
            if(item->isPreprocessed())
            {
                if(currentState->featureDataSource != NULL)
                {
                    delete currentState->featureDataSource;
                }
                currentState->featureDataSource = new MatrixCsvDataSource<double>(item->getPreprocessedDataFilename().toStdString());

                computeSummarization();
            }

            setStateSourceVideoOpened(item->getFilename());
        }
    }
    else
    {
        setStateAllClosed();
    }
}

void MainWindow::startWorker(SummAlgorithmCore *method, cv::Mat data, QString filename)
{
    if(workerThread != NULL && workerThread->isRunning())
    {
        workerThread->terminate();
        while(workerThread->isRunning()) {}
        delete workerThread;
        workerThread = NULL;
    }

    workerThread = new SummWorkerThread(method, data, filename, this);
    connect(workerThread, SIGNAL(started(SummWorkerThread*)), SLOT(on_worker_started(SummWorkerThread*)));
    connect(workerThread, SIGNAL(finished(SummWorkerThread*)), SLOT(on_worker_finished(SummWorkerThread*)));

    assert(workerThread != NULL);

    workerThread->start();
}

void MainWindow::on_worker_started(SummWorkerThread *thread)
{
    Q_UNUSED(thread);
    progressIndicator->setText("Creating summarization...");
    progressIndicator->show();
}

void MainWindow::on_worker_finished(SummWorkerThread *thread)
{
    setStateSummarizedVideoOpened(currentState->openedVideoFilename, thread->getTubes());
    progressIndicator->hide();
}

SegmentsTimeline::Segments MainWindow::tubesToSegments(SummarizationFramework::Model::TubeCollectionPtr tubes, int videoLength)
{
    SegmentsTimeline::Segments segments;

    int nextStart = 1;
    for(SummarizationFramework::Model::TubeCollection::Iterator it = tubes->Begin(); it != tubes->End(); it++)
    {
        SummarizationFramework::Model::TubePtr tube = *it;
        SummarizationFramework::FrameSpan span = tube->GetFrameSpan();

        assert(span.From >= nextStart);

        if(span.From > nextStart)
        {
            SegmentsTimeline::Segment interSegment(nextStart, span.From-1, colorSegmentOther, NULL);
            segments.push_back(interSegment);
        }

        SegmentsTimeline::Segment regularSegment(span.From, span.To, colorSegmentSumm, tube);
        segments.push_back(regularSegment);

        nextStart = span.To+1;
    }

    if(nextStart < videoLength)
    {
        SegmentsTimeline::Segment endSegment(nextStart, videoLength, colorSegmentOther, NULL);
        segments.push_back(endSegment);
    }

    for(SegmentsTimeline::Segments::iterator it = segments.begin(); it != segments.end(); it++)
    {
        SegmentsTimeline::Segment seg = *it;
    }

    return segments;
}

CvVideoPlayer::PlaySchema MainWindow::tubesToPlaySchema(SummarizationFramework::Model::TubeCollectionPtr tubes)
{
    CvVideoPlayer::PlaySchema schema;

    for(SummarizationFramework::Model::TubeCollection::Iterator it = tubes->Begin(); it != tubes->End(); it++)
    {
        SummarizationFramework::Model::TubePtr tube = *it;
        SummarizationFramework::FrameSpan span = tube->GetFrameSpan();

        schema.push_back(cv::Vec2i(span.From, span.To));
    }

    return schema;
}

void MainWindow::on_videoControls_play()
{
    playerThread->startPlayback(PlayerThread::PLAY_ALL, currentState->openedVideoFps);
}

void MainWindow::on_videoControls_stop()
{
    playerThread->stopPlayback();
}

void MainWindow::on_videoControls_goToStart()
{
    ui->videoPlayer->previousSegment();
}

void MainWindow::on_videoControls_goToEnd()
{
    ui->videoPlayer->nextSegment();
}

void MainWindow::on_videoControls_goBackwards()
{
    ui->videoPlayer->setPosition(ui->videoPlayer->getPosition()-2);
}

void MainWindow::on_videoControls_goBackwardsFast()
{

}

void MainWindow::on_videoControls_goForwards()
{
    ui->videoPlayer->next();
}

void MainWindow::on_videoControls_goForwardsFast()
{

}

void MainWindow::on_videoControls_changeSequence(VideoControls::Sequence seq)
{
    if(seq == VideoControls::SEQ_SUMMARIZED)
    {
        ui->videoPlayer->setSchema(currentState->playerSchema);
    }
    else
    {
        ui->videoPlayer->setSchema();
    }
}

void MainWindow::on_segmentSelector_cursorMoved(int segmentIdx, double positionSegment, double positionGlobal)
{
    Q_UNUSED(segmentIdx);
    Q_UNUSED(positionSegment);

    ui->videoPlayer->setPosition(positionGlobal*currentState->openedVideoLength);
}

void MainWindow::on_segmentSelector_segmentClicked(int segmentIdx, double positionSegment, double positionGlobal)
{
    Q_UNUSED(segmentIdx);
    Q_UNUSED(positionSegment);

    ui->videoPlayer->setPosition(positionGlobal*currentState->openedVideoLength);
    ui->segmentSelector->setCursorPositionGlobal(positionGlobal);
}

void MainWindow::on_videoPlayer_positionChanged(int frame)
{
    ui->segmentSelector->setCursorPositionGlobal((double)frame / currentState->openedVideoLength);
}

void MainWindow::on_playerThread_playbackStarted(PlayerThread *thread)
{
    Q_UNUSED(thread);
    ui->videoControls->setState(VideoControls::STATE_PLAYING);
}

void MainWindow::on_playerThread_playbackStopped(PlayerThread *thread)
{
    Q_UNUSED(thread);
    ui->videoControls->setState(VideoControls::STATE_IDLE);
}

void MainWindow::on_playerThread_nextFrameRequested(PlayerThread *thread, bool *requestStop)
{
    Q_UNUSED(thread);
    *requestStop = !ui->videoPlayer->next();
}

void  MainWindow::on_videoSelector_exportRequested()
{
    QFileDialog dialog(this);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    dialog.setNameFilter(trUtf8("Video sequences (*.avi)"));

    if(dialog.exec())
    {
        ExportWindow *exportWindow = new ExportWindow(currentState->openedVideoFilename, dialog.selectedFiles().first(), currentState->tubes, this);
        exportWindow->exec();
    }
}

void MainWindow::on_methodSelector_recomputeRequested(SummMethodSelector *selector)
{
    Q_UNUSED(selector);
    computeSummarization();
}

void MainWindow::resizeEvent(QResizeEvent *e) {
    assert(progressIndicator != NULL);
    assert(e != NULL);

    progressIndicator->move(ui->videoViewLayout->geometry().x(), ui->videoViewLayout->geometry().y());
    progressIndicator->resize(ui->videoViewLayout->geometry().width(), ui->videoViewLayout->geometry().y()-ui->videoViewLayout->geometry().y()+ui->videoViewLayout->geometry().height());
    e->accept();
}

void MainWindow::acceptOrCreateState(SummVideoSelectorModelItem *item)
{
    if(item->getUserData() == NULL)
    {
        currentState = new WindowState();
        item->setUserData(currentState);
    }
    else
    {
        currentState = (WindowState *)item->getUserData();
    }

    if(currentState->algorithm != NULL)
    {
        ui->methodSelector->setMethod(currentState->algorithm);
    }
}

void MainWindow::computeSummarization()
{
    if(currentState != NULL && !currentState->isEmpty())
    {
        ITubeFeature::FeatureData data = *currentState->featureDataSource->Load();

        if(currentState->algorithm != NULL)
        {
            delete currentState->algorithm;
        }
        currentState->algorithm = ui->methodSelector->getMethod();

        startWorker(currentState->algorithm, data, currentState->openedVideoFilename);
    }
}
