/*
** 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 <sstream>
#include <fstream>
#include <string>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>

/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
#include "interface/resource.hpp"
#include "interface/Interface.hpp"

#include "core/Core.hpp"
#include "interface/Interface_graphic.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/Container.hpp"
/* -- */
#include <QFileDialog> // To choose the file to save
#include <QKeyEvent>
#include <QDropEvent>
#include <QtXml>
#include <QFile>
#include <iostream>

#include "interface/Node_select.hpp"
Q_DECLARE_METATYPE( Container * )

using namespace std;

Node_select::Node_select(Interface_graphic * console,QWidget *parent) : QWidget(parent), _trace(nullptr),_console(console), _sort_order(false) {
    setupUi(this);



    QMetaObject::connectSlotsByName(nullptr);
}

Node_select::~Node_select() = default;

void Node_select::set_initial_container_names(){

  const Container::Vector *root_containers = _trace->get_root_containers();

    if(root_containers->empty()) {
        *Message::get_instance() << tr("No containers in this trace").toStdString() << Message::ende;
        return;
    }
        _nodes_original->clear();
    // Add the root container names
    QList<QTreeWidgetItem *> items;
    QFlags<Qt::ItemFlag>     flg;
    for (const auto &root_container : *root_containers) {
        string           name = root_container->get_Name().to_string();
        QStringList      temp(QString::fromStdString(name));
        QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)nullptr, temp);
            current_node->setData(0,Qt::UserRole,qVariantFromValue(root_container));//store the pointer to the container in the Data field
        items.append(current_node);

        // Recursivity to add the children names
        set_container_names_rec(current_node, root_container, flg, false);
    }

    _nodes_original->insertTopLevelItems(0, items);

    _nodes_original->expandAll();

}


void Node_select::set_displayed_container_names(){

    const Container::Vector *root_containers = _trace->get_view_root_containers();
    if(root_containers->empty())root_containers = _trace->get_root_containers();

    if(root_containers->empty()) {
        *Message::get_instance() << tr("No containers in this trace").toStdString() << Message::ende;
        return;
    }

    _displayed_containers.clear();
    _nodes_displayed->clear();
    // Add the root container names
    QList<QTreeWidgetItem *> items;
    QFlags<Qt::ItemFlag>     flg= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate;
    for (const auto &root_container : *root_containers) {
        string           name = root_container->get_Name().to_string();
        QStringList      temp(QString::fromStdString(name));
        QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)nullptr, temp);

            current_node->setFlags(flg);
        current_node->setCheckState(0,Qt::Checked);
        current_node->setData(0,Qt::UserRole,qVariantFromValue(root_container));//store the pointer to the container in the Data field
        items.append(current_node);

        // Recursivity to add the children names
        set_container_names_rec(current_node, root_container, flg, true);
    }

    _nodes_displayed->insertTopLevelItems(0, items);

    _nodes_displayed->expandAll();

}





void Node_select::set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container, QFlags<Qt::ItemFlag> flg, bool load_view) {

    const Container::Vector *children;

    if(load_view==true)children = current_container->get_view_children();
    if(load_view==false || children->empty())children = current_container->get_children();
    //QFlags<Qt::ItemFlag>     flg= Qt::ItemIsDragEnabled;

    for (const auto &child : *children) {
        // We create the node and we do the recursivity
        string name = child->get_Name().to_string();
        QStringList temp(QString::fromStdString(name));
        QTreeWidgetItem *node = new QTreeWidgetItem(current_node, temp);

            node->setFlags(flg);
        node->setData(0,Qt::UserRole, qVariantFromValue(child));//store the pointer to the container in the Data field
                if(flg & Qt::ItemIsUserCheckable)node->setCheckState(0,Qt::Checked);
        set_container_names_rec(node ,child, flg, load_view);
    }
}

Trace* Node_select::get_trace() {
        return _trace;
}

