#include "MainWindow.h"
#include <QVBoxLayout>
#include <QDebug>
#include <QDragEnterEvent>
#include <QUrl>
#include <QList>
#include <QMessageBox>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPainter>
#include <QPainterPath>
#include <QKeyEvent>
#include <QClipboard>
#include <QApplication>
#include <QBuffer>
#include <QDrag>
#include <QMimeData>
#include <QTemporaryFile>
#include <QDir>
#include <QFileDialog>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QEventLoop>
#include <QNetworkReply>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , mView(new GraphicsView(&mScene, this))
    , mForeground(":/images/tokentop.png")
    , mValidFinalImage(false)
    , mScale(1.0)
    , mScaleChanged(true)
    , mOffsetX(0)
    , mOffsetY(0)
    , mTimer(new QTimer(this))
{
    setupUi();
    connect(mTimer, SIGNAL(timeout()), this, SLOT(regenerateSmooth()));
    mTimer->setSingleShot(true);
    mTimer->setInterval(100);
    regenerate();
}

MainWindow::~MainWindow()
{}

void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    // Only accept drop if file.
    if(e->mimeData()->hasUrls())
    {
        e->acceptProposedAction();
    }
}

void MainWindow::dropEvent(QDropEvent *e)
{
    QMessageBox msg;

    // If drop contains more than one file, reject.
    if(e->mimeData()->urls().length() != 1)
    {
        msg.setText("Please drag in only one file.");
        msg.setIcon(QMessageBox::Critical);
        msg.exec();
        return;
    }

    // Attempt opening image.
    QString fileName = e->mimeData()->urls().at(0).toLocalFile();
    if(e->source())
        return;
    QImage image = QImage(fileName);

    // If it's an online resource.
    if(fileName.isNull())
    {
        fileName = e->mimeData()->urls().at(0).toString();
        image = download(e->mimeData()->urls().at(0));
    }

    // Loading failed.
    if(image.isNull())
    {
        msg.setText("Loading image failed.");
        msg.setInformativeText(fileName + " is not an image or not possible to open.");
        msg.setIcon(QMessageBox::Critical);
        msg.exec();
        return;
    }
    else
    {
        mFileName = fileName.split("/").last();
        QStringList intermediate = mFileName.split(".");
        intermediate.removeLast();
        mFileName = intermediate.join(".");
        updateTitle(mFileName);
        mImage = image;
        reset();
        regenerate();
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
    // Offset
    if(e->buttons() & Qt::RightButton || ((e->buttons() & Qt::LeftButton) && e->modifiers().testFlag(Qt::ShiftModifier)))
    {
        long diffX = (mStartDrag.x() - e->pos().x());
        long diffY = (mStartDrag.y() - e->pos().y());
        mOffsetX += diffX;
        mOffsetY += diffY;
        mStartDrag = e->pos();
        regenerate();
    }
}

void MainWindow::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::RightButton || (e->button() == Qt::LeftButton && e->modifiers().testFlag(Qt::ShiftModifier)))
    {
        mStartDrag = e->pos();
    }

    if(e->button() == Qt::LeftButton && !e->modifiers().testFlag(Qt::ShiftModifier) && mValidFinalImage && !mFileName.isNull())
    {
        QString path = QDir::tempPath() + "/" + mFileName + " - token.png";
        QFile file(path);
        if(file.open(QIODevice::WriteOnly))
        {
            QUrl url = QUrl::fromLocalFile(file.fileName());
            file.close();
            mFinalImage.save(file.fileName(), "PNG");

            QDrag *drag = new QDrag(this);
            QMimeData *mimeData = new QMimeData();
            mimeData->setUrls(QList<QUrl>() << url);
            drag->setMimeData(mimeData);
            drag->setPixmap(mIcon.scaled(70, 70, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
            drag->exec(Qt::CopyAction);
        }
    }
}

void MainWindow::wheelEvent(QWheelEvent *e)
{
    double modifier = e->modifiers().testFlag(Qt::ShiftModifier) ? 0.05 : 0.005;
    if(e->delta() > 0)
    {
        mScale += modifier;
        mScaleChanged = true;
        regenerate();
    }
    else
    {
        mScale -= modifier;
        mScaleChanged = true;
        regenerate();
    }
}

void MainWindow::keyPressEvent(QKeyEvent *e)
{
    bool ctrl = e->modifiers().testFlag(Qt::ControlModifier);
    if(e->key() == Qt::Key_Left)
    {
        mOffsetX += 3;
        regenerate();
    }

    if(e->key() == Qt::Key_Right)
    {
        mOffsetX -= 3;
        regenerate();
    }

    if(e->key() == Qt::Key_Up)
    {
        mOffsetY += 3;
        regenerate();
    }

    if(e->key() == Qt::Key_Down)
    {
        mOffsetY -= 3;
        regenerate();
    }

    if(e->key() == Qt::Key_Equal)
    {
        mScale += 0.01;
        mScaleChanged = true;
        regenerate();
    }

    if(e->key() == Qt::Key_Minus)
    {
        mScale -= 0.01;
        mScaleChanged = true;
        regenerate();
    }

    if(ctrl && e->key() == Qt::Key_V)
    {
        QClipboard *clip = QApplication::clipboard();
        if(!clip->image().isNull())
        {
            mImage = clip->image();
            mFileName = "Clipboard";
            updateTitle(mFileName);
            reset();
            regenerate();
            return;
        }

        if(clip->mimeData()->hasUrls())
        {
            QString fileName = clip->mimeData()->urls().at(0).toLocalFile();
            QImage image = QImage(fileName);

            // Loading failed.
            if(!image.isNull())
            {
                mFileName = fileName.split("/").last();
                QStringList intermediate = mFileName.split(".");
                intermediate.removeLast();
                mFileName = intermediate.join(".");
                mImage = image;
                reset();
                regenerate();
            }
        }

        // Maybe URL?
        if(clip->mimeData()->hasText())
        {
            QUrl url(clip->text());
            QImage image = download(url);

            if(!image.isNull())
            {
                mFileName = url.toString().split("/").last();
                QStringList intermediate = mFileName.split(".");
                intermediate.removeLast();
                mFileName = intermediate.join(".");
                qDebug() << mFileName;
                mImage = image;
                reset();
                regenerate();
            }
        }
    }

    if(e->key() == Qt::Key_F1)
    {
        // Help
        QMessageBox msg;
        msg.setWindowTitle("Help");
        msg.setText("Usage:");
        msg.setInformativeText("1. Drag in or paste (ctrl+V) in an image.\n2. Right click and drag to move and scroll to zoom (hold shift to zoom faster.)\n3. Ctrl+S to save or simply drag out of the application.");
        msg.setIcon(QMessageBox::Information);
        msg.exec();
    }

    if(ctrl && e->key() == Qt::Key_S && mValidFinalImage && !mFileName.isNull())
    {
        QString filename = QFileDialog::getSaveFileName(this, "Save Image", mFileName + " - token.png", "PNG Files (*.png)");
        if(!filename.isEmpty())
        {
            if(!filename.endsWith(".png", Qt::CaseInsensitive))
            {
                filename = filename + ".png";
            }
            mFinalImage.save(filename, "PNG");
        }
    }

    if(e->key() == Qt::Key_Q && mValidFinalImage && !mFileName.isNull())
    {
        if(!QDir("quicksave").exists())
        {
            QDir().mkdir("quicksave");
        }
        mFinalImage.save("quicksave/" + mFileName + ".png", "PNG");
    }

    if(e->key() == Qt::Key_Escape)
    {
        QApplication::quit();
    }
}

void MainWindow::setupUi()
{
    this->setAcceptDrops(true);
    this->setMinimumSize(420, 420);
    this->setMaximumSize(420, 420);
    this->setMouseTracking(true);
    this->setWindowTitle("Token Creator");
    this->setWindowIcon(QIcon(":/images/icon.png"));
    mView->setBackgroundBrush(Qt::gray);
    mView->show();
    mView->setDragMode(QGraphicsView::NoDrag);
    mView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    mView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->setCentralWidget(mView);

    // Attempt to load custom border.
    QImage customBorder("border.png");
    if(!customBorder.isNull())
    {
         if(customBorder.size() != QSize(420, 420))
         {
             QMessageBox msg;
             msg.setText("Custom border must be exactly 420x420.");
             msg.setIcon(QMessageBox::Critical);
             msg.exec();
         }
         else
         {
             mForeground = customBorder;
         }
    }
}

void MainWindow::regenerate(bool smooth)
{
    if(mTimer->isActive())
    {
        mTimer->stop();
    }
    mScene.clear();

    // IMAGE PROCESSING
    // Create white background.
    QImage bg(420, 420, QImage::Format_ARGB32);
    QImage fg(420, 420, QImage::Format_ARGB32);
    mFinalImage = QImage(420, 420, QImage::Format_ARGB32);
    bg.fill(Qt::white);
    fg.fill(Qt::white);
    mFinalImage.fill(Qt::white);

    // Process dragged image.
    if(!mImage.isNull())
    {
        if(mScaleChanged)
        {
            mCached = mImage.convertToFormat(QImage::Format_ARGB32);
            if(smooth)
            {
                mCached = mCached.scaled(mImage.width() * mScale, mImage.height() * mScale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
                mScaleChanged = false;
            }
            else
            {
                mCached = mCached.scaled(mImage.width() * mScale, mImage.height() * mScale);
            }
        }
        fg = mCached.copy(mOffsetX, mOffsetY, 420, 420);
    }

    // Composite.
    QPainter p(&mFinalImage);
    p.setRenderHint(QPainter::Antialiasing);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    p.fillRect(mFinalImage.rect(), Qt::transparent);
    QPainterPath path;
    path.addEllipse(7, 7, 406, 406);
    p.setClipPath(path);
    p.setCompositionMode(QPainter::CompositionMode_SourceOver);
    p.drawImage(0, 0, bg);
    p.drawImage(0, 0, fg);
    path = QPainterPath();
    path.addEllipse(6, 6, 408, 408);
    p.setClipPath(path);
    p.drawImage(0, 0, mForeground);
    p.end();

    QPixmap pixmap = QPixmap::fromImage(mFinalImage);
    if(!pixmap.isNull())
    {
        mIcon = pixmap;
        mScene.addPixmap(pixmap);
    }

    mValidFinalImage = smooth;
    if(!smooth)
    {
        mTimer->start();
    }
}

void MainWindow::reset()
{
    // Sane defaults.
    mOffsetX = 0;
    mOffsetY = 0;
    mScaleChanged = true;
    mScale = 1.0;

    // Scale smallest axis.
    if(mImage.width() < mImage.height())
    {
        mScale = 420.0 / mImage.width();
    }
    else
    {
        mScale = 420.0 / mImage.height();
    }
}

void MainWindow::updateTitle(QString title)
{
    this->setWindowTitle(title);
}

QImage MainWindow::download(QUrl url)
{
    QNetworkRequest r(url);
    QNetworkAccessManager m;
    QNetworkReply *reply = m.get(r);
    QEventLoop loop;
    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();
    QTemporaryFile file;
    if(!file.open())
    {
        return QImage();
    }
    const QByteArray data = reply->readAll();
    file.write(data);
    file.close();
    return QImage(file.fileName());
}

void MainWindow::regenerateSmooth()
{
    regenerate(true);
}
