/*
 *   This file is part of Dianara
 *   Copyright 2012-2016  JanKusanagi JRR <jancoding@gmx.com>
 *
 *   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 "imageviewer.h"


ImageViewer::ImageViewer(QString url, QSize imageSize,
                         QString title, QString suggestedFN,
                         bool isAnimated,
                         QWidget *parent) : QWidget(parent)
{
    if (title.isEmpty())
    {
        title = "<" + tr("Untitled") + ">";
    }
    this->setWindowTitle(tr("Image") + ": " + title + " - Dianara");
    this->setWindowIcon(QIcon::fromTheme("folder-image",
                                         QIcon(":/images/attached-image.png")));
    this->setWindowFlags(Qt::Window);
    this->setMinimumSize(640, 320);

    this->imageUrl = url;

    this->suggestedFilename = suggestedFN;
    this->imageIsAnimated = isAnimated;

    zoomLevel = 1.0;
    fitToWindow = true;


    imageLabel = new QLabel(this);
    imageLabel->setAlignment(Qt::AlignCenter);

    scrollArea = new QScrollArea(this);
    scrollArea->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
    scrollArea->setWidget(imageLabel);
    scrollArea->setWidgetResizable(true);
    scrollArea->setAlignment(Qt::AlignCenter);



    // Buttons
    saveImageButton = new QPushButton(QIcon::fromTheme("document-save-as",
                                                       QIcon(":/images/button-save.png")),
                                      tr("&Save As..."),
                                      this);
    saveImageButton->setDisabled(true);
    connect(saveImageButton, SIGNAL(clicked()),
            this, SLOT(saveImage()));


    if (imageIsAnimated)
    {
        this->movie = new QMovie(this);

        restartButton = new QPushButton(QIcon::fromTheme("view-refresh",
                                                         QIcon(":/images/menu-refresh.png")),
                                        tr("&Restart",
                                           "Restart animation"),
                                        this);
        restartButton->setDisabled(true);
        connect(restartButton, SIGNAL(clicked()),
                this, SLOT(restartAnimation()));
    }


    fitButton = new QPushButton(QIcon::fromTheme("zoom-fit-best"),
                                tr("Fit",
                                   "As in: fit image to window"),
                                this);
    connect(fitButton, SIGNAL(clicked()),
            this, SLOT(zoomToFit()));

    fullButton = new QPushButton(QIcon::fromTheme("zoom-original"),
                                 "100%",
                                 this);
    connect(fullButton, SIGNAL(clicked()),
            this, SLOT(zoomToFit()));


    zoomInButton = new QPushButton(QIcon::fromTheme("zoom-in"),
                                   "+",
                                   this);
    zoomInButton->setShortcut(QKeySequence(Qt::Key_Plus));
    zoomInButton->setDisabled(true);
    connect(zoomInButton, SIGNAL(clicked()),
            this, SLOT(zoomIn()));


    zoomOutButton = new QPushButton(QIcon::fromTheme("zoom-out"),
                                    "-",
                                    this);
    zoomOutButton->setShortcut(QKeySequence(Qt::Key_Minus));
    zoomOutButton->setDisabled(true);
    connect(zoomOutButton, SIGNAL(clicked()),
            this, SLOT(zoomOut()));


    infoLabel = new QLabel(this);
    infoLabel->setAlignment(Qt::AlignCenter);


    closeButton = new QPushButton(QIcon::fromTheme("window-close",
                                                   QIcon(":/images/button-close.png")),
                                  tr("&Close"),
                                  this);
    connect(closeButton, SIGNAL(clicked()),
            this, SLOT(close()));


    this->createContextMenu();


    // Layout
    fitFullLayout = new QHBoxLayout();
    fitFullLayout->setContentsMargins(0, 0, 0, 0);
    fitFullLayout->setSpacing(0);
    fitFullLayout->addWidget(fitButton);
    fitFullLayout->addWidget(fullButton);

    buttonsLayout = new QHBoxLayout();
    buttonsLayout->addWidget(saveImageButton);
    if (imageIsAnimated)
    {
        buttonsLayout->addWidget(restartButton);
    }
    buttonsLayout->addLayout(fitFullLayout);
    buttonsLayout->addWidget(zoomInButton);
    buttonsLayout->addWidget(zoomOutButton);
    buttonsLayout->addWidget(infoLabel, 1);
    buttonsLayout->addWidget(closeButton);

    mainLayout = new QVBoxLayout();
    mainLayout->addWidget(scrollArea);
    mainLayout->addLayout(buttonsLayout);
    this->setLayout(mainLayout);


    this->updateButtons();

    // Ensure these are disabled until image is actually loaded
    this->fitButton->setDisabled(true);
    this->fullButton->setDisabled(true);

    this->loadImage(this->imageUrl, imageSize);


    // Set initial window size according to image size and desktop (screen) size
    QDesktopWidget desktopWidget;
    int desktopWidth = desktopWidget.availableGeometry().width();
    int desktopHeight = desktopWidget.availableGeometry().height();

    int imageWidth = imageSize.width();
    int imageHeight = imageSize.height();
    // If image is already loaded, use its actual w/h values, just in case
    if (!originalPixmap.isNull())
    {
        imageWidth = originalPixmap.width();
        imageHeight = originalPixmap.height();
    }

    int windowWidth = qMin(imageWidth + 100, // Some margin for the window itself
                           desktopWidth);
    int windowHeight = qMin(imageHeight + 100,
                            desktopHeight);

    // Resize window only if image is big enough, otherwise leave resizing up to the layout
    if (imageWidth > (desktopWidth / 3)      // Kinda TMP...
     || imageHeight > (desktopHeight / 3))
    {
        this->resize(windowWidth, windowHeight);
    }

    qDebug() << "ImageViewer created";
}


ImageViewer::~ImageViewer()
{
    qDebug() << "ImageViewer destroyed";
}



void ImageViewer::createContextMenu()
{
    this->saveAction = new QAction(QIcon::fromTheme("document-save-as",
                                                    QIcon(":/images/button-save.png")),
                                   tr("Save Image..."),
                                   this);
    saveAction->setShortcut(QKeySequence("Ctrl+S"));
    saveAction->setDisabled(true);
    connect(saveAction, SIGNAL(triggered()),
            this, SLOT(saveImage()));

    this->closeAction = new QAction(QIcon::fromTheme("window-close",
                                                     QIcon(":/images/button-close.png")),
                                    tr("Close Viewer"),
                                    this);
    closeAction->setShortcut(QKeySequence(Qt::Key_Escape));
    connect(closeAction, SIGNAL(triggered()),
            this, SLOT(close()));


    this->contextMenu = new QMenu("imageViewerMenu", this);
    contextMenu->addAction(saveAction);
    contextMenu->addAction(closeAction);

    // Make the shortcuts work
    this->addAction(saveAction);
    this->addAction(closeAction);
}


void ImageViewer::loadImage(QString url, QSize expectedSize)
{
    this->originalFileURI = MiscHelpers::getCachedImageFilename(url);

    originalPixmap = QPixmap(originalFileURI);
    if (originalPixmap.isNull())
    {
        QString expectedResolution = MiscHelpers::resolutionString(expectedSize.width(),
                                                                   expectedSize.height());

        this->imageLabel->setText("<img src=\":/images/image-loading.png\" />"
                                  "<br /><br /><br />"
                                  "<big>" + tr("Downloading full image...")
                                  + "</big>"
                                    "<br /><br />"
                                    "(" + expectedResolution + ")");
    }
    else
    {
        QString resolution = MiscHelpers::resolutionString(originalPixmap.width(),
                                                           originalPixmap.height());

        QString imageDetails = resolution + "  ~  "
                               + MiscHelpers::fileSizeString(originalFileURI);

        this->infoLabel->setText(imageDetails);

        if (imageIsAnimated)
        {
            movie->setFileName(this->originalFileURI);
            movie->start();

            imageLabel->setMovie(movie);

            restartButton->setEnabled(true);
        }


        this->drawImage();

        this->saveImageButton->setEnabled(true);
        this->saveAction->setEnabled(true);
    }
}



void ImageViewer::drawImage()
{
    toggleZoomButtons();

    int width;
    int height;

    if (fitToWindow)
    {
        width = qMin(scrollArea->width() - 8,   // 8px as margin
                     originalPixmap.width());
        height = qMin(scrollArea->height() - 8,
                      originalPixmap.height());
    }
    else
    {
        width = originalPixmap.width() * zoomLevel;
        height = originalPixmap.height() * zoomLevel;
    }


    if (!this->imageIsAnimated)
    {
        imageLabel->setPixmap(originalPixmap.scaled(width, height,
                                                    Qt::KeepAspectRatio,
                                                    Qt::SmoothTransformation));
    }
    else
    {
        QSize movieSize(originalPixmap.width(),
                        originalPixmap.height());
        movieSize.scale(width, height, Qt::KeepAspectRatio);
        movie->setScaledSize(movieSize);
    }

    this->updateButtons();
}


void ImageViewer::toggleZoomButtons()
{
    this->zoomInButton->setDisabled(this->zoomLevel == 5.0);
    this->zoomOutButton->setDisabled(this->zoomLevel < 0.1);
}


void ImageViewer::updateButtons()
{
    if (fitToWindow)
    {
        this->fitButton->setDisabled(true);
        this->fullButton->setEnabled(true);

        fullButton->setShortcut(QKeySequence(Qt::Key_F));

        this->zoomInButton->setVisible(false);
        this->zoomOutButton->setVisible(false);
    }
    else
    {
        this->fullButton->setDisabled(true);
        this->fitButton->setEnabled(true);

        fitButton->setShortcut(QKeySequence(Qt::Key_F));

        this->zoomInButton->setVisible(true);
        this->zoomOutButton->setVisible(true);
    }
}



/****************************************************************************/
/************************************ SLOTS *********************************/
/****************************************************************************/