void Node_select::set_trace(Trace *trace) {
    // Initialize _trace
    _trace = trace;
    // Fill the tree
    set_initial_container_names();
    set_displayed_container_names();
}
/*
void Stats_window::set_selected_nodes(string kind_of_state){
    const list<StateType *>          *states_types_list;
    list<StateType *>::const_iterator itstat;
    list<StateType *>::const_iterator endstat;
    const ContainerType              *kind_of_container = NULL;

    states_types_list = _trace->get_state_types();
    endstat = states_types_list->end();
    for (itstat  = states_types_list->begin();
         itstat != endstat;
         itstat++){
       if ((*itstat)->get_name().to_string() == kind_of_state){
         kind_of_container = (*itstat)->get_container_type();
         continue;
       }
    }

    // We delete the previous selected containers
    if(!_selected_containers.empty()) {
        _selected_containers.clear();
    }

    // We fill the new selected containers
    // TODO : Use the tree instead of the list
    QTreeWidgetItemIterator it(_nodes_selected);
    while (*it) {
        if ((*it)->checkState(0) == Qt::Checked){
            Container *cont = _trace->search_container((*it)->text(0).toStdString());
            //cout << ((ContainerType *)cont->get_type())->get_name().to_string() << " " << ((ContainerType *)kind_of_container)->get_name().to_string() << endl;
            if (cont->get_type() == kind_of_container)
              _selected_containers.push_back(cont);
        }
        it ++;
    }
    _number_of_selected_container = _selected_containers.size();

#ifdef STAT_DEBUG
    for(unsigned int i = 0 ; i < _selected_containers.size() ; i ++) {
        cout << _selected_containers[i]->get_name().to_string() << endl;
    }
#endif
}

*/

void Node_select::on_reset_button_clicked(){
    //resets to initial containers,
    _displayed_containers.clear();
    QList<QTreeWidgetItem *> items;
    QTreeWidgetItemIterator it(_nodes_original);//browse only top containers
    while (*it) {
        if((*it)->parent()==nullptr){//we only want parent nodes
            _displayed_containers.push_back(((*it)->data(0,Qt::UserRole)).value<Container*>());
            reassign_children_rec(*it,  nullptr);
        }
        ++it;

    }

    _nodes_displayed->clear();
    //can we load that from _nodes_original instead ?
    set_displayed_container_names();
    Element_pos zoom[2]={Info::Render::_x_min, Info::Render::_x_max};
    Info::Render::_x_min_visible = 0.0;
    Info::Render::_x_max_visible = 0.0;
    _trace->set_view_root_containers(_displayed_containers);
    //_trace->set_selected_container(_trace->get_root_containers());
    _console->get_console()->launch_action(Core:: _STATE_CLEAN_RENDER_AREA);
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    _trace->updateTrace(new Interval(zoom[0], zoom[1]));
#endif
    _console->get_console()->draw_trace(_console->get_filename(),Core::_DRAW_OPENGL);
    _console->get_console()->launch_action(Core:: _STATE_ZOOM_IN_AN_INTERVAL, &zoom);
    _console->get_console()->launch_action(Core:: _STATE_RENDER_UPDATE);
}



void Node_select::on_load_button_clicked(){
    //load an xml file and appply the structure described inside to the trace
     const QString path_by_default = QString(_file_viewed.substr(0, _file_viewed.find_last_of('.')).c_str()) + ".xml";

    QString filename = QFileDialog::getOpenFileName(this, tr("Import File"),
                                                    path_by_default,
                                                    tr("Documents XML (*.xml)"));

    if (filename.isEmpty()) {
        *Message::get_instance() << tr("No File Selected").toStdString() << Message::ende;
        return ;
    }
    else {
        // Adding .svg to the end
        if(!filename.endsWith(".xml")) {
            filename += ".xml";
        }
    }

        _trace->load_config_from_xml(filename); // we set the view_trace, but not our representation, we have to parse it again

        set_displayed_container_names();


}


