/**
 *
 * @file src/statistics/Statistics_window.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Luca Bourroux
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <list>
#include <stack>
#include <vector>
/* -- */
#include <QFileDialog> // To choose the file to save
#include <QKeyEvent>
#include <QGraphicsLayout>
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
#include "interface/resource.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/tree/Interval.hpp"
/* -- */
#include "statistics/Statistic.hpp"
#include "statistics/Statistics_window.hpp"
/* -- */

#include <QtWidgets/qlabel.h>

#include <QtSvg/QSvgGenerator>

#include "Diagram.hpp"

Statistics_window::Statistics_window(QWidget *parent) :
    Plugin(parent) {
    setupUi(this);

    _qt_chart = new QChart();
    _qt_chart->layout()->setContentsMargins(0, 0, 0, 0);
    _qt_chart->setBackgroundRoundness(0);
    _qt_chart->setTheme(QChart::ChartThemeDark);

    _qt_chart_view = new Chart_View(*_x_scroll, *_y_scroll, _qt_chart);
    _qt_chart_view->setRenderHint(QPainter::Antialiasing);
    _qt_chart_view->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
    _qt_chart_view->setDragMode(QGraphicsView::ScrollHandDrag);

    _chart_layout->addWidget(_qt_chart_view);

    connect(_qt_chart_view, &Chart_View::chart_focused, this, &Statistics_window::auto_update_stats);

    QMetaObject::connectSlotsByName(nullptr);

    _auto_reload_when_zoom = true;
}

void Statistics_window::set_trace(Trace *trace) {
    _trace = trace;
    clear();

    if (!_trace)
        return;

    set_container_names();
    auto &states_types_list = *_trace->get_state_types();

    // we populate the combo box that displays all the states type.
    for (auto &it: states_types_list) {
        auto std_str = it.second->get_alias();
        auto qt_str = QString::fromStdString(std_str);
        _kind_of_state_box->addItem(qt_str);
    }
}

void Statistics_window::set_container_names() {
    auto root_containers = _trace->get_root_containers();

    if (root_containers->empty()) {
        auto &instance = *Message::get_instance();
        instance << tr("No containers in this trace").toStdString() << Message::ende;
        return;
    }
    // Add the root container names
    QList<QTreeWidgetItem *> items;
    QFlags<Qt::ItemFlag> flg = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate;

    for (const auto &root_container: *root_containers) {
        std::string name = root_container->get_name();
        std::string alias = root_container->get_alias();
        QStringList str_name(QString::fromStdString(name));
        QString str_alias(QString::fromStdString(alias));
        QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)nullptr, str_name);

        current_node->setFlags(flg);
        current_node->setCheckState(0, Qt::Checked);
        current_node->setText(1, str_alias);
        items.append(current_node);

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

    (*(items.begin()))->setExpanded(true);
    _nodes_selected->insertTopLevelItems(0, items);
}

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

void Statistics_window::set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container) {
    const Container::Vector *children = current_container->get_children();
    QFlags<Qt::ItemFlag> flg = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate;

    for (const auto &it: *children) {
        // We create the node and we do the recursivity
        std::string name = it->get_name();
        std::string alias = it->get_alias();
        QStringList str_name(QString::fromStdString(name));
        QString str_alias(QString::fromStdString(alias));
        QTreeWidgetItem *node = new QTreeWidgetItem(current_node, str_name);
        node->setFlags(flg);
        node->setCheckState(0, Qt::Checked);
        node->setText(1, str_alias);
        set_container_names_rec(node, it);
    }
}

void Statistics_window::clear() {
    _nodes_selected->clear();
    _kind_of_state_box->clear();
}

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

void Statistics_window::init() {
    // We set the names of the containers for the tree widget
    _nodes_selected->clear();
    if (_trace)
        set_container_names();

    _end_time_widget->setValue(100);
}

void Statistics_window::set_arguments(std::map<std::string /*argname*/, QVariant * /*argValue*/>) {
}

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

std::string Statistics_window::get_name() {
    return "Statistics";
}

void Statistics_window::auto_update_stats() {
    if (!_auto_reload_when_zoom)
        return;

    // We update the times on the text bars
    // We reload the stats
    on_reload_button_clicked();
}

void Statistics_window::on_auto_reload_box_stateChanged(int) {
    _auto_reload_when_zoom = auto_reload_box->isChecked();
}

