#include <QDateTime>
#include <QDir>
#include <QFileDialog>
#include <QIcon>
#include <QMessageBox>
#include <QSharedPointer>
#include <QSize>
#include <QStandardPaths>
#include <QtConcurrentRun>
#include <opencv2/highgui/highgui.hpp>
#include <assert.h>
#include <openbr/openbr_plugin.h>

#include "gallerytoolbar.h"
#include "utility.h"

/**** GALLERY ****/
/*** STATIC ***/
QMutex br::GalleryToolBar::galleryLock;

/*** PUBLIC ***/
br::GalleryToolBar::GalleryToolBar(QWidget *parent)
    : QToolBar("Gallery", parent)
{
    lGallery.setAlignment(Qt::AlignCenter);
    lGallery.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    tbOpenFile.setIcon(QIcon(":/glyphicons/png/glyphicons_138_picture@2x.png"));
    tbOpenFile.setToolTip("Load Photo");
    tbOpenFolder.setIcon(QIcon(":/glyphicons/png/glyphicons_144_folder_open@2x.png"));
    tbOpenFolder.setToolTip("Load Photo Folder");
    tbWebcam.setCheckable(true);
    tbWebcam.setIcon(QIcon(":/glyphicons/png/glyphicons_301_webcam@2x.png"));
    tbWebcam.setToolTip("Load Webcam");
    tbBack.setEnabled(false);
    tbBack.setIcon(QIcon(":/glyphicons/png/glyphicons_221_unshare@2x.png"));
    tbBack.setToolTip("Back");
    tbMean.setIcon(QIcon(":/glyphicons/png/glyphicons_003_user@2x.png"));
    tbMean.setToolTip("Mean Image");

    addWidget(&tbOpenFile);
    addWidget(&tbOpenFolder);
    addWidget(&tbWebcam);
    addSeparator();
    addWidget(&lGallery);
    addSeparator();
    addWidget(&tbBack);
    addWidget(&tbMean);
    setIconSize(QSize(20,20));

    connect(&tbOpenFile, SIGNAL(clicked()), this, SLOT(openFile()));
    connect(&tbOpenFolder, SIGNAL(clicked()), this, SLOT(openFolder()));
    connect(&tbBack, SIGNAL(clicked()), this, SLOT(home()));
    connect(&tbMean, SIGNAL(clicked()), this, SLOT(mean()));
    connect(&enrollmentWatcher, SIGNAL(finished()), this, SLOT(enrollmentFinished()));
    connect(&timer, SIGNAL(timeout()), this, SLOT(checkWebcam()));

    timer.start(500);
}

/*** PUBLIC SLOTS ***/
void br::GalleryToolBar::enroll(const br::File &input)
{
    if (input.isNull()) return;
    enrollmentWatcher.setFuture(QtConcurrent::run(this, &GalleryToolBar::_enroll, input));
}

void br::GalleryToolBar::enroll(const QImage &input)
{
    QString tempFileName = br::Context::scratchPath() + "/tmp/" + QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss:zzz") + ".png";
    input.save(tempFileName);
    enroll(tempFileName);
}

void br::GalleryToolBar::select(const br::File &file)
{
    tbBack.setEnabled(true);
    emit newFiles(br::FileList() << file);
    emit newGallery(file);
}

/*** PRIVATE ***/
void br::GalleryToolBar::_enroll(const br::File &input)
{
    galleryLock.lock();
    this->input = input;
    gallery = input.name + ".mem";
    br::Enroll(input.flat(), gallery.flat());
    files = FileList::fromGallery(gallery);

    galleryLock.unlock();
}

void br::GalleryToolBar::_checkWebcam()
{
    static QSharedPointer<cv::VideoCapture> videoCapture;
    if (videoCapture.isNull()) {
        videoCapture = QSharedPointer<cv::VideoCapture>(new cv::VideoCapture(0));
        cv::Mat m;
        while (!m.data) videoCapture->read(m); // First frames can be empty
    }

    if (galleryLock.tryLock()) {
        cv::Mat m;
        videoCapture->read(m);
        galleryLock.unlock();
        enroll(toQImage(m));
    }
}

/*** PRIVATE SLOTS ***/
void br::GalleryToolBar::checkWebcam()
{
    // Check webcam
    if (!tbWebcam.isChecked()) return;
    QtConcurrent::run(this, &GalleryToolBar::_checkWebcam);
}

void br::GalleryToolBar::enrollmentFinished()
{
    if (files.isEmpty()) {
        if (input.get<bool>("enrollAll", false) && !tbWebcam.isChecked()) {
            QMessageBox msgBox;
            msgBox.setText("Quality test failed.");
            msgBox.setInformativeText("Enroll anyway?");
            msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
            msgBox.setDefaultButton(QMessageBox::Ok);
            int ret = msgBox.exec();

            if (ret == QMessageBox::Ok) {
                br::File file = input;
                file.set("enrollAll", QVariant(false));
                enroll(file);
            }
        }
        return;
    }

    lGallery.setText(input.baseName() + (files.size() != 1 ? " (" + QString::number(files.size()) + ")" : ""));
    tbBack.setEnabled(false);
    emit newFiles(files);
    emit newGallery(gallery);
}

void br::GalleryToolBar::home()
{
    tbBack.setEnabled(false);
    emit newGallery(gallery);
}

void br::GalleryToolBar::mean()
{
    const QString file = QString("%1/mean/%2.png").arg(br::Globals->scratchPath(), input.baseName()+input.hash());
    br_set_property("CENTER_TRAIN_B", qPrintable(file));
    br::File trainingFile = input;
    br_train(qPrintable(trainingFile.flat()), "[algorithm=MedianFace]");
    enroll(file);
}

void br::GalleryToolBar::openFile()
{
    enroll(QFileDialog::getOpenFileName(this, "Select Photo", QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first()));
}

void br::GalleryToolBar::openFolder()
{
    enroll(QFileDialog::getExistingDirectory(this, "Select Photo Directory", QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()));
}

#include "moc_gallerytoolbar.cpp"
