/*
    This file is part of Photoflare.

    Photoflare 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 3 of the License, or
    (at your option) any later version.

    Photoflare 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 Photoflare.  If not, see <https://www.gnu.org/licenses/>.
*/

// Batch dialog class for the Photoflare application.

//#include <QDebug>
#include <QFileDialog>
#include <QColorDialog>
#include <QMessageBox>
#include <QMimeDatabase>
#include <QMimeData>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QSettings>

#include "batchdialog.h"
#include "ui_batchdialog.h"
#include "../Settings.h"

enum {Pixels, Percent};

class BatchDialogPrivate
{
public:
    QStringList fileList;
    QStringList filterList;
    QString openDir;
    QString outDir;
    QString outFormat;
    double imageRatio;
};

batchDialog::batchDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::batchDialog),
    d(new BatchDialogPrivate)
{
    ui->setupUi(this);
    setFixedSize(size());

    QStringList sourceFormats;
    sourceFormats << tr("All formats");
    sourceFormats << ".png";
    sourceFormats << ".jpg";
    sourceFormats << ".gif";

    QStringList outputformats;
    outputformats << ".png";
    outputformats << ".jpg";
    outputformats << ".gif";

    ui->sourceFormat->addItems(sourceFormats);
    ui->outFormat->addItems(outputformats);

    QStringList filters;
    filters << tr("Oil");
    filters << tr("Charcoal");
    filters << tr("Swirl");
    filters << tr("Solarize");
    filters << tr("Wave");
    filters << tr("Implode");
    filters << tr("Explode");
    filters << tr("Soften");
    filters << tr("Blur");
    filters << tr("Motion Blur");
    filters << tr("Sharpen");
    filters << tr("Reinforce");
    filters << tr("Emboss");
    filters << tr("Sketch");
    filters << tr("Grayscale");
    filters << tr("Black and white");
    filters << tr("Old Photo");
    filters << tr("Sepia");
    filters << tr("Negative");
    filters << tr("Simple Frame");
    filters << tr("3D Frame");
    filters << tr("Normalize");
    filters << tr("Auto levels");
    filters << tr("Auto contrast");
    filters << tr("Crop to center");
    filters << tr("Equalize colours");
    filters << tr("Monochrome edges");
    filters << tr("Pixelate");
    filters << tr("Vignette");
    filters << tr("Posterize");
    filters << tr("Pixel Scatter");
    filters << tr("Gaussian noise");
    filters << tr("Impulse noise");
    filters << tr("Laplacian noise");
    filters << tr("Poisson noise");
    filters << tr("Dust Reduction");
    filters << tr("Despeckle");
    filters << tr("Trim");
    filters << tr("Drop shadow");
    filters << tr("Opacity");
    ui->listWidget_2->addItems(filters);

    for(int i=Qt::white; i <= Qt::yellow; i++)
    {
        QPixmap pixmap(QSize(ui->backgroundColorComboBox->width(),ui->backgroundColorComboBox->height()));
        pixmap.fill(static_cast<Qt::GlobalColor>(i));
        ui->backgroundColorComboBox->addItem(QString(), pixmap);
    }

    ui->backgroundColorComboBox->setOnClickHandler(this);
    ui->listWidget->setIconSize(QSize(64,64));
    setAcceptDrops(true);

    if(SETTINGS->getMemParamsEnabled() == true)
    {
        readSettings(this);
    }
    else
    {
       d->openDir = QString();
    }
}

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

