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

using namespace std;

Settings_window::Settings_window(Core *c, QWidget *parent) : QWidget(parent), _c(c) {
    setupUi(this);

    plugin_load_dirs_name();
    reload_general_tab();

    _changed = false;

    Session &S = Session::getSession();

    // const string &tabName = tabWidget->tabText(index).toStdString();

    // Need to load the current palette.
    palette_list->clear();
    QStringList palettes;
    S.get_palettes_name("palette", palettes);
    palette_list->addItems(palettes);
    string current = S.get_current_palette("palette");

    fill_table_states(current);
    palette_list->setCurrentIndex(palette_list->findText(QString::fromStdString(current))); // Set the combobox at the good index.


    reload_minimap_tab();
    event_types_palette_list->clear();
    S.get_palettes_name("event_types", palettes);
    event_types_palette_list->addItems(palettes);
    current = S.get_current_palette("event_types");
    // Need to load the types
    fill_table_events(current);
    event_types_palette_list->setCurrentIndex(event_types_palette_list->findText(QString::fromStdString(current))); // Set the combobox at the good index.
    link_types_palette_list->clear();
    //   QStringList palettes;
    S.get_palettes_name("link_types", palettes);
    link_types_palette_list->addItems(palettes);
    current = S.get_current_palette("link_types");
    // Need to load the types
    fill_table_links(current);
    link_types_palette_list->setCurrentIndex(link_types_palette_list->findText(QString::fromStdString(current))); // Set the combobox at the good index.
}

Settings_window::~Settings_window() {
}

void Settings_window::on_tabWidget_currentChanged(int ) {

}

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

/************************/
/***** General tab. *****/
/************************/
void Settings_window::reload_general_tab() {
    Session &S = Session::getSession();
    use_palette_cb->setChecked(S.get_use_palette("palette"));
}

/************************/
/***** 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);
}

/************************/
/****** State tab. ******/
/************************/

void Settings_window::color_changed(string name, QTableWidget *table, const QColor &qc) {
    _changed = true;

    if( !table || !palette_list)
        return;

    Color color(qc.red()/255., qc.green()/255., qc.blue()/255.);
    int row = 0;

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

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

    std::string palette_name = palette_list->currentText().toStdString();
    std::string state_name   = table->item(row, 0)->text().toStdString();

    Session &S = Session::getSession();
    Palette *p = S.get_palette(name, palette_name);

    bool checked = qobject_cast<QCheckBox *>(table->cellWidget(row, 2))->checkState()== Qt::Checked;

    S.add_state_to_palette(name, palette_name, state_name, color, checked );
    p->add_state(state_name, color, checked );
    return;
}

void Settings_window::add_table_line(QTableWidget *table,
                                     const char *slot,
                                     int &row, std::string st_name, Color *c, bool checked) {
    /* Name */
    QTableWidgetItem *name = new QTableWidgetItem(st_name.c_str());
    name->setFlags(Qt::ItemIsEnabled);

    /* Color */
    QtColorPicker *color_widget = new QtColorPicker();
    color_widget->setStandardColors();
    if(c) {
        color_widget->setCurrentColor(QColor(c->get_red()*255, c->get_green()*255, c->get_blue()*255));
    }
    else { // \todo check the default color value -> put this in Palette as static member
        color_widget->setCurrentColor(QColor(255, 255, 255));
    }
    color_widget->setColorDialogEnabled(false);
    connect(color_widget, SIGNAL(colorChanged(const QColor &)),
            this, slot );

    /* Visible */
    QCheckBox *cb = new QCheckBox();
    cb->setChecked(checked);

    table->insertRow(row);
    table->setItem(row, 0, name);
    table->setCellWidget(row, 1, color_widget);
    table->setCellWidget(row, 2, cb);
    row ++;
}

void Settings_window::fill_table( QTableWidget *table,
                                  const char *slot,
                                  const string pname,
                                  const string &palette_name )
{
    map<string, Color *> colors_settings;
    map<string, Color *> colors_trace;
    int row = 0;

    /* Clean the table before insertion */
    while(table->rowCount() > 0) { // Clear the current table
        table->removeRow(0);
    }

    /* Get the list form settings */
    Palette *p = Session::getSession().get_palette( pname, palette_name );
    colors_settings = p->get_map();

    /* Get the list from trace */
    if(_c->get_trace()) { // Need a trace loaded
        _c->get_trace()->get_colors(pname, colors_trace);
    }
    else {
        //cerr << "no trace loaded" << endl;
        return;
    }

    map<string, Color *>::const_iterator it;

    colors_settings.insert( colors_trace.begin(), colors_trace.end() );

    for(it = colors_settings.begin();
        it != colors_settings.end(); ++it)
    {
        add_table_line(table, slot,
                       row, it->first, it->second, p->is_visible( it->first ));
    }
}

