/** mainwindow.cpp - Qt main window
 *
 * bsAniPic - http://www.buckosoft.com/bsAniPic/
 *
 * Copyright(c) BuckoSoft, 2017.
 * Released under GNU GPL v3 https://www.gnu.org/licenses/gpl-3.0.en.html
 */

#include <QDebug>
#include <QFileDialog>
#include <QSettings>
#include <QCloseEvent>
#include "dirman.h"
#include "mainwindow.h"
#include "imagelabel.h"
#include "ui_mainwindow.h"
#include "configdialog.h"
#include "getframedialog.h"
#include "animationoptionsdialog.h"

QString   companyName = "BuckoSoft";
QString   productName = "bsAniPic";

static const bool DEBUG = false;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    timer(this),
    goIcon(":/resources/icons/play.png"),
    stopIcon(":/resources/icons/stop.png")
{
    dirMan = new DirMan();
    config = new Config();
    animateOptions = new AnimationOptions();
    currentImage = 0;
    leftMarker = -1;
    rightMarker = -1;
    animateDir = 1;
    isFocused = true;
    virgin = true;
    readSettings();
    ui->setupUi(this);
    connect(this, SIGNAL(window_loaded()), this, SLOT(onWindowLoaded()));
    connect(&timer, SIGNAL(timeout()), this, SLOT(on_timer()));
    connect(ui->imageLabel, SIGNAL(clicked(Qt::MouseButtons, QPoint)), this, SLOT(onImageClicked(Qt::MouseButtons)));
    setWindowTitle(productName);
    QList<QMenu*> menus = menuBar()->findChildren<QMenu*>();
    //qInfo() << "menus size=" << menus.size();
    foreach(QMenu* menu, menus) {
        foreach(QAction* a, menu->actions()) {
            if (a->menu()) {
                QMenu* m = a->menu();
                //qDebug("action: %s", qUtf8Printable(a->text()));
                if (a->text() == "&Recent") {
                    for (int i=0; i< MaxRecentFiles; i++) {
                        recentFileActions[i] = new QAction(this);
                        recentFileActions[i]->setVisible(false);
                        connect(recentFileActions[i], SIGNAL(triggered()), this, SLOT(openRecentDirectory()));
                        m->addAction(recentFileActions[i]);
                    }
                }
            } else if (a->text() == "&Go") {
                goAction = a;
            }
        }
    }
}

MainWindow::~MainWindow()
{
    if (timer.isActive())
        timer.stop();
    delete dirMan;
    delete config;
    delete ui;
}

void MainWindow::updateMRUActions()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, companyName, productName);
    QStringList dirs = settings.value("recentFileList").toStringList();
    dirs.sort();
    int numRecentFiles = qMin(dirs.size(), (int)MaxRecentFiles);
    for (int i=0; i<numRecentFiles; i++) {
        QString s = tr("&%1 %2").arg(QString::number(i+1), dirs[i]);
        recentFileActions[i]->setText(s);
        recentFileActions[i]->setData(dirs[i]);
        recentFileActions[i]->setVisible(true);
    }
    for (int j=numRecentFiles; j<MaxRecentFiles; j++)
        recentFileActions[j]->setVisible(false);
}

void MainWindow::setNumberOfImages(int i)
{
    ui->horizontalSlider->setMaximum(i > 0 ? i-1 : 0);
}

void MainWindow::on_horizontalSlider_valueChanged(int value)
{
    // qInfo() << "slider changed " << value;
    currentImage = value;
    displayCurrentImage();
}