void batchDialog::done(int r)
{
    if(QDialog::Accepted == r)
    {
        if(ui->listWidget->count() > 0 && ui->outputDir->text().size() > 0)
        {
            if(SETTINGS->getMemParamsEnabled() == true)
            {
                QDir dir = QFileInfo(d->fileList[0]).absoluteDir();
                d->openDir = dir.absolutePath();
                writeSettings(this);
            }

            QDialog::done(r);
            return;
        }
        else if(ui->listWidget->count() == 0)
        {
            QMessageBox msgBox;
            msgBox.setIcon(QMessageBox::Warning);
            msgBox.setWindowTitle(tr("Batch files required"));
            msgBox.setText(tr("Please add files to the batch list."));
            msgBox.exec();
            return;
        }
        else if(ui->outputDir->text().size() == 0)
        {
            QMessageBox msgBox;
            msgBox.setIcon(QMessageBox::Warning);
            msgBox.setWindowTitle(tr("Output directory required"));
            msgBox.setText(tr("Please set the output folder."));
            msgBox.exec();
            return;
        }
    }
    else
    {
        QDialog::done(r);
        return;
    }
}

void batchDialog::on_listWidget_2_doubleClicked(const QModelIndex &index)
{
    if(ui->listWidget_2->item(index.row()))
    {
        QListWidgetItem *item = ui->listWidget_2->item(index.row());
        ui->listWidget_3->addItem(item->text());
        d->filterList.append(item->text());
        ui->listWidget_2->removeItemWidget(item);
        delete item;
    }
}

void batchDialog::on_listWidget_3_doubleClicked(const QModelIndex &index)
{
    if(ui->listWidget_3->item(index.row()))
    {
        QListWidgetItem *item = ui->listWidget_3->item(index.row());
        ui->listWidget_2->addItem(item->text());
        ui->listWidget_3->removeItemWidget(item);
        d->filterList.removeOne(item->text());
        delete item;
    }
}

QStringList batchDialog::fileList()
{
    return d->fileList;
}

QStringList batchDialog::filterList()
{
    return d->filterList;
}

QString batchDialog::outDir()
{
    return d->outDir;
}

QString batchDialog::outputFormat()
{
    return d->outFormat;
}