void Settings_window::state_color_changed(const QColor &qc) {
    color_changed("palette", states_table, qc);
}

void Settings_window::event_types_color_changed(const QColor &qc) {
    color_changed("event_types", event_types_table, qc);
}

void Settings_window::link_types_color_changed(const QColor &qc) {
    color_changed("link_types", link_types_table, qc);
}

void Settings_window::fill_table_states( const string &palette_name ) {
    fill_table( states_table,
                SLOT(state_color_changed(const QColor &)),
                "palette", palette_name );
}

void Settings_window::fill_table_events( const string &palette_name ) {
    fill_table( event_types_table,
                SLOT(event_types_color_changed(const QColor &)),
                "event_types", palette_name );
}

void Settings_window::fill_table_links( const string &palette_name ) {
    fill_table( link_types_table,
                SLOT(link_types_color_changed(const QColor &)),
                "link_types", palette_name );
}





void Settings_window::on_palette_list_currentIndexChanged(const QString & text) {
    _changed=true;
    fill_table_states(text.toStdString());
}

void Settings_window::add_palette(QComboBox* list, const std::string type){
    _changed=true;
    bool ok;
    QString text = QInputDialog::getText(this, tr("ViTE"),
                                         tr("Palette name:"), QLineEdit::Normal,
                                         "", &ok);
    if (ok && !text.isEmpty()) { // Palette name is set
        list->addItem(text);
        Session::getSession().create_palette(type,text.toStdString());
    }
}


void Settings_window::on_add_palette_btn_clicked() {
    add_palette(palette_list, "palette" );
}


