#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 <QGLWidget>
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
#include "interface/resource.hpp"
#include "interface/Interface.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/tree/Interval.hpp"
/* -- */
#include "render/render_stats.hpp"
#include "render/render_stats_opengl.hpp"
#include "render/render_stats_svg.hpp"
/* -- */
#include "statistics/Statistic.hpp"
#include "statistics/DrawStats.hpp"
#include "statistics/DrawVDiagram.hpp"
#include "statistics/DrawHDiagram.hpp"
#include "statistics/DrawCounter.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(QtCharts::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, SIGNAL(chart_focused()), this, SLOT(auto_update_stats()));

    QMetaObject::connectSlotsByName(nullptr);
}

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::ItemIsTristate;

    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::ItemIsTristate;

    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 "Qt Statistics window";
}

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(){
    int     kind_of_diagram = _kind_of_diagram_box->currentIndex();
    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(_file_viewed.substr(0, _file_viewed.find_last_of('.')).c_str()) + ".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(".svg")) {
            filename += ".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(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();
}

