/**
 *
 * @file src/interface/Settings_window.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 * @author Thibault Soucarre
 * @author Augustin Degomme
 *
 * @date 2024-07-17
 */

#include <iostream>
#include <string>
#include <cassert>
/* -- */
#include <QStringList>
#include <QColor>
#include <QFileDialog>
#include <QColorDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QMap>
/* -- */
#include <qtcolorpicker.h>
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "common/Palette.hpp"
#include "common/Session.hpp"
#include "common/common.hpp"
#include "common/Info.hpp"
/* -- */
#include "core/Core.hpp"
#include "interface/Interface_graphic.hpp"
#include "interface/Settings_window.hpp"

using namespace std;

Settings_tab::Settings_tab(Interface_graphic *interface_graphic, std::string ecname) {
    _changed = false;
    _interface_graphic = interface_graphic;
    _ecname = std::move(ecname);

    // Create the header
    hdr_label = new QLabel(this);
    hdr_label->setObjectName(QStringLiteral("hdr_label"));
    hdr_label->setText(QApplication::translate("settings", "Colors set:", nullptr));

    list_palette = new QComboBox(this);
    list_palette->setObjectName(QStringLiteral("list_palette"));

    hdr_spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

    btn_palette_cp = new QPushButton(this);
    btn_palette_cp->setObjectName(QStringLiteral("btn_palette_cp"));
    btn_palette_cp->setText(QApplication::translate("settings", "Copy", nullptr));

    btn_palette_rm = new QPushButton(this);
    btn_palette_rm->setObjectName(QStringLiteral("btn_palette_rm"));
    btn_palette_rm->setText(QApplication::translate("settings", "Remove", nullptr));

    header = new QHBoxLayout();
    header->setObjectName(QStringLiteral("header"));
    header->addWidget(hdr_label);
    header->addWidget(list_palette);
    header->addItem(hdr_spacer);
    header->addWidget(btn_palette_cp);
    header->addWidget(btn_palette_rm);

    // Create the content
    content = new QTableWidget(this);
    content->setColumnCount(3);
    content->setObjectName(QStringLiteral("content"));

    QTableWidgetItem *__qtablewidgetitem = new QTableWidgetItem();
    content->setHorizontalHeaderItem(0, __qtablewidgetitem);
    __qtablewidgetitem->setText(QApplication::translate("settings", "Name", nullptr));

    QTableWidgetItem *__qtablewidgetitem1 = new QTableWidgetItem();
    content->setHorizontalHeaderItem(1, __qtablewidgetitem1);
    __qtablewidgetitem1->setText(QApplication::translate("settings", "Color", nullptr));

    QTableWidgetItem *__qtablewidgetitem2 = new QTableWidgetItem();
    content->setHorizontalHeaderItem(2, __qtablewidgetitem2);
    __qtablewidgetitem2->setText(QApplication::translate("settings", "Visible", nullptr));

    QSizePolicy sizePolicy3(QSizePolicy::Expanding, QSizePolicy::Expanding);
    sizePolicy3.setHorizontalStretch(100);
    sizePolicy3.setVerticalStretch(100);
    sizePolicy3.setHeightForWidth(content->sizePolicy().hasHeightForWidth());
    content->setSizePolicy(sizePolicy3);

    // Create the footer
    ftr_spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

    btn_apply = new QPushButton(this);
    btn_apply->setObjectName(QStringLiteral("btn_apply"));
    btn_apply->setText(QApplication::translate("settings", "Apply", nullptr));

    btn_cancel = new QPushButton(this);
    btn_cancel->setObjectName(QStringLiteral("btn_cancel"));
    btn_cancel->setText(QApplication::translate("settings", "Cancel", nullptr));

    btn_reload = new QPushButton(this);
    btn_reload->setObjectName(QStringLiteral("btn_reload"));
    btn_reload->setText(QApplication::translate("settings", "Reload from file", nullptr));
#ifndef QT_NO_TOOLTIP
    btn_reload->setToolTip(QApplication::translate("settings", "Reload all the states types from the displayed trace", nullptr));
#endif // QT_NO_TOOLTIP

    footer = new QHBoxLayout();
    footer->setObjectName(QStringLiteral("footer"));
    footer->addItem(ftr_spacer);
    footer->addWidget(btn_apply);
    footer->addWidget(btn_cancel);
    footer->addWidget(btn_reload);

    // Assemble the background
    background = new QVBoxLayout(this);
    background->setObjectName(QStringLiteral("background"));
    background->addLayout(header);
    background->addWidget(content);
    background->addLayout(footer);

    refresh();

    QMetaObject::connectSlotsByName(this);
}