void ImageViewer::reloadImage(QString url)
{
    if (url == this->imageUrl)
    {
        this->loadImage(url, QSize());
    }
}

void ImageViewer::onImageFailed(QString url)
{
    if (url == this->imageUrl)
    {
        this->imageLabel->setText("<img src=\":/images/image-missing.png\" />"
                                  "<br /><br /><br />"
                                  "<big>" + tr("Error downloading image!")
                                  + "<br /><br />"
                                  + tr("Try again later.")
                                  + "</big>");
    }
}


void ImageViewer::saveImage()
{
    bool savedCorrectly;

    QString filename;
    filename = QFileDialog::getSaveFileName(this, tr("Save Image As..."),
                                            QDir::homePath() + "/" + suggestedFilename,
                                            tr("Image files") + " (*.jpg *.png);;"
                                            + tr("All files") + " (*)");

    if (!filename.isEmpty())
    {
        // Save pixmap from original file
        savedCorrectly = QPixmap(this->originalFileURI).save(filename);

        // FIXME: change this to directly copy the unmodified original instead
        if (!savedCorrectly)
        {
            QMessageBox::warning(this,
                                 tr("Error saving image"),
                                 tr("There was a problem while saving %1.\n\n"
                                    "Filename should end in .jpg "
                                    "or .png extensions.").arg(filename));
        }
    }
}