void batchDialog::on_tabWidget_currentChanged(int index)
{
    if(index == 5)
    {
        ui->plainTextEdit->clear();

        // --- Input files ---
        ui->plainTextEdit->insertPlainText(
            tr("Input files (%1):").arg(d->fileList.size()) + "\n");
        foreach (QString file, d->fileList) {
            ui->plainTextEdit->insertPlainText("  " + file + "\n");
        }
        ui->plainTextEdit->insertPlainText("\n");

        // --- Output ---
        ui->plainTextEdit->insertPlainText(tr("Output") + "\n");
        ui->plainTextEdit->insertPlainText("  " + tr("Folder:").leftJustified(22) + d->outDir + "\n");
        ui->plainTextEdit->insertPlainText("  " + tr("Format:").leftJustified(22) + ui->outFormat->currentText() + "\n");
        if(ui->sourceFormat->currentIndex() != 0)
        {
            ui->plainTextEdit->insertPlainText("  " + tr("Source filter:").leftJustified(22) + ui->sourceFormat->currentText() + "\n");
        }
        ui->plainTextEdit->insertPlainText("\n");

        // --- Resize ---
        if(ui->imageSizeCheckBox->isChecked() || ui->canvasSizeCheckBox->isChecked())
        {
            ui->plainTextEdit->insertPlainText(tr("Resize") + "\n");
            if(ui->imageSizeCheckBox->isChecked())
            {
                QString size = QString::number(ui->imageWvalue->value()) + " x "
                               + QString::number(ui->imageHvalue->value())
                               + " " + ui->imageWHcombo->currentText();
                ui->plainTextEdit->insertPlainText("  " + tr("Image size:").leftJustified(22) + size + "\n");
            }
            if(ui->canvasSizeCheckBox->isChecked())
            {
                QString size = QString::number(ui->canvasWvalue->value()) + " x "
                               + QString::number(ui->canvasHvalue->value());
                ui->plainTextEdit->insertPlainText("  " + tr("Canvas size:").leftJustified(22) + size + "\n");
                ui->plainTextEdit->insertPlainText("  " + tr("Background color:").leftJustified(22) + backgroundColor().name() + "\n");
                const QStringList positionNames = {
                    tr("Top Left"),    tr("Top Center"),    tr("Top Right"),
                    tr("Center Left"), tr("Center"),        tr("Center Right"),
                    tr("Bottom Left"), tr("Bottom Center"), tr("Bottom Right")
                };
                int pos = static_cast<int>(ui->positionWidget->imagePosition());
                QString posName = (pos >= 0 && pos < positionNames.size()) ? positionNames.at(pos) : QString::number(pos);
                ui->plainTextEdit->insertPlainText("  " + tr("Image position:").leftJustified(22) + posName + "\n");
            }
            ui->plainTextEdit->insertPlainText("\n");
        }

        // --- Adjustments ---
        bool hasAdjustments = ui->brightnessSlider->value() != 0
                              || ui->contrastSlider->value() != 0
                              || ui->saturationSlider->value() != 0
                              || ui->gammaSlider->value() != 100;
        if(hasAdjustments)
        {
            ui->plainTextEdit->insertPlainText(tr("Adjustments") + "\n");
            if(ui->brightnessSlider->value() != 0)
            {
                ui->plainTextEdit->insertPlainText("  " + tr("Brightness:").leftJustified(22)
                    + QString::number(ui->brightnessSlider->value())
                    + " (" + ui->brightnessChannel->currentText() + ")\n");
            }
            if(ui->contrastSlider->value() != 0)
            {
                ui->plainTextEdit->insertPlainText("  " + tr("Contrast:").leftJustified(22)
                    + QString::number(ui->contrastSlider->value())
                    + " (" + ui->contrastChannel->currentText() + ")\n");
            }
            if(ui->saturationSlider->value() != 0)
            {
                ui->plainTextEdit->insertPlainText("  " + tr("Saturation:").leftJustified(22)
                    + QString::number(ui->saturationSlider->value())
                    + " (" + ui->SaturationChannel->currentText() + ")\n");
            }
            if(ui->gammaSlider->value() != 100)
            {
                ui->plainTextEdit->insertPlainText("  " + tr("Gamma:").leftJustified(22)
                    + QString::number(ui->gammaSlider->value())
                    + " (" + ui->gammaChannel->currentText() + ")\n");
            }
            ui->plainTextEdit->insertPlainText("\n");
        }

        // --- Transform ---
        if(ui->rotateCheckBox->isChecked() || ui->flipCheckBox->isChecked())
        {
            ui->plainTextEdit->insertPlainText(tr("Transform") + "\n");
            if(ui->rotateCheckBox->isChecked())
            {
                QString rotateAmount;
                if(ui->rotate90radioButton->isChecked())
                    rotateAmount = ui->rotate90radioButton->text();
                else if(ui->rotate90ccwRadioButton->isChecked())
                    rotateAmount = ui->rotate90ccwRadioButton->text();
                else if(ui->rotate180RadioButton->isChecked())
                    rotateAmount = ui->rotate180RadioButton->text();
                ui->plainTextEdit->insertPlainText("  " + tr("Rotate:").leftJustified(22) + rotateAmount + "\n");
            }
            if(ui->flipCheckBox->isChecked())
            {
                QString flipDirection = ui->flipHorizontalRadioButton->isChecked()
                    ? ui->flipHorizontalRadioButton->text()
                    : ui->flipVerticalRadioButton->text();
                ui->plainTextEdit->insertPlainText("  " + tr("Flip:").leftJustified(22) + flipDirection + "\n");
            }
            ui->plainTextEdit->insertPlainText("\n");
        }

        // --- Filters ---
        ui->plainTextEdit->insertPlainText(tr("Filters (%1)").arg(d->filterList.size()) + "\n");
        foreach (QString filter, d->filterList) {
            ui->plainTextEdit->insertPlainText("  " + filter + "\n");
        }
    }
}