Settings_tab::~Settings_tab() {
    delete hdr_label;
    delete list_palette;
    delete hdr_spacer;
    delete btn_palette_cp;
    delete btn_palette_rm;
    delete header;
    delete content;
    delete ftr_spacer;
    delete btn_apply;
    delete btn_cancel;
    delete btn_reload;
    delete footer;
    delete background;
}

void Settings_tab::refresh() {
    // Initialize the parameters of the tab
    Session &S = Session::getSession();
    QStringList palettes;
    string current;

    S.get_palettes_name(_ecname, palettes);
    current = S.get_current_palette(_ecname);

    list_palette->clear();
    list_palette->addItems(palettes);
    // Set the combobox at the good index.
    list_palette->setCurrentIndex(list_palette->findText(QString::fromStdString(current)));

    fill_table();
}

void Settings_tab::item_changed(int row) {
    _changed = true;

    if (!content || !list_palette)
        return;

    if (row == -1) {
        // Find the row of the colorPicker
        row = 0;
        while (content->cellWidget(row, 1) && (content->cellWidget(row, 1) != sender()) && (content->cellWidget(row, 2) != sender())) {
            row++;
        }
    }

    if (!content->cellWidget(row, 1))
        return; // epic fail...

    std::string ev_alias = content->item(row, 3)->text().toStdString();

    // If the state has not already been changed, we register it
    if (_changes.count(ev_alias) == 0) {
        _changes[ev_alias] = row;
    }
    return;
}

void Settings_tab::add_table_line(int &row,
                                  EntityValue *ev,
                                  bool used) {
    /* Name */
    QTableWidgetItem *name = new QTableWidgetItem(QString::fromStdString(ev->get_name()));

    /* Alias */
    QTableWidgetItem *alias = new QTableWidgetItem(QString::fromStdString(ev->get_alias()));

    /* Color */
    const Color *c = nullptr;
    QtColorPicker *color_widget = new QtColorPicker();
    color_widget->setStandardColors();
    if (used) {
        c = ev->get_used_color();
    }
    else {
        c = ev->get_file_color();
    }
    assert(c);

    QColor qc(c->get_red() * 255, c->get_green() * 255, c->get_blue() * 255);
    color_widget->setCurrentColor(qc);
    color_widget->setColorDialogEnabled(false);
    connect(color_widget, &QtColorPicker::colorChanged,
            this, [=]() {
                item_changed();
            });

    /* Visible */
    QCheckBox *cb = new QCheckBox();
    cb->setChecked(ev->get_visible());
    connect(cb, &QCheckBox::stateChanged,
            this, &Settings_tab::item_changed);

    /* Set the row */
    content->insertRow(row);
    content->setItem(row, 0, name);
    content->setCellWidget(row, 1, color_widget);
    content->setCellWidget(row, 2, cb);
    content->setItem(row, 3, alias);

    /* If we reload from file, backup the changes */
    if (!used) {
        /* Change the color test */
        if ((c != ev->get_used_color()) || (!ev->get_visible())) {
            item_changed(row);
        }
    }
    row++;
}

void Settings_tab::fill_table(bool used) {
    map<string, EntityValue *> *ev_list;
    map<string, EntityValue *>::const_iterator ev;
    int row = 0;

    /* Reset the table before insertion */
    content->clearContents();
    content->setRowCount(0);
    content->setColumnCount(4);
    content->setColumnHidden(3, true);

    /* Get the list from trace */
    if (_interface_graphic->get_trace()) { // Need a trace loaded
        ev_list = _interface_graphic->get_trace()->get_all_entityvalues(_ecname);
    }
    else {
        std::cerr << "fill_table (" << _ecname << "): No trace loaded" << std::endl;
        return;
    }

    // Add a line per EntityValue
    for (ev = ev_list->begin();
         ev != ev_list->end(); ++ev) {
        add_table_line(row, ev->second, used);
    }
}

void Settings_tab::update_table_from_palette(const string &pname) {
    Session &S = Session::getSession();
    Palette *p = S.get_palette(_ecname, pname);

    map<string, EntityValue *>::const_iterator ev;

    int rownbr = content->rowCount();

    for (int i = 0; i < rownbr; i++) {
        string ev_name = content->item(i, 0)->text().toStdString();
        string ev_alias = content->item(i, 3)->text().toStdString();
        Color *c = p->get_color(ev_name);

        if (c != nullptr) {
            _changed = true;
            bool v = p->is_visible(ev_name);

            QtColorPicker *color_widget = qobject_cast<QtColorPicker *>(content->cellWidget(i, 1));
            color_widget->setCurrentColor(QColor(c->get_red() * 255,
                                                 c->get_green() * 255,
                                                 c->get_blue() * 255));

            QCheckBox *cb = qobject_cast<QCheckBox *>(content->cellWidget(i, 2));
            cb->setCheckState(v ? Qt::Checked : Qt::Unchecked);

            _changes[ev_alias] = i;
        }
    }
}

