/**
 *
 * @file src/interface/Node_select.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 Philippe Swartvagher
 * @author Augustin Degomme
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
#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/Interface_graphic.hpp"
#include "interface/resource.hpp"
/* -- */
#include "core/Core.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 <QDomDocument>
#include <QDomElement>
#include <QTextStream>
#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), _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 = _console->get_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, QVariant::fromValue(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 = _console->get_trace()->get_view_root_containers();

    if (root_containers->empty()) {
        root_containers = _console->get_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::ItemIsAutoTristate;
    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, QVariant::fromValue(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 = NULL;

    if (load_view == true) {
        children = current_container->get_view_children();
    }

    /* children cannot be NULL when accessed, because it means load_view==true
     * and thus, children was assigned in the above condition block. */
    if (load_view == false || children->empty()) {
        children = current_container->get_children();
    }

    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, QVariant::fromValue(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);
    }
}

void Node_select::init_window() {
    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 = _console->get_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 = _console->get_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();
    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 *>());
#if defined(HAVE_QT5_15)
            reassign_children_rec(*it, QFlags<Qt::CheckState>(Qt::Unchecked));
#else
            reassign_children_rec(*it, 0);
#endif
        }
        ++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;
    _console->get_trace()->set_view_root_containers(_displayed_containers);
    //_console->get_trace()->set_selected_container(_console->get_trace()->get_root_containers());
    _console->render_area_clean();
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    _console->get_trace()->updateTrace(new Interval(zoom[0], zoom[1]));
#endif
    _console->get_console()->draw_trace(_console->get_trace());
    _console->render_zoom_on_interval(zoom[0], zoom[1]);
    _console->get_render_area()->update_render();
}

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::fromStdString(_file_viewed.substr(0, _file_viewed.find_last_of('.'))) + QStringLiteral(".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(QLatin1String(".xml"))) {
            filename += QLatin1String(".xml");
        }
    }

    _console->get_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;
 }
_console->get_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);
    _console->get_trace()->set_view_root_containers(_displayed_containers);
    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;
    _console->render_area_clean();
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    _console->get_trace()->updateTrace(new Interval(zoom[0], zoom[1]));
#endif
    _console->get_console()->draw_trace(_console->get_trace());

    _console->render_zoom_on_interval(zoom[0], zoom[1]);
    _console->get_render_area()->update_render();
}

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::fromStdString(_file_viewed.substr(0, _file_viewed.find_last_of('.'))) + QStringLiteral(".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(QLatin1String(".xml"))) {
            filename += QLatin1String(".xml");
        }
    }

    QDomDocument doc(QStringLiteral("Subset "));
    QDomElement root = doc.createElement(QStringLiteral("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(QStringLiteral("rootNode"));
            rootElement.setAttribute(QStringLiteral("name"), QString::fromStdString((*it)->data(0, Qt::UserRole).value<Container *>()->get_Name().to_string()));

            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 ? QStringLiteral("Sort >") : QStringLiteral("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(QStringLiteral("Node"));
            element.setAttribute(QStringLiteral("name"), QString::fromStdString(item->child(i)->data(0, Qt::UserRole).value<Container *>()->get_Name().to_string()));
            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";
}*/

#include "moc_Node_select.cpp"