void batchDialog::onFinished()
{
    ui->tabWidget->setCurrentIndex(5);
    ui->plainTextEdit->insertPlainText("\n");
    ui->plainTextEdit->insertPlainText(tr("Batch processing finished successfully !"));
}

void batchDialog::on_imageSizeCheckBox_clicked(bool checked)
{
    ui->imageWidth->setEnabled(checked);
    ui->imageHeight->setEnabled(checked);
    ui->imageUnit->setEnabled(checked);
    ui->aspectRatioCheckBox->setEnabled(checked);
    ui->imageWvalue->setEnabled(checked);
    ui->imageHvalue->setEnabled(checked);
    ui->imageWHcombo->setEnabled(checked);
}

void batchDialog::on_canvasSizeCheckBox_clicked(bool checked)
{
    ui->canvasWidth->setEnabled(checked);
    ui->canvasHeight->setEnabled(checked);
    ui->backgroundLabel->setEnabled(checked);
    ui->positionLabel->setEnabled(checked);
    ui->canvasWvalue->setEnabled(checked);
    ui->canvasHvalue->setEnabled(checked);
    ui->backgroundColorComboBox->setEnabled(checked);
    ui->positionWidget->setEnabled(checked);
}

void batchDialog::on_imageWHcombo_currentIndexChanged(int index)
{
    if(index == Pixels)
    {
        d->imageRatio = 800.0f / 600.0f;
        ui->imageWvalue->setValue(800);
        ui->imageHvalue->setValue(600);
    }
    else
    {
        d->imageRatio = 1;
        ui->imageWvalue->setValue(100);
        ui->imageHvalue->setValue(100);
    }
}

void batchDialog::on_imageWvalue_valueChanged(double value)
{
    if(ui->aspectRatioCheckBox->isChecked())
    {
        ui->imageHvalue->setValue(value / d->imageRatio);
    }
    else
    {
        d->imageRatio = value / ui->imageHvalue->value();
    }
}

void batchDialog::on_imageHvalue_valueChanged(double value)
{
    if(ui->aspectRatioCheckBox->isChecked())
    {
        ui->imageWvalue->setValue(value * d->imageRatio);
    }
    else
    {
        d->imageRatio = ui->imageWvalue->value() / value;
    }
}

void batchDialog::mousePressEvent(QComboBox* obj, QMouseEvent *e)
{
    Q_UNUSED(obj);
    if(!ui->backgroundColorComboBox->isVisible())
    {
        e->accept();
        return;
    }

    if(e->x() < ui->backgroundColorComboBox->width() - 20)
    {
        QColor selectedColor = QColorDialog::getColor(Qt::white, this);
        if (selectedColor.isValid())
        {
            QPixmap pixmap(QSize(ui->backgroundColorComboBox->width(),ui->backgroundColorComboBox->height()));
            pixmap.fill(selectedColor);
            ui->backgroundColorComboBox->insertItem(0, QString(), pixmap);
            ui->backgroundColorComboBox->setCurrentIndex(0);
        }
        e->ignore();
    }
    else
    {
        e->accept();
    }
}

bool batchDialog::changeImageSize() const
{
    return ui->imageSizeCheckBox->isChecked();
}

bool batchDialog::changeCanvasSize() const
{
    return ui->canvasSizeCheckBox->isChecked();
}

QSize batchDialog::imageSize() const
{
    return QSize(ui->imageWvalue->value(), ui->imageHvalue->value());
}

char batchDialog::imageSizeUnits() const
{
    return ui->imageWHcombo->currentIndex();
}

QSize batchDialog::canvasSize() const
{
    return QSize(ui->canvasWvalue->value(), ui->canvasHvalue->value());
}

QColor batchDialog::backgroundColor() const
{
    QImage img = (QImage)ui->backgroundColorComboBox->currentData().value<QImage>();
    return img.pixel(0,0);
}