void MainWindow::wheelEvent(QWheelEvent* event)
{
    if (timer.isActive())
        on_actionGo_triggered();        // turn off animation
    int mul = 1;
    if (event->modifiers() & Qt::CTRL)
        mul = 10;
    if (event->modifiers() & Qt::SHIFT)
        mul *= 10;
    // qInfo() << "mul=" << mul;
    if (event->delta() > 0) {
        if (currentImage-mul >= 0) {
            currentImage -= mul;
            //displayCurrentImage();
        } else if (currentImage != 0) {
            currentImage = 0;
            //displayCurrentImage();
        }
    } else {
        if (currentImage+mul <= dirMan->getNumberOfImages()-1) {
            currentImage += mul;
            //displayCurrentImage();
        } else if (currentImage < dirMan->getNumberOfImages()-1) {
            currentImage = dirMan->getNumberOfImages()-1;
            //displayCurrentImage();
        }
    }
    if (ui->horizontalSlider->value() != currentImage)
        ui->horizontalSlider->setValue(currentImage);
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    qDebug() << "keyPressEvent:" << event->key();
    switch(event->key()) {
    case ' ':
        animateDir = 1;
        on_actionGo_triggered();
        break;
    case 'A':
        on_animationOptions();
        break;
    case 'B':
        animateDir = -1;
        on_actionGo_triggered();
        break;
    case 'C':
        on_actionConfigure_triggered();
        break;
    case 'G':
        onKeyGoTo();
        break;
    case 'R':
        on_actionRefresh_triggered();
        break;
    case ',':
        on_setLeftMarker();
        break;
    case '.':
        on_setRightMarker();
        break;
    case Qt::Key_Delete:
        // future
        break;
    }
}

void MainWindow::changeEvent(QEvent* event)
{
    QMainWindow::changeEvent(event);
    if (event->type() == QEvent::ActivationChange) {
        isFocused = this->isActiveWindow();
        //qDebug() << "focused: " << isFocused;
        if (isFocused) {
            qDebug() << "focused. refresh";
            on_actionRefresh_triggered();
        }
        activatedTime = QTime::currentTime();
    }
}

void MainWindow::on_actionExit_triggered()
{
    QApplication::quit();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    writeSettings();
    if (timer.isActive())
        timer.stop();
    dirMan->prepareToDie();
    event->accept();
}

void MainWindow::showEvent(QShowEvent *ev)
{
    QMainWindow::showEvent(ev);
    emit window_loaded();
}

void MainWindow::onWindowLoaded()
{
//    if (dirMan->getNumberOfImages() > 0) {
//        on_actionRefresh_triggered();
//    }
//    else
    if (virgin && !currentDirectory.isEmpty()) {
        dirMan->setDirectory(currentDirectory);
        activateDirectory();
    }
    virgin = false;
}

void MainWindow::openRecentDirectory()
{
    QAction *action = qobject_cast<QAction *>(sender());
    if (action) {
        currentDirectory = action->data().toString();
        dirMan->setDirectory(currentDirectory);
        activateDirectory();
    }

}

void MainWindow::writeSettings()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, companyName, productName);
    settings.beginGroup("MainWindow");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.endGroup();
    settings.beginGroup("Options");
    settings.setValue("fileInWindowTitle", config->isFilenameInWindowTitle);
    settings.setValue("displayBsacText", config->isDisplayBsacText);
    settings.endGroup();
    settings.beginGroup("AnimateOptions");
    settings.setValue("stopAtMissingFrames", animateOptions->stopAtMissingFrames);
    settings.endGroup();
    settings.setValue("directory", currentDirectory);
}

void MainWindow::readSettings()
{
    if (DEBUG) qDebug() << "MainWindow::readSettings";
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, companyName, productName);
    settings.beginGroup("MainWindow");
    resize(settings.value("size", QSize(400,400)).toSize());
    move(settings.value("pos", QPoint(200,200)).toPoint());
    settings.endGroup();
    settings.beginGroup("Options");
    config->isFilenameInWindowTitle = settings.value("fileInWindowTitle", false).toBool();
    config->isDisplayBsacText = settings.value("displayBsacText", false).toBool();
    settings.endGroup();
    settings.beginGroup("AnimateOptions");
    animateOptions->stopAtMissingFrames = settings.value("stopAtMissingFrames", false).toBool();
    settings.endGroup();
    currentDirectory = settings.value("directory", "").toString();
    dirMan->setLoadBsacText(config->isDisplayBsacText);
    //config->virgin = false;
}