void Settings_tab::on_list_palette_currentIndexChanged(const QString &text) {
    update_table_from_palette(text.toStdString());
}

void Settings_tab::on_btn_palette_cp_clicked() {
    bool ok;
    QString text = QInputDialog::getText(this, tr("ViTE"),
                                         tr("New colors set name:"),
                                         QLineEdit::Normal,
                                         QString(), &ok);

    // Copy the color set
    if (ok && !text.isEmpty()) {

        Session &S = Session::getSession();

        // Check if the name already exists
        if (list_palette->findText(text) != -1) {
            if (QMessageBox::warning(this, tr("ViTE"),
                                     tr("Colors set already exists.\n"
                                        "Are you sure you want to erase it?"),
                                     QMessageBox::Ok | QMessageBox::Cancel)
                != QMessageBox::Ok) {
                return;
            }

            // Remove the existing set
            S.remove_palette(_ecname, text.toStdString());
        }
        else {
            list_palette->addItem(text);
        }

        S.copy_palette(_ecname,
                       list_palette->currentText().toStdString(),
                       text.toStdString());
        list_palette->setCurrentIndex(list_palette->findText(text));
    }
}

void Settings_tab::on_btn_palette_rm_clicked() {
    QString current = list_palette->currentText();

    Session::getSession().remove_palette(_ecname, current.toStdString());

    if (current == QLatin1String("Default")) {
        // Reload default from file
        fill_table(false);
    }
    else {
        list_palette->removeItem(list_palette->currentIndex());

        int index = list_palette->findText(QStringLiteral("Default"));
        assert(index != -1);
        list_palette->setCurrentIndex(index);

        update_table_from_palette("Default");
    }
}

void Settings_tab::on_btn_apply_clicked() {
    if (_changed) {
        Session &S = Session::getSession();

        string setname = list_palette->currentText().toStdString();
        S.set_current_palette(_ecname, setname);

        map<string, EntityValue *> *ev_list;
        map<string, EntityValue *>::iterator ev;
        std::map<std::string, int>::iterator it;

        ev_list = _interface_graphic->get_trace()->get_all_entityvalues(_ecname);

        it = _changes.begin();
        while (it != _changes.end()) {
            int i = it->second;
            string ev_name = content->item(i, 0)->text().toStdString();
            QColor qc = qobject_cast<QtColorPicker *>(content->cellWidget(i, 1))->currentColor();
            bool visible = qobject_cast<QCheckBox *>(content->cellWidget(i, 2))->checkState() == Qt::Checked;
            const Color color = Color(qc.red() / 255., qc.green() / 255., qc.blue() / 255.);

            ev = ev_list->find(it->first);
            assert(ev != ev_list->end());

            (ev->second)->set_used_color(color);
            (ev->second)->set_visible(visible);

            S.add_state_to_palette(_ecname, setname, ev_name, color, visible);

            ++it;
        }

        _changes.clear();
        _changed = false;

        _interface_graphic->get_render_area()->update_render();
    }
}

void Settings_tab::on_btn_cancel_clicked() {
    if (_changed) {
        map<string, EntityValue *> *ev_list;
        map<string, EntityValue *>::iterator ev;
        std::map<std::string, int>::iterator it;

        ev_list = _interface_graphic->get_trace()->get_all_entityvalues(_ecname);

        it = _changes.begin();
        while (it != _changes.end()) {
            int i = it->second;

            ev = ev_list->find(it->first);
            assert(ev != ev_list->end());

            bool visible = (ev->second)->get_visible();
            const Color *color = (ev->second)->get_used_color();

            QtColorPicker *color_widget = qobject_cast<QtColorPicker *>(content->cellWidget(i, 1));
            color_widget->setCurrentColor(QColor(color->get_red() * 255,
                                                 color->get_green() * 255,
                                                 color->get_blue() * 255));

            QCheckBox *cb = qobject_cast<QCheckBox *>(content->cellWidget(i, 2));
            cb->setCheckState(visible ? Qt::Checked : Qt::Unchecked);

            ++it;
        }

        _changes.clear();
        _changed = false;
        update();
    }
}

void Settings_tab::on_btn_reload_clicked() {
    fill_table(false);
}