ImagePosition batchDialog::imagePosition() const
{
    return ui->positionWidget->imagePosition();
}

int batchDialog::brightness() const
{
    return ui->brightnessSlider->value();
}

int batchDialog::brightnessChannel() const
{
    return ui->brightnessChannel->currentIndex();
}

double batchDialog::gamma() const
{
    return (float) ui->gammaSlider->value() / (float)100;
}

int batchDialog::gammaChannel() const
{
    return ui->gammaChannel->currentIndex();
}

int batchDialog::contrast() const
{
    return ui->contrastSlider->value();
}

int batchDialog::contrastChannel() const
{
    return ui->contrastChannel->currentIndex();
}

int batchDialog::saturation() const
{
    return ui->saturationSlider->value();
}

int batchDialog::saturationChannel() const
{
    return ui->SaturationChannel->currentIndex();
}

void batchDialog::on_rotateCheckBox_clicked(bool checked)
{
    ui->rotate90radioButton->setEnabled(checked);
    ui->rotate90ccwRadioButton->setEnabled(checked);
    ui->rotate180RadioButton->setEnabled(checked);
}

void batchDialog::on_flipCheckBox_clicked(bool checked)
{
    ui->flipVerticalRadioButton->setEnabled(checked);
    ui->flipHorizontalRadioButton->setEnabled(checked);
}

char batchDialog::rotate() const
{
    if(ui->rotateCheckBox->isChecked() && ui->rotate90radioButton->isChecked())
        return Rotate90CW;
    else if(ui->rotateCheckBox->isChecked() && ui->rotate90ccwRadioButton->isChecked())
        return Rotate90CCW;
    else if(ui->rotateCheckBox->isChecked() && ui->rotate180RadioButton->isChecked())
        return Rotate180;
    else
        return 0;
}

char batchDialog::flip() const
{
    if(ui->flipCheckBox->isChecked() && ui->flipVerticalRadioButton->isChecked())
        return FlipVertical;
    else if(ui->flipCheckBox->isChecked() && ui->flipHorizontalRadioButton->isChecked())
        return FlipHorizontal;
    else
        return 0;
}

void batchDialog::on_addFilesButton_clicked()
{
    QStringList selected = QFileDialog::getOpenFileNames(this, tr("Select Files"), d->openDir, tr("Image Files (*.png *.jpg *.jpeg *.gif)"));

    if (selected.isEmpty())
        return;

    addFilesToList(selected);
}

void batchDialog::on_outFolderPushButton_clicked()
{
    d->outDir = QFileDialog::getExistingDirectory();
    ui->outputDir->setText(d->outDir);
}

void batchDialog::on_brightnessSlider_valueChanged(int value)
{
    ui->brightnessValue->setText(QString::number(value) + QString("%"));
}

void batchDialog::on_contrastSlider_valueChanged(int value)
{
    ui->contrastValue->setText(QString::number(value) + QString("%"));
}

void batchDialog::on_saturationSlider_valueChanged(int value)
{
    ui->saturationValue->setText(QString::number(value) + QString("%"));
}

void batchDialog::on_gammaSlider_valueChanged(int value)
{
    ui->gammaValue->setText(QString::number((float)value / (float)100, 'f', 2));
}

void batchDialog::writeSettings(QWidget* window)
{
    QSettings settings;

    settings.beginGroup(window->objectName());
    settings.setValue("pos", window->pos());
    settings.setValue("opendir", d->openDir);
    settings.setValue("outputdir", d->outDir);
    settings.endGroup();
}

void batchDialog::readSettings(QWidget* window)
{
    QSettings settings;

    settings.beginGroup(window->objectName());
    QVariant value = settings.value("pos");
    if (!value.isNull())
    {
        window->move(settings.value("pos").toPoint());
        d->openDir = settings.value("opendir").toString();
        d->outDir = settings.value("outputdir").toString();
        ui->outputDir->setText(d->outDir);
    }
    settings.endGroup();
}