void ImageViewer::restartAnimation()
{
    if (imageIsAnimated)
    {
        this->movie->stop();
        this->movie->start();
    }
}


void ImageViewer::zoomToFit()
{
    this->fitToWindow = !fitToWindow;
    this->zoomLevel = 1.0; // Reset

    this->drawImage();
}


void ImageViewer::zoomIn()
{
    this->fitToWindow = false;

    if (zoomLevel > 1)
    {
        this->zoomLevel += 0.5;
        if (zoomLevel > 5)
        {
            zoomLevel = 5.0;
        }
    }
    else
    {
        this->zoomLevel += 0.1;
    }

    this->drawImage();
}



void ImageViewer::zoomOut()
{
    this->fitToWindow = false;

    if (zoomLevel > 1)
    {
        this->zoomLevel -= 0.5;
    }
    else
    {
        this->zoomLevel -= 0.1;
        if (zoomLevel < 0.1)
        {
            zoomLevel = 0.05;
        }
    }

    this->drawImage();
}



/****************************************************************************/
/********************************** PROTECTED *******************************/
/****************************************************************************/


void ImageViewer::closeEvent(QCloseEvent *event)
{
    qDebug() << "ImageViewer::closeEvent(); hiding and destroying widget!";
    event->ignore();

    this->hide();
    this->deleteLater();
}

void ImageViewer::hideEvent(QHideEvent *event)
{
    qDebug() << "ImageViewer::hideEvent()";
    event->accept();
}


void ImageViewer::resizeEvent(QResizeEvent *event)
{
    qDebug() << "ImageViewer::resizeEvent()";

    if (!this->originalPixmap.isNull())
    {
        this->drawImage();
    }

    event->accept();
}



void ImageViewer::contextMenuEvent(QContextMenuEvent *event)
{
    this->contextMenu->exec(event->globalPos());
}