QString Settings_window::copy_palette(QTableWidget* , QComboBox* list, const std::string type){
    _changed=true;
    bool ok;
    QString text = QInputDialog::getText(this, tr("ViTE"),
                                         tr("new palette name:"), QLineEdit::Normal,
                                         "", &ok);
    string current = list->currentText().toStdString();

    Session &S = Session::getSession();

    if (ok && !text.isEmpty()) { // Palette name is set
        if (list->findText(text) != -1) { // Already set
            if(QMessageBox::warning(this, tr("ViTE"), tr("Palette already exists.\nAre you sure you want to erase it?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) {
                return "default";
            }

            // Save
            // We remove the existing one
            S.remove_palette(type,text.toStdString());
        }
        else { // Adding if not existing in the list
            list->addItem(text);
        }

        //write the current palette to the file before copying it
        //remove it and then rewrite it (default might have been used with another trace and not be the same)
        /* S.remove_palette(type,current);
         S.create_palette(type,current);

         for (int i=0; i<table->rowCount(); i++)
         {
         if(table->cellWidget(i, 2) && qobject_cast<QCheckBox *>(table->cellWidget(i, 2))->checkState()== Qt::Checked) {
         QColor qc= qobject_cast<QtColorPicker *>(table->cellWidget(i, 1))->currentColor();
         Color* color= new Color(qc.red()/255., qc.green()/255., qc.blue()/255.);
         S.add_state_to_palette(type, current, table->item(i, 0)->text().toStdString(), *color);

         }
         }*/
        //now we can copy the palette

        S.copy_palette(type,current, text.toStdString());
        list->setCurrentIndex(list->findText(text));
    }

    return text;

}

void Settings_window::on_copy_palette_btn_clicked() {
    QString text= copy_palette(states_table, palette_list, "palette");
    fill_table_states(text.toStdString());
}



void Settings_window::rm_palette(QComboBox* list, const std::string type){
    _changed=true;
    QString text = list->currentText();
    list->removeItem (list->currentIndex());
    Session::getSession().remove_palette(type,text.toStdString());
}

void Settings_window::on_rm_palette_btn_clicked() {
    rm_palette(palette_list, "palette");
    fill_table_states(palette_list->currentText().toStdString());
}

void Settings_window::on_reload_states_btn_clicked(){
    _changed=true;
    while(states_table->rowCount() > 0) { // Clear the current table
        states_table->removeRow(0);
    }

    map<string, Color *> colors;
    int row = 0;

    if(_c->get_trace()) { // Need a trace loaded
        _c->get_trace()->get_states_colors(colors);
    }
    else {
        cerr << "no trace loaded" << endl;
        return;
    }

    for(map<string, Color *>::iterator it = colors.begin() ;
        it != colors.end() ; ++ it) {
        add_table_line(states_table, SLOT(state_color_changed(const QColor &)),
                       row, (*it).first.c_str(), (*it).second, true);
    }

}

void Settings_window::on_add_state_btn_clicked() {
    _changed=true;
    bool ok;
    QString text = QInputDialog::getText(this, tr("ViTE"),
                                         tr("State name:"), QLineEdit::Normal,
                                         "", &ok);
    if (ok && !text.isEmpty()) {
        QColor qc = QColorDialog::getColor();
        if(!qc.isValid()) {
            cerr << "Color not chosen\n" << endl;
        }
        else { // State name is set and color is valid.
            Color color(qc.red()/255., qc.green()/255., qc.blue()/255.);
            // Add a row to the table
            const int new_row = states_table->rowCount();
            states_table->insertRow(new_row);
            // First item : the state name
            QTableWidgetItem *name = new QTableWidgetItem(text);
            name->setFlags(Qt::ItemIsEnabled);
            states_table->setItem(new_row, 0, name);
            // Second item : the color
            QtColorPicker *color_widget = new QtColorPicker();
            color_widget->setStandardColors();
            color_widget->setCurrentColor(qc);
            states_table->setCellWidget(new_row, 1, color_widget);
            // Third item : if we show the state or no (not implemented here and in the trace)
            QCheckBox *cb = new QCheckBox();
            cb->setChecked(true);
            states_table->setCellWidget(new_row, 2, cb);

            Session::getSession().add_state_to_palette("palette",palette_list->currentText().toStdString(), text.toStdString(), color,true);
        }
    }
}

void Settings_window::on_rm_state_btn_clicked() {
    _changed=true;
    if (states_table->currentRow()!=-1){//do nothing if no row is selected
        QString text = states_table->item(states_table->currentRow(), 0)->text();
        Session::getSession().remove_state_to_palette("palette",palette_list->currentText().toStdString(), text.toStdString());
        states_table->removeRow(states_table->currentRow());
    }
}



/*************************/
/****** Types tab. ******/
/*************************/

void Settings_window::on_event_types_palette_list_currentIndexChanged(const QString &text) {
    _changed=true;
    fill_table_events(text.toStdString());
}

void Settings_window::on_link_types_palette_list_currentIndexChanged(const QString &text) {
    _changed=true;
    fill_table_links(text.toStdString());
}

void Settings_window::on_reload_link_types_palette_btn_clicked() {
    _changed=true;
    while(link_types_table->rowCount() > 0) { // Clear the current table
        link_types_table->removeRow(0);
    }
    int row = 0;
    //add_palette(link_types_palette_list, "link_types" );
    const map<Name, LinkType* > * link_types;
    if(_c->get_trace()) { // Need a trace loaded
        link_types = _c->get_trace()->get_link_types();
    }
    else {
        cerr << "no trace loaded" << endl;
        return;
    }

    for(map<Name, LinkType *>::const_iterator it = link_types->begin() ;
        it != link_types->end() ; ++ it) {

        Color *color = NULL;
        if((*it).second->get_extra_fields()!=NULL && ((*it).second->get_extra_fields()->find(string("Color")) != (*it).second->get_extra_fields()->end())) {
            color = (Color *)((*it).second->get_extra_fields()->find(string("Color"))->second);
        }


        add_table_line(link_types_table, SLOT(link_types_color_changed(const QColor&)),
                       row, (*it).first.to_string(), color, true);
    }

}

void Settings_window::on_reload_event_types_palette_btn_clicked() {
    _changed=true;
    while(event_types_table->rowCount() > 0) { // Clear the current table
        event_types_table->removeRow(0);
    }
    int row = 0;
    //add_palette(link_types_palette_list, "link_types" );
    const map<Name, EventType* > * event_types;
    if(_c->get_trace()) { // Need a trace loaded
        event_types = _c->get_trace()->get_event_types();
    }
    else {
        cerr << "no trace loaded" << endl;
        return;
    }

    for(map<Name, EventType *>::const_iterator it = event_types->begin() ;
        it != event_types->end() ; ++ it) {

        Color *color = NULL;
        if((*it).second->get_extra_fields()!=NULL && ((*it).second->get_extra_fields()->find(string("Color")) != (*it).second->get_extra_fields()->end())) {
            color = (Color *)((*it).second->get_extra_fields()->find(string("Color"))->second);
        }


        add_table_line(event_types_table, SLOT(event_types_color_changed(const QColor&)),
                       row, (*it).first.to_string(), color, true);
    }
}

void Settings_window::on_copy_link_types_palette_btn_clicked() {
    QString text= copy_palette(link_types_table, link_types_palette_list, "link_types");
    fill_table_links(text.toStdString());
}

void Settings_window::on_copy_event_types_palette_btn_clicked() {
    QString text= copy_palette(event_types_table, event_types_palette_list, "event_types");
    fill_table_events(text.toStdString());
}

void Settings_window::on_rm_link_types_palette_btn_clicked() {
    rm_palette(link_types_palette_list, "link_types");
    fill_table_links(link_types_palette_list->currentText().toStdString());
}

void Settings_window::on_rm_event_types_palette_btn_clicked() {
    rm_palette(event_types_palette_list, "event_types");
    fill_table_events(event_types_palette_list->currentText().toStdString());
}


/*************************/
/* Global buttons click. */
/*************************/
void Settings_window::on_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 states tab
    S.set_current_palette("palette",     palette_list->currentText().toStdString());
    S.set_current_palette("link_types",  link_types_palette_list->currentText().toStdString());
    S.set_current_palette("event_types", event_types_palette_list->currentText().toStdString());

    // The general tab
    S.set_use_palette("palette",    use_palette_cb->isChecked());
    S.set_use_palette("link_types", use_palette_cb->isChecked());
    S.set_use_palette("event_types",use_palette_cb->isChecked());

    // 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());

    //set the types to display
    //S.set_custom_types(true);
    //S.clear_event_type();
    //S.clear_link_type();

    S.remove_palette("palette",     palette_list->currentText().toStdString());
    S.create_palette("palette",     palette_list->currentText().toStdString());
    S.remove_palette("link_types",  link_types_palette_list->currentText().toStdString());
    S.create_palette("link_types",  link_types_palette_list->currentText().toStdString());
    S.remove_palette("event_types", event_types_palette_list->currentText().toStdString());
    S.create_palette("event_types", event_types_palette_list->currentText().toStdString());

    for (int i=0; i<states_table->rowCount(); i++)
    {
        if(states_table->cellWidget(i, 2))
        {
            QColor qc= qobject_cast<QtColorPicker *>(states_table->cellWidget(i, 1))->currentColor();
            Color* color= new Color(qc.red()/255., qc.green()/255., qc.blue()/255.);
            //S.add_link_type(states_table->item(i, 0)->text().toStdString(), *color);
            S.add_state_to_palette("palette", palette_list->currentText().toStdString(), states_table->item(i, 0)->text().toStdString(), *color, qobject_cast<QCheckBox *>(states_table->cellWidget(i, 2))->checkState()== Qt::Checked);
        }

    }

    for (int i=0; i<link_types_table->rowCount(); i++)
    {
        if(link_types_table->cellWidget(i, 2))
        {
            QColor qc= qobject_cast<QtColorPicker *>(link_types_table->cellWidget(i, 1))->currentColor();
            Color* color= new Color(qc.red()/255., qc.green()/255., qc.blue()/255.);
            //S.add_link_type(link_types_table->item(i, 0)->text().toStdString(), *color);
            S.add_state_to_palette("link_types",
                                   link_types_palette_list->currentText().toStdString(),
                                   link_types_table->item(i, 0)->text().toStdString(),
                                   *color,qobject_cast<QCheckBox *>(link_types_table->cellWidget(i, 2))->checkState()== Qt::Checked);
        }
    }

    for (int i=0; i<event_types_table->rowCount(); i++)
    {
        if(event_types_table->cellWidget(i, 2))
        {
            QColor qc= qobject_cast<QtColorPicker *>(event_types_table->cellWidget(i, 1))->currentColor();
            Color* color= new Color(qc.red()/255., qc.green()/255., qc.blue()/255.);
            //S.add_event_type(event_types_table->item(i, 0)->text().toStdString(), *color);
            S.add_state_to_palette("event_types", event_types_palette_list->currentText().toStdString(), event_types_table->item(i, 0)->text().toStdString(), *color, qobject_cast<QCheckBox *>(event_types_table->cellWidget(i, 2))->checkState()== Qt::Checked);

        }
    }

    // emit changes. For now it is a global emit. Maybe only emits signals for what has changed using booleans?
    emit settings_changed(); // Catch by at least the interface_graphic which will dispatch it to the classes which needs
    //hide();
}


void Settings_window::on_use_palette_cb_stateChanged(){
    _changed = true;
}

void Settings_window::on_cancel_clicked() {
    hide();
}

void Settings_window::on_ok_clicked(){
    if( _changed )
        on_apply_clicked();
    hide();
}