void batchDialog::on_outFormat_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    d->outFormat = ui->outFormat->currentText();
}

void batchDialog::on_sourceFormat_currentIndexChanged(int index)
{
    ui->listWidget->clear();

    if(index == 0 && original_list.length()>0)
    {
        //Restore original list
        d->fileList = original_list;

        //Add to ListWidget to display the file list
        for(int i=0;i<original_list.length();i++)
        {
            addItemToFileListWidget(original_list[i]);
        }
    }
    else
    {
        if(original_list.length() == 0)
        {
            original_list = d->fileList;
        }
        else
        {
            d->fileList = original_list;
        }

        QString sourceExt = ui->sourceFormat->currentText();
        QString filterMIMEType = "";
        QStringList newFileList;

        if(sourceExt == ".png")
        {
            filterMIMEType = "image/png";
        }
        else if(sourceExt == ".jpg")
        {
            filterMIMEType = "image/jpeg";
        }
        else
        {
            filterMIMEType = "image/gif";
        }

        if(original_list.length()>0)
        {
            for(int i=0;i<original_list.length();i++)
            {
                QMimeDatabase db;
                QMimeType mime = db.mimeTypeForFile(original_list[i], QMimeDatabase::MatchContent);

                if(mime.name() == filterMIMEType)
                {
                    //Push to new array if correct file type
                    newFileList.append(original_list[i]);

                    //Add to ListWidget to display the file list
                    addItemToFileListWidget(original_list[i]);
                }
            }
            d->fileList = newFileList;
        }
    }
}

void batchDialog::addItemToFileListWidget(QString filePath)
{
    QListWidgetItem *itm = new QListWidgetItem(QFileInfo(filePath).fileName() + "\r\n" + filePath);
    itm->setSizeHint(QSize(64,70));
    // Load the image and set it as the icon for the list item,
    // scaling it to fit within a 64x64 pixel area while maintaining the aspect ratio
    QImage img(filePath);
    if (!img.isNull()) {
        itm->setIcon(QIcon(QPixmap::fromImage(img.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation))));
    }
    ui->listWidget->addItem(itm);
}

void batchDialog::on_addFilterButton_clicked()
{
    on_listWidget_2_doubleClicked(ui->listWidget_2->currentIndex());
}

void batchDialog::on_removeFilterButton_clicked()
{
    on_listWidget_3_doubleClicked(ui->listWidget_3->currentIndex());
}

void batchDialog::addFilesToList(const QStringList &files)
{
    QMimeDatabase db;
    static const QStringList validMimeTypes = { "image/png", "image/jpeg", "image/gif" };

    // Determine which list to check for duplicates: the full unfiltered list when
    // a source-format filter is active, otherwise the current file list.
    const QStringList *base = original_list.isEmpty() ? &d->fileList : &original_list;

    QStringList newFiles;
    for (const QString &file : files) {
        if (base->contains(file))
            continue;
        QMimeType mime = db.mimeTypeForFile(file, QMimeDatabase::MatchContent);
        if (!validMimeTypes.contains(mime.name()))
            continue;
        newFiles.append(file);
    }

    if (newFiles.isEmpty())
        return;

    if (!original_list.isEmpty()) {
        // A source-format filter is active. Append to the full list then re-apply
        // the filter so the widget stays consistent.
        original_list.append(newFiles);
        on_sourceFormat_currentIndexChanged(ui->sourceFormat->currentIndex());
    } else {
        for (const QString &file : newFiles) {
            d->fileList.append(file);
            addItemToFileListWidget(file);
        }
    }
}

void batchDialog::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasUrls())
        event->acceptProposedAction();
}

void batchDialog::dropEvent(QDropEvent *event)
{
    QStringList files;
    for (const QUrl &url : event->mimeData()->urls()) {
        if (url.isLocalFile())
            files.append(url.toLocalFile());
    }
    addFilesToList(files);
}
