/*
 ** This file is part of the ViTE project.
 **
 ** This software is governed by the CeCILL-A license under French law
 ** and abiding by the rules of distribution of free software. You can
 ** use, modify and/or redistribute the software under the terms of the
 ** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
 ** URL: "http://www.cecill.info".
 **
 ** As a counterpart to the access to the source code and rights to copy,
 ** modify and redistribute granted by the license, users are provided
 ** only with a limited warranty and the software's author, the holder of
 ** the economic rights, and the successive licensors have only limited
 ** liability.
 **
 ** In this respect, the user's attention is drawn to the risks associated
 ** with loading, using, modifying and/or developing or reproducing the
 ** software by the user in light of its specific status of free software,
 ** that may mean that it is complicated to manipulate, and that also
 ** therefore means that it is reserved for developers and experienced
 ** professionals having in-depth computer knowledge. Users are therefore
 ** encouraged to load and test the software's suitability as regards
 ** their requirements in conditions enabling the security of their
 ** systems and/or data to be ensured and, more generally, to use and
 ** operate it in the same conditions as regards security.
 **
 ** The fact that you are presently reading this means that you have had
 ** knowledge of the CeCILL-A license and that you accept its terms.
 **
 **
 ** ViTE developers are (for version 0.* to 1.0):
 **
 **        - COULOMB Kevin
 **        - FAVERGE Mathieu
 **        - JAZEIX Johnny
 **        - LAGRASSE Olivier
 **        - MARCOUEILLE Jule
 **        - NOISETTE Pascal
 **        - REDONDY Arthur
 **        - VUCHENER Clément
 **
 */

#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 "interface/Interface.hpp"
#include "core/Core.hpp"
#include "interface/Settings_window.hpp"

using namespace std;

Settings_tab::Settings_tab(Core *core, std::string ecname)
{
    _changed = false;
    _core    = core;
    _ecname  = std::move(ecname);

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

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

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

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

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

    header = new QHBoxLayout();
    header->setObjectName(QString("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(QString("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(QString("btn_apply"));
    btn_apply->setText(QApplication::translate("settings", "Apply", nullptr));

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

    btn_reload = new QPushButton(this);
    btn_reload->setObjectName(QString("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(QString("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(QString("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(ev->get_name().c_str());

    /* Alias */
    QTableWidgetItem *alias = new QTableWidgetItem(ev->get_alias().c_str());

    /* Color */
    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, SIGNAL(colorChanged(const QColor &)),
            this, SLOT(item_changed()) );

    /* Visible */
    QCheckBox *cb = new QCheckBox();
    cb->setChecked( ev->get_visible() );
    connect(cb, SIGNAL(stateChanged(int)),
            this, SLOT(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(_core->get_trace()) { // Need a trace loaded
        ev_list = _core->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,
                                          "", &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 == "Default" ) {
        // Reload default from file
        fill_table( false );
    }
    else {
        list_palette->removeItem( list_palette->currentIndex() );

        int index = list_palette->findText("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 = _core->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;
            Color *color = new 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;

        _core->launch_action(Core::_STATE_RENDER_UPDATE);
    }
}

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 = _core->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();
            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(Core *c, QWidget *parent)
    : QWidget(parent), _c(c)
{
    QStringList palettes;
    string current;

    setupUi(this);

    plugin_load_dirs_name();
    reload_general_tab();

    _changed = false;

    reload_minimap_tab();

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

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

    _tab_links = new Settings_tab( _c, "link_types" );
    tabWidget->insertTab(3, _tab_links, "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.setFileMode(QFileDialog::DirectoryOnly);
    dialog.exec();

    if(dialog.result() == QDialog::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
        emit settings_changed();

        _c->launch_action(Core::_STATE_RENDER_UPDATE);
    }
    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;
    _c->launch_action(Core::_STATE_RENDER_UPDATE);
}

void
Settings_window::on_cb_nolinks_stateChanged(int index)
{
    _c->launch_action(Core::_STATE_RENDER_UPDATE);
    (void)index;
}

void
Settings_window::on_cb_noevents_stateChanged(int index)
{
    _c->launch_action(Core::_STATE_RENDER_UPDATE);
    (void)index;
}

