/*
 * Copyright (c) 2020-2025, Ilya Kotov <forkotov02@ya.ru>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <QSettings>
#include <QDir>
#include <QInputDialog>
#include <QMessageBox>
#include <QFile>
#include <QMenu>
#include "qt6ct.h"
#include "qsseditordialog.h"
#include "qsspage.h"
#include "ui_qsspage.h"

#define QSS_FULL_PATH_ROLE (Qt::ItemDataRole(Qt::UserRole))
#define QSS_WRITABLE_ROLE (Qt::ItemDataRole(Qt::UserRole + 1))

QSSPage::QSSPage(QWidget *parent) :
    TabPage(parent),
    m_ui(new Ui::QSSPage)
{
    m_ui->setupUi(this);
    QDir::root().mkpath(Qt6CT::userStyleSheetPath());

    m_menu = new QMenu(this);
    m_menu->addAction(QIcon::fromTheme(u"accessories-text-editor"_s), tr("Edit"), this, &QSSPage::on_editButton_clicked);
    m_menu->addAction(QIcon::fromTheme(u"edit-copy"_s), tr("Create a Copy"), this, &QSSPage::copyStyleSheet);
    m_menu->addAction(tr("Rename"), this, &QSSPage::on_renameButton_clicked);
    m_menu->addSeparator();
    m_menu->addAction(QIcon::fromTheme(u"edit-delete"_s), tr("Remove"), this, &QSSPage::on_removeButton_clicked);

    readSettings();

    //icons
    m_ui->createButton->setIcon(QIcon::fromTheme(u"document-new"_s));
    m_ui->editButton->setIcon(QIcon::fromTheme(u"accessories-text-editor"_s));
    m_ui->removeButton->setIcon(QIcon::fromTheme(u"edit-delete"_s));
}

QSSPage::~QSSPage()
{
    delete m_ui;
}

void QSSPage::writeSettings(QSettings *settings)
{
    QStringList styleSheets;

    for(int i = 0; i < m_ui->qssListWidget->count(); ++i)
    {
        QListWidgetItem *item = m_ui->qssListWidget->item(i);
        if(item->checkState() == Qt::Checked)
            styleSheets << item->data(QSS_FULL_PATH_ROLE).toString();
    }

    settings->setValue(u"Interface/stylesheets"_s, styleSheets);
}

void QSSPage::on_qssListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *)
{
    m_ui->editButton->setText(tr("Edit"));
    if(current)
    {
        m_ui->editButton->setEnabled(true);
        if(!current->data(QSS_WRITABLE_ROLE).toBool())
            m_ui->editButton->setText(tr("View"));
        m_ui->removeButton->setEnabled(current->data(QSS_WRITABLE_ROLE).toBool());
        m_ui->renameButton->setEnabled(current->data(QSS_WRITABLE_ROLE).toBool());
    }
    else
    {
        m_ui->editButton->setEnabled(false);
        m_ui->removeButton->setEnabled(false);
        m_ui->renameButton->setEnabled(false);
    }
}

void QSSPage::on_createButton_clicked()
{
    QString name = QInputDialog::getText(this, tr("Enter Style Sheet Name"), tr("File name:"));
    if(name.isEmpty())
        return;

    if(!name.endsWith(u".qss"_s, Qt::CaseInsensitive))
        name.append(u".qss"_s);

    QString filePath = Qt6CT::userStyleSheetPath() + QLatin1Char('/') + name;

    if(QFile::exists(filePath))
    {
        QMessageBox::warning(this, tr("Error"), tr("The file \"%1\" already exists").arg(filePath));
        return;
    }

    //creating empty file
    QFile file(filePath);
    if(file.open(QIODevice::WriteOnly))
        file.close();

    //creating item
    QFileInfo info(filePath);
    QListWidgetItem *item = new QListWidgetItem(info.fileName(),  m_ui->qssListWidget);
    item->setToolTip(info.filePath());
    item->setData(QSS_FULL_PATH_ROLE, info.filePath());
    item->setData(QSS_WRITABLE_ROLE, info.isWritable());
    item->setCheckState(Qt::Unchecked);
}

void QSSPage::on_editButton_clicked()
{
    QListWidgetItem *item = m_ui->qssListWidget->currentItem();
    if(item)
    {
        QSSEditorDialog dialog(item->data(QSS_FULL_PATH_ROLE).toString(), this);
        dialog.exec();
    }
}

void QSSPage::on_removeButton_clicked()
{
    QListWidgetItem *item = m_ui->qssListWidget->currentItem();
    if(!item)
        return;

    int button = QMessageBox::question(this, tr("Confirm Remove"),
                                       tr("Are you sure you want to remove style sheet \"%1\"?")
                                       .arg(item->text()),
                                       QMessageBox::Yes | QMessageBox::No);
    if(button == QMessageBox::Yes)
    {
        QFile::remove(item->data(QSS_FULL_PATH_ROLE).toString());
        delete item;
    }
}

void QSSPage::readSettings()
{
    //load stylesheets
    m_ui->qssListWidget->clear();
    findStyleSheets(Qt6CT::userStyleSheetPath());
    findStyleSheets(Qt6CT::sharedStyleSheetPaths());

    QSettings settings(Qt6CT::configFile(), QSettings::IniFormat);
    QStringList styleSheets = settings.value("Interface/stylesheets"_L1).toStringList();
    for(int i = 0; i < m_ui->qssListWidget->count(); ++i)
    {
        QListWidgetItem *item = m_ui->qssListWidget->item(i);
        if(styleSheets.contains(item->data(QSS_FULL_PATH_ROLE).toString()))
            item->setCheckState(Qt::Checked);
        else
            item->setCheckState(Qt::Unchecked);
    }
}

void QSSPage::findStyleSheets(const QString &path)
{
    QDir dir(path);
    dir.setFilter(QDir::Files);
    dir.setNameFilters(QStringList() << u"*.qss"_s);

    for(const QFileInfo &info : dir.entryInfoList())
    {
        QListWidgetItem *item = new QListWidgetItem(info.fileName(),  m_ui->qssListWidget);
        item->setToolTip(info.filePath());
        item->setData(QSS_FULL_PATH_ROLE, info.filePath());
        item->setData(QSS_WRITABLE_ROLE, info.isWritable());
    }
}

void QSSPage::findStyleSheets(const QStringList &paths)
{
    for(const QString &p : paths)
        findStyleSheets(p);
}

void QSSPage::on_renameButton_clicked()
{
    QListWidgetItem *item = m_ui->qssListWidget->currentItem();
    if(!item)
        return;

    QString name = QInputDialog::getText(this, tr("Rename Style Sheet"), tr("Style sheet name:"),
                          QLineEdit::Normal, item->text(), nullptr);
    if(name.isEmpty())
        return;

    if(!name.endsWith(u".qss"_s, Qt::CaseInsensitive))
        name.append(u".qss"_s);

    if(!m_ui->qssListWidget->findItems(name, Qt::MatchExactly).isEmpty())
    {
        QMessageBox::warning(this, tr("Error"), tr("The style sheet \"%1\" already exists").arg(name));
        return;
    }

    QString newPath = Qt6CT::userStyleSheetPath() + QLatin1String("/") + name;

    if(!QFile::rename(item->data(QSS_FULL_PATH_ROLE).toString(), newPath))
    {
        QMessageBox::warning(this, tr("Error"), tr("Unable to rename file"));
        return;
    }

    item->setText(name);
    item->setData(QSS_FULL_PATH_ROLE, newPath);
    item->setToolTip(newPath);
}

void QSSPage::on_qssListWidget_customContextMenuRequested(const QPoint &pos)
{
    QListWidgetItem *item = m_ui->qssListWidget->currentItem();
    if(item && item->data(QSS_WRITABLE_ROLE).toBool())
    {
        m_menu->exec(m_ui->qssListWidget->viewport()->mapToGlobal(pos));
    }
}

void QSSPage::copyStyleSheet()
{
    QListWidgetItem *item = m_ui->qssListWidget->currentItem();

    if(!item)
        return;

    QString name = QInputDialog::getText(this, tr("Enter Style Sheet Name"), tr("File name:"),
                                         QLineEdit::Normal,
                                         tr("%1 (copy).qss").arg(item->text().section(QLatin1Char('.'),0,0)));

    if(name.isEmpty())
        return;

    if(!name.endsWith(u".qss"_s, Qt::CaseInsensitive))
        name.append(u".qss"_s);

    if(!m_ui->qssListWidget->findItems(name, Qt::MatchExactly).isEmpty())
    {
        QMessageBox::warning(this, tr("Error"), tr("The style sheet \"%1\" already exists").arg(name));
        return;
    }

    QString newPath = Qt6CT::userStyleSheetPath() + QLatin1Char('/') + name;

    if(!QFile::copy(item->data(QSS_FULL_PATH_ROLE).toString(), newPath))
    {
        QMessageBox::warning(this, tr("Error"), tr("Unable to copy file"));
        return;
    }

    QListWidgetItem *newItem = new QListWidgetItem(m_ui->qssListWidget);
    newItem->setText(name);
    newItem->setData(QSS_FULL_PATH_ROLE, newPath);
    newItem->setData(QSS_WRITABLE_ROLE, true);
    newItem->setToolTip(newPath);
    newItem->setCheckState(Qt::Unchecked);
}