Settings_window::Settings_window(Interface_graphic *interface_graphic, QWidget *parent) :
    QWidget(parent), _interface_graphic(interface_graphic) {
    QStringList palettes;
    string current;

    setupUi(this);

    plugin_load_dirs_name();
    reload_general_tab();

    _changed = false;

    reload_minimap_tab();

    _tab_states = new Settings_tab(interface_graphic, "palette");
    tabWidget->insertTab(1, _tab_states, QStringLiteral("States"));

    _tab_events = new Settings_tab(interface_graphic, "event_types");
    tabWidget->insertTab(2, _tab_events, QStringLiteral("Events"));

    _tab_links = new Settings_tab(interface_graphic, "link_types");
    tabWidget->insertTab(3, _tab_links, QStringLiteral("Links"));
}

Settings_window::~Settings_window() = default;

void Settings_window::on_tabWidget_currentChanged(int) {
}

void Settings_window::refresh() {
    _tab_states->refresh();
    _tab_events->refresh();
    _tab_links->refresh();
}

void Settings_window::show() {
    // The plugin tab
    refresh();
    dir_list->clear();
    plugin_load_dirs_name();
    reload_minimap_tab();
    QWidget::show();
}

/************************/
/***** General tab. *****/
/************************/
void Settings_window::reload_general_tab() {
}

/************************/
/***** Minimap tab. *****/
/************************/
void Settings_window::reload_minimap_tab() {
    MinimapSettings &ms = Session::getSessionMinimap();

    minimap_hide_cb->setChecked(ms._is_closed);
    minimap_x_sb->setValue(ms._x);
    minimap_y_sb->setValue(ms._y);
    minimap_w_sb->setValue(ms._width);
    minimap_h_sb->setValue(ms._height);
    // \todo Square border size? kesako?
    // \todo maybe colors?
}

/*************************/
/****** Plugin tab. ******/
/*************************/

void Settings_window::on_rm_dir_btn_clicked() {
    dir_list->takeItem(dir_list->currentRow());
}

void Settings_window::on_add_dir_btn_clicked() {
    QFileDialog dialog(this, tr("Add a directory"));

    dialog.setOption(QFileDialog::ShowDirsOnly, true);
    dialog.exec();

    if (dialog.result() == QFileDialog::Accepted) {
        QStringList list = dialog.selectedFiles();
        dir_list->addItems(list);
    }
}

void Settings_window::plugin_load_dirs_name() {
    QStringList dirs;
    Session::getSession().load_plugin_directories(dirs);
    dir_list->addItems(dirs);
}

/*************************/
/* Global buttons click. */
/*************************/
// void Settings_window::on_btn_apply_clicked() {
//     Session         &S  = Session::getSession();
//     MinimapSettings &ms = Session::getSessionMinimap();

//     _changed=false;
//     // The plugin tab
//     QStringList dir_names;
//     for(int i = 0 ; i < dir_list->count() ; i ++) {
//         dir_names << dir_list->item(i)->text();
//     }
//     S.save_plugin_directories(dir_names);

//     // The minimap tab
//     minimap_hide_cb->setChecked(ms._is_closed);
//     minimap_x_sb->setValue(ms._x);
//     minimap_y_sb->setValue(ms._y);
//     minimap_w_sb->setValue(ms._width);
//     minimap_h_sb->setValue(ms._height);

//     /*cout <<minimap_x_sb->value() << " " << minimap_y_sb->value() <<" " <<
//      minimap_w_sb->value() <<" " <<  minimap_h_sb->value() <<" " <<
//      minimap_hide_cb->isChecked() << endl;*/

//     ms.save( minimap_x_sb->value(),
//              minimap_y_sb->value(),
//              minimap_w_sb->value(),
//              minimap_h_sb->value(),
//              minimap_hide_cb->isChecked());

//     _c->launch_action(Core::_STATE_RENDER_UPDATE);
// }

void Settings_window::on_btn_ok_clicked() {
    _tab_states->on_btn_apply_clicked();
    _tab_events->on_btn_apply_clicked();
    _tab_links->on_btn_apply_clicked();

    if (_changed) {
        // Catch by at least the interface_graphic which will dispatch it to the classes which needs
        Q_EMIT settings_changed();

        _interface_graphic->get_render_area()->update_render();
    }
    hide();
}

void Settings_window::on_btn_cancel_clicked() {
    _tab_states->on_btn_cancel_clicked();
    _tab_events->on_btn_cancel_clicked();
    _tab_links->on_btn_cancel_clicked();

    hide();
}

void Settings_window::on_cb_tip_currentIndexChanged(int index) {
    Info::Render::_arrows_shape = index;
    _interface_graphic->get_render_area()->update_render();
}

void Settings_window::on_cb_nolinks_stateChanged(int index) {
    _interface_graphic->get_render_area()->update_render();
    (void)index;
}

void Settings_window::on_cb_noevents_stateChanged(int index) {
    _interface_graphic->get_render_area()->update_render();
    (void)index;
}

#include "moc_Settings_window.cpp"