void Statistics_window::on_reload_button_clicked() {

    auto kind_of_diagram = _kind_of_diagram_box->currentIndex();
    auto kind_of_state = _kind_of_state_box->currentText();

    // This will fill _selected_containers based on wether or not every container in the tree
    // list in the ui is checked or not and is of the right type(matching with the combobox).
    set_selected_nodes(kind_of_state.toStdString());

    if (_selected_containers.empty()) {
        *Message::get_instance()
            << tr("You must select at least one container to view the stats").toStdString()
            << Message::ende;
        return;
    }

    auto range = (Info::Render::_x_max_visible - Info::Render::_x_min_visible);
    auto start_time = Info::Render::_x_min_visible + _start_time_widget->value() * range / 100;
    auto end_time = Info::Render::_x_min_visible + _end_time_widget->value() * range / 100;

    auto interval = Interval { start_time, end_time };

    // Diagram::xxxxx(...) will populate the QChart.

    _qt_chart_view->reset_scroll_zoom();
    switch (kind_of_diagram) {
    case Diagram_Type::Horizontal: {
        _qt_chart_view->restrict_scroll(false, true);
        if (_stacked_checked)
            Diagram::stacked_horizontal(*_qt_chart, _selected_containers, interval);
        else
            Diagram::horizontal(*_qt_chart, _selected_containers, interval);
        break;
    }
    case Diagram_Type::Vertical: {
        _qt_chart_view->restrict_scroll(true, false);
        if (_stacked_checked)
            Diagram::stacked_vertical(*_qt_chart, _selected_containers, interval);
        else
            Diagram::vertical(*_qt_chart, _selected_containers, interval);
        break;
    }
    case Diagram_Type::Counter: {
        _qt_chart_view->restrict_scroll(true, true);
        Diagram::counter(*_qt_chart, _selected_containers, interval);
        break;
    }
    }

    _qt_chart->legend()->setVisible(true);
    _qt_chart->legend()->setAlignment(Qt::AlignBottom);
}

void Statistics_window::on_export_button_clicked() {
    QString kind_of_state = _kind_of_state_box->currentText();

    set_selected_nodes(kind_of_state.toStdString());

    if (_selected_containers.empty()) {
        *Message::get_instance()
            << tr("You must select at least one container to export the stats").toStdString()
            << Message::ende;
        return;
    }

    const QString path_by_default = QString::fromStdString(_file_viewed.substr(0, _file_viewed.find_last_of('.'))) + QStringLiteral(".svg");

    QString filename = QFileDialog::getSaveFileName(this, tr("Export File"),
                                                    path_by_default,
                                                    tr("Images (*.svg)"));

    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(".svg"))) {
            filename += QLatin1String(".svg");
        }
    }

    // We don't need to have a switch case for every possible type of render here because
    // this will export _whatever_ is currently on screen in a .svg
    QSvgGenerator generator;
    generator.setFileName(filename);
    generator.setSize(_qt_chart_view->size());
    generator.setViewBox(_qt_chart_view->rect());

    QPainter painter;
    painter.begin(&generator);
    _qt_chart_view->render(&painter);
    painter.end();
}

void Statistics_window::set_selected_nodes(const std::string &kind_of_state) {
    const ContainerType *kind_of_container = nullptr;

    auto &states_types_list = *_trace->get_state_types();
    for (auto &it: states_types_list) {
        if (it.second->get_alias() == kind_of_state) {
            kind_of_container = it.second->get_container_type();
        }
    }

    // We delete the previous selected containers
    _selected_containers.clear();

    // We fill the new selected containers
    // TODO : Use the tree instead of the list
    QTreeWidgetItemIterator it(_nodes_selected);
    for (; *it; it++) {
        if ((*it)->checkState(0) == Qt::Checked) {
            auto str = (*it)->text(1).toStdString();

            Container *cont = _trace->search_container(std::move(str));
            if (!cont) {
                continue;
            }
            if (cont->get_type() == kind_of_container) {
                _selected_containers.push_back(cont);
            }
        }
    }
    _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_alias() << endl;
    }
#endif
}

void Statistics_window::on_want_stacked_stateChanged(int x) {
    _stacked_checked = x == Qt::Checked;
    on_reload_button_clicked();
}

#include "moc_Statistics_window.cpp"