void Node_select::on_display_button_clicked(){
    //take the new list and apply it to the trace
    // if not checked, remove container
    // if position has changed, set to the new position

    //we need to be recursive again, and to avoid problems with similar names


        /*
        //this only works to select containers, we cannot order them by this way
        _displayed_containers.clear();
         QTreeWidgetItemIterator it(_nodes_displayed, QTreeWidgetItemIterator::Checked);
     while (*it) {
         _displayed_containers.push_back(((*it)->data(0,Qt::UserRole)).value<Container*>());
         ++it;
     }
    _trace->set_selected_container(&_displayed_containers);*/

        //use _view_root containers and _view_children in order not to modify the actual structure and limit modifications to do in drawtrace
    _displayed_containers.clear();
    build_displayed_nodes(_displayed_containers);
    _trace->set_view_root_containers(_displayed_containers);
    Element_pos it[2]={Info::Render::_x_min, Info::Render::_x_max};
    //Info::Render::_x_min_visible = 0.0;
    //Info::Render::_x_max_visible = 0.0;
    _console->get_console()->launch_action(Core:: _STATE_CLEAN_RENDER_AREA);
    #if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    _trace->updateTrace(new Interval(it[0], it[1]));
    #endif
    _console->get_console()->draw_trace(_console->get_filename(),Core::_DRAW_OPENGL);

    _console->get_console()->launch_action(Core:: _STATE_ZOOM_IN_AN_INTERVAL, &it);
    _console->get_console()->launch_action(Core:: _STATE_RENDER_UPDATE);
}


void Node_select::build_displayed_nodes( std::list<Container *>& displayed_containers){
    QTreeWidgetItemIterator it(_nodes_displayed, QTreeWidgetItemIterator::Checked);//browse only top containers
    while (*it) {
        if((*it)->parent()==nullptr){//we only want parent nodes
            displayed_containers.push_back(((*it)->data(0,Qt::UserRole)).value<Container*>());
            QFlags<Qt::CheckState> flg(Qt::Checked|Qt::PartiallyChecked);
            reassign_children_rec(*it,flg);
        }
        ++it;
     }
}


void Node_select::reassign_children_rec(QTreeWidgetItem* item,  QFlags<Qt::CheckState> flg){
    //browse all childs of a container, add him and recursively all its children to the view we want
    item->data(0,Qt::UserRole).value<Container*>()->clear_view_children();
    for(int i=0; i<item->childCount(); i++){
         if(flg==0 || (item->child(i)->checkState(0)& flg) ){
            item->data(0,Qt::UserRole).value<Container*>()->add_view_child(item->child(i)->data(0,Qt::UserRole).value<Container*>());
            reassign_children_rec(item->child(i), flg);
        }

    }

}


void Node_select::set_filename(string filename){
    _file_viewed = std::move(filename);
}


void Node_select::on_export_button_clicked(){
 //export the trees to a XML file, in order to allow easy reusing

    const QString path_by_default = QString(_file_viewed.substr(0, _file_viewed.find_last_of('.')).c_str()) + ".xml";

    QString filename = QFileDialog::getSaveFileName(this, tr("Export File"),
                                                    path_by_default,
                                                    tr("Documents XML (*.xml)"));

    if (filename.isEmpty()) {
        *Message::get_instance() << tr("You must select a name for the file").toStdString() << Message::ende;
        return ;
    }
    else {
        // Adding .svg to the end
        if(!filename.endsWith(".xml")) {
            filename += ".xml";
        }
    }

    QDomDocument doc( "Subset " );
    QDomElement root = doc.createElement( "nodes" );
    doc.appendChild( root );

    //go through all elements of _nodes_displayed
         QTreeWidgetItemIterator it(_nodes_displayed);//browse only top containers

     while (*it) {
        if((*it)->parent()==nullptr){//we only want parent nodes

            QDomElement rootElement = doc.createElement( "rootNode" );
            rootElement.setAttribute( "name", QString((*it)->data(0,Qt::UserRole).value<Container*>()->get_Name().to_string().c_str()) );

            export_children_rec(*it, rootElement, doc);
            root.appendChild( rootElement );
        }
        ++it;

     }

    QFile file( filename );
    if( !file.open( QIODevice::WriteOnly) ){
                *Message::get_instance() << tr("Error while opening the file").toStdString() << Message::ende;
                return ;
        }

    QTextStream ts( &file );
    ts << doc.toString();

    file.close();





}