void MainWindow::on_actionConfigure_triggered()
{
    ConfigDialog dialog;
    dialog.setWindowTitle("Configure bsAniPic");
    dialog.setDisplayImageNameInTitle(config->isFilenameInWindowTitle);
    dialog.setDisplayBsacText(config->isDisplayBsacText);
    dialog.setDirName(currentDirectory);
    dialog.setIsBsAniPicDir(dirMan->isBsAniDirectory);
    dialog.setPovDebugFileName(dirMan->povDebugFileName);
    int result = dialog.exec();
    qInfo() << "result" << result;
    if (result) {
        Config* oldConfig = config;
        Config* newConfig = new Config();
        //newConfig->virgin = false;
        newConfig->isFilenameInWindowTitle = dialog.getDisplayImageNameInTitle();
        newConfig->isDisplayBsacText = dialog.getDisplayBsacText();
        dirMan->setLoadBsacText(newConfig->isDisplayBsacText);
        config = newConfig;
        if (oldConfig->isFilenameInWindowTitle != newConfig->isFilenameInWindowTitle) {
            if (newConfig->isFilenameInWindowTitle)
                displayCurrentImage();
            else
                setWindowTitle(productName);
        }
        if (oldConfig->isDisplayBsacText != newConfig->isDisplayBsacText) {
            resizeMainWindow();
            if (newConfig->isDisplayBsacText) {
                displayCurrentImage();
            }
        }
        if (dialog.getIsBsAniPicDir() && ! dirMan->isBsAniDirectory) {
            dirMan->createBsAniDirectory(dialog.getPovDebugFileName());
            dirMan->setDirectory(currentDirectory);
            resizeMainWindow();
            displayCurrentImage();
        }
        delete oldConfig;
    }
}

void MainWindow::on_animationOptions()
{
    AnimationOptionsDialog dialog;
    dialog.setWindowTitle("Animation Options");
    dialog.fromOptions(animateOptions);
    int result = dialog.exec();
    if (result) {
        dialog.toOptions(animateOptions);
        QSettings settings(QSettings::IniFormat, QSettings::UserScope, companyName, productName);
    }
}

void MainWindow::onKeyGoTo()
{
    GetFrameDialog  dialog;
    int result = dialog.exec();
    if (result) {
        qDebug() << "Get frame: " << dialog.getValue();
        currentImage = dirMan->getImageNumberFromFrameNumber(dialog.getValue());
        displayCurrentImage();
    }
}

void MainWindow::displayMainImage(const QImage *image)
{
    if (image != NULL)
        ui->imageLabel->setPixmap(QPixmap::fromImage(*image));
}

void MainWindow::on_actionOpen_Directory_triggered()
{
    QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
                                                 !dirMan->getDirectory().isEmpty() ? dirMan->getDirectory() : "/",
                                                 QFileDialog::ShowDirsOnly
                                                 | QFileDialog::DontResolveSymlinks);
    qDebug() << "\ndir =" << dir;
    if (!dir.isEmpty()) {
        currentDirectory = dir;
        dirMan->setDirectory(dir);
        activateDirectory();
    }
}

void MainWindow::activateDirectory()
{
    const QImage* image = NULL;
    int numberOfImages = dirMan->getNumberOfImages();
    setNumberOfImages(numberOfImages);
    ui->horizontalSlider->setValue(0);
    if (numberOfImages > 0) {
        dirMan->setCurrentImage(0);
        image = dirMan->getCurrentImage();
    } else
        image = dirMan->getBadImage();
    displayMainImage(image);
    if (dirMan->getNumberOfImages() > 0 && (dirMan->isBsacDirectory || dirMan->isBsAniDirectory)) {
        const BsImage* bi = dirMan->getCurrentBsImage();
        if (bi != NULL)
            ui->textEdit->setText(bi->text);
    } else
        ui->textEdit->setText("");
    resizeMainWindow();

    // update MRU
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, companyName, productName);
    QStringList files = settings.value("recentFileList").toStringList();
    files.removeAll(currentDirectory);
    files.prepend(currentDirectory);
    while (files.size() > MaxRecentFiles)
        files.removeLast();
    settings.setValue("recentFileList", files);
    updateMRUActions();
}

void MainWindow::resizeMainWindow()
{
    const QImage* image = NULL;
    int numberOfImages = dirMan->getNumberOfImages();
    if (numberOfImages > 0) {
        dirMan->setCurrentImage(0);
        image = dirMan->getCurrentImage();
    } else
        image = dirMan->getBadImage();

    if ((!dirMan->isBsacDirectory && !dirMan->isBsAniDirectory) || !config->isDisplayBsacText)
        ui->textEdit->hide();
    else
        ui->textEdit->show();
    // calculate window size
    QSize size;
    QSize imageSize = image->size();
    size = imageSize;
    int h = ui->horizontalSlider->height();
    h += ui->menuBar->height();
    h += ui->mainToolBar->height();
    h += ui->statusBar->height();
    h += 5;     // slop: slider runs under statusbar
    //ui->textEdit->resize(ui->textEdit->width(), 170);
    int textEditHeight = 170;
    if (!ui->textEdit->isHidden())
        h += textEditHeight;
    size += QSize(0,h);
    resize(size);
    ui->imageLabel->resize(imageSize);
    int w = imageSize.width() - ui->sliderLabel->width();
    ui->sliderLabel->move(w, imageSize.height());
    ui->horizontalSlider->resize(w, ui->horizontalSlider->height());
    ui->horizontalSlider->move(0, imageSize.height());
    ui->textEdit->move(0, imageSize.height() + ui->horizontalSlider->height());
    ui->textEdit->resize(imageSize.width(), textEditHeight);
    updateSliderLabelValue();
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    QMainWindow::resizeEvent(event);
    if (DEBUG) qDebug() << "resize";
    if ((!dirMan->isBsacDirectory && !dirMan->isBsAniDirectory) || !config->isDisplayBsacText)
        return;
    QSize size = event->size();
    int h = size.height();
    QPoint p = ui->textEdit->pos();
    h -= ui->statusBar->height();
    h -= p.ry();
    h -= 60;    // slop
    ui->textEdit->resize(size.width(), h);

}

void MainWindow::updateSliderLabelValue()
{
    QString f;
    int i;
    if (dirMan->getNumberOfImages() == 0) {
        f = "NO IMAGES";
        i = 0;
    } else {
        const BsImage* bi = dirMan->getCurrentBsImage();
        if (bi != NULL) {
            f = bi->filename;
            f = f.left(f.lastIndexOf('.'));
            i = currentImage+1;
        } else {
            f = "BAD IMAGES";
            i = 0;
        }
    }
    QString s = QString("%1/%2 %3").arg(QString::number(i),
                                        QString::number(dirMan->getNumberOfImages()),
                                        f);
    ui->sliderLabel->setText(s);
    if (config->isFilenameInWindowTitle) {
        QString s = QString("%1 - %2").arg(f, productName);
        setWindowTitle(s);
    }
}

void MainWindow::displayCurrentImage()
{
    dirMan->setCurrentImage(currentImage);
    const BsImage* bi = dirMan->getCurrentBsImage();
    if (bi != NULL) {
        displayMainImage(bi->image);
        if (ui->horizontalSlider->value() != currentImage)
            ui->horizontalSlider->setValue(currentImage);
        if (dirMan->isBsacDirectory || dirMan->isBsAniDirectory) {
            ui->textEdit->setText(bi->text);
        }
        updateSliderLabelValue();
    }
}

void MainWindow::stopAnimation()
{
    timer.stop();
    goAction->setIcon(goIcon);
}