void Node_select::on_sort_button_clicked(){
    //sort_order : 0 for high to low, 1 for low to high
    _sort_order=!_sort_order;
    sort_button->setText(_sort_order?"Sort >":"Sort <");
    _nodes_displayed->sortItems(0,_sort_order?Qt::AscendingOrder:Qt::DescendingOrder);

}


void Node_select::export_children_rec(QTreeWidgetItem* item, QDomElement& parent, QDomDocument& doc){
    for(int i=0; i<item->childCount(); i++){
        if((item->child(i)->checkState(0)& (Qt::Checked|Qt::PartiallyChecked)) ){
            QDomElement element = doc.createElement( "Node" );
            element.setAttribute( "name", QString(item->child(i)->data(0,Qt::UserRole).value<Container*>()->get_Name().to_string().c_str()) );
            parent.appendChild( element );
            export_children_rec(item->child(i), element, doc);

        }

    }

}


void Node_select::close_window(){
    hide();
}




void Node_select::keyPressEvent(QKeyEvent *) {
  /*  switch (event->key()) {
    case Qt::Key_Left:
        // Key 'left' pressed.
        if(_x_translated > 0) {
            _ui_stats_area->translate_x(--_x_translated);
            x_scroll->setSliderPosition(_x_translated);
        }
        break;
    case Qt::Key_Right:
        // Key 'right' pressed.
        if(_x_translated < 99) {
            _ui_stats_area->translate_x(++_x_translated);
            x_scroll->setSliderPosition(_x_translated);
        }
        break;
    case Qt::Key_Up:
        // Key 'up' pressed.
        if(_y_translated > 0) {
            _ui_stats_area->translate_y(--_y_translated);
            y_scroll->setSliderPosition(_y_translated);
        }
        break;
    case Qt::Key_Down:
        // Key 'down' pressed.
        if(_y_translated < 99) {
            _ui_stats_area->translate_y(++_y_translated);
             y_scroll->setSliderPosition(_y_translated);
       }
        break;
    case Qt::Key_PageUp:
        // Key 'Page Up' pressed.
        _ui_stats_area->translate_y(0);
        y_scroll->setSliderPosition(0);
        break;
    case Qt::Key_PageDown:
        // Key 'Page Down' pressed.
        _ui_stats_area->translate_y(100);
        y_scroll->setSliderPosition(100);
        break;
    default:

        break;
    }

    event->accept();*/
}
/*
void Stats_window::init() {
    // We set the names of the containers for the tree widget
    _nodes_selected->clear();
    set_container_names();

    // We init the times
    _start_time = Info::Render::_x_min_visible;
    _end_time   = Info::Render::_x_max_visible;

    QString temp;
    temp.setNum(_start_time);
    _start_time_widget->setText(temp);
    temp.setNum(_end_time);
    _end_time_widget->setText(temp);

    _ui_stats_area->clear();
    Reinit_scroll_bars();
}

void Stats_window::clear() {
    _ui_stats_area->makeCurrent();
    _nodes_selected->clear();
    _ui_stats_area->clear();
    _kind_of_state_box->clear();
    Reinit_scroll_bars();
    _ui_stats_area->doneCurrent();
}

void Stats_window::set_arguments(std::map<std::string , QVariant *>) {
}

void Stats_window::execute() {
       on_reload_button_clicked();
}

string Stats_window::get_name() {
    return "Statistics window";
}*/