void MainWindow::on_actionGo_triggered()
{
    if (timer.isActive()) {
        stopAnimation();
    } else {
        timer.start(42);
        goAction->setIcon(stopIcon);
        if (animateDir > 0) {
            if (animateOptions->stopAtMissingFrames && currentImage < dirMan->getNumberOfImages()-1) {
                currentImage++;
                displayCurrentImage();
            }
        } else {
            if (animateOptions->stopAtMissingFrames && currentImage > 0) {
                currentImage--;
                displayCurrentImage();
            }
        }
    }
}

void MainWindow::onImageClicked(Qt::MouseButtons buttons)
{
    QTime now = QTime::currentTime().addMSecs(-50);             // if this was the click the focused the window
    if (now < activatedTime)                                    // then don't start the animation, just take focus.
        return;

    if (timer.isActive()) {
        if ((buttons & Qt::LeftButton && animateDir == 1)
         || (buttons & Qt::RightButton && animateDir == -1)) {
            stopAnimation();
            return;
        }
        if (buttons & Qt::LeftButton && animateDir == -1) {
            animateDir = 1;
            return;
        }
        if (buttons & Qt::RightButton && animateDir == 1) {
            animateDir = -1;
            return;
        }

    }
    if (buttons & Qt::LeftButton) {
        animateDir = 1;
        if (animateOptions->stopAtMissingFrames && currentImage < dirMan->getNumberOfImages()-1) {
            currentImage++;
            displayCurrentImage();
        }
    } else {
        animateDir = -1;
        if (animateOptions->stopAtMissingFrames && currentImage > 0) {
            currentImage--;
            displayCurrentImage();
        }
    }
    on_actionGo_triggered();
}

void MainWindow::on_timer()
{
    if (animateDir > 0) {
        if (currentImage < dirMan->getNumberOfImages()-1) {
            int frameNumber = 0;
            const BsImage* bi = dirMan->getCurrentBsImageNaked();
            if (bi != NULL) {
                frameNumber = bi->frame;
            } else {
                qWarning() << "bi is null";
            }
            currentImage++;
            if (animateOptions->stopAtMissingFrames) {
                if (frameNumber+1 != dirMan->getBsImageNaked(currentImage)->frame) {
                    stopAnimation();
                    currentImage--;
                }
            }
            displayCurrentImage();
            if (leftMarker != -1 && currentImage == leftMarker)
                stopAnimation();
            if (rightMarker != -1 && currentImage == rightMarker)
                stopAnimation();
        } else {
            stopAnimation();
        }
    } else {
        if (currentImage > 0) {
            int frameNumber = 0;
            const BsImage* bi = dirMan->getCurrentBsImageNaked();
            if (bi != NULL) {
                frameNumber = bi->frame;
            } else {
                qWarning() << "bi is null";
            }
            currentImage--;
            if (animateOptions->stopAtMissingFrames) {
                if (frameNumber-1 != dirMan->getBsImageNaked(currentImage)->frame) {
                    stopAnimation();
                    currentImage++;
                }
            }
            displayCurrentImage();
            if (leftMarker != -1 && currentImage == leftMarker)
                stopAnimation();
            if (rightMarker != -1 && currentImage == rightMarker)
                stopAnimation();
        } else {
            stopAnimation();
        }
    }
}

void MainWindow::on_actionRefresh_triggered()
{
    dirMan->refreshDirectory();
    setNumberOfImages(dirMan->getNumberOfImages());
    currentImage = dirMan->getCurrentImageIndex();
    displayCurrentImage();
}

void MainWindow::on_setLeftMarker()
{
    if (leftMarker == currentImage)
        leftMarker = -1;
    else
        leftMarker = currentImage;
    ui->horizontalSlider->setLeftMarker(leftMarker);
}
void MainWindow::on_setRightMarker()
{
    if (rightMarker == currentImage)
        rightMarker = -1;
    else
        rightMarker = currentImage;
    ui->horizontalSlider->setRightMarker(rightMarker);
}
