/*
** 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 <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/Stats_window.hpp"
/* -- */
using namespace std;

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

    CKFP(_ui_stats_area_layout = this->findChild<QVBoxLayout *>("stats_area"), QObject::tr("Cannot find the stats_area_layout in the .ui file").toStdString());

    _ui_stats_area = new Render_stats_opengl(this);
    _ui_stats_area_layout->addWidget(_ui_stats_area);

    _x_translated = 0;
    _y_translated = 0;

    _auto_reload_when_zoom = false;

    QMetaObject::connectSlotsByName(NULL);
}

Stats_window::~Stats_window() {
    delete _ui_stats_area;
}

void Stats_window::set_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;
    }
    // Add the root container names
    QList<QTreeWidgetItem *> items;
    QFlags<Qt::ItemFlag>     flg=Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate;

    for (Container::VectorIt it = root_containers->begin();
         it != root_containers->end();
         it ++) {
        string           name = (*it)->get_name().to_string();
        QStringList      temp(QString::fromStdString(name));
        QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)0, temp);
	
	current_node->setFlags(flg);
	current_node->setCheckState(0,Qt::Checked);
        items.append(current_node);
        
        // Recursivity to add the children names
        set_container_names_rec(current_node, (*it));
    }

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

void Stats_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 (Container::VectorIt it = children->begin();
         it != children->end();
         it ++) {
        // We create the node and we do the recursivity
        string name = (*it)->get_name().to_string();
        QStringList temp(QString::fromStdString(name));
        QTreeWidgetItem *node = new QTreeWidgetItem(current_node, temp);
	node->setFlags(flg);
	node->setCheckState(0,Qt::Checked);
        set_container_names_rec(node ,(*it));
    }
}

void Stats_window::set_filename(string filename){
    _file_viewed = filename;
}


void Stats_window::set_trace(Trace *trace) {
    const map<Name, StateType *> *states_types_list;
    map<Name, StateType *>::const_iterator it;
    map<Name, StateType *>::const_iterator end;

    // Initialize _trace
    _trace = trace;
    
    // Clear the boxes
    clear();
    
    // Fill the tree
    set_container_names();



    // Fill in the combobox
    states_types_list = _trace->get_state_types();
    end = states_types_list->end();
    for (it  = states_types_list->begin();
         it != end;
         it++){
        string name = (*it).second->get_name().to_string();
        QString temp(QString::fromStdString(name));
        _kind_of_state_box->addItem(temp);
    }
}

void Stats_window::set_selected_nodes(string kind_of_state){
    const map<Name, StateType *>          *states_types_list;
    map<Name, StateType *>::const_iterator itstat;
    map<Name, 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).second->get_name().to_string() == kind_of_state){
	 kind_of_container = (*itstat).second->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 Stats_window::on_reload_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 view the stats").toStdString() << Message::ende;
        return ;
    }

    Reinit_scroll_bars();

    // We get the times
    _start_time = _start_time_widget->text().toDouble();
    _end_time   = _end_time_widget->text().toDouble();

    // We create a drawer
    DrawStats<Render_stats_opengl> *drawer;
    switch (kind_of_diagram) {
    case _HDIAGRAM_INDEX:
	drawer = new DrawHDiagram<Render_stats_opengl>();
	break;
    case _DIAGRAM_INDEX:
	drawer = new DrawVDiagram<Render_stats_opengl>();
	break;
    case _COUNTER_INDEX:
	drawer = new DrawCounter<Render_stats_opengl>();
        break;
    default:
	*Message::get_instance() << _kind_of_diagram_box->currentText().toStdString() 
				 << tr(" not yet implemented").toStdString() << Message::endw;
        drawer = new DrawHDiagram<Render_stats_opengl>();
    }
    drawer->set_times(_start_time, _end_time);
    drawer->build(_ui_stats_area, _selected_containers);
    _ui_stats_area->updateGL();

    delete drawer;
}

void Stats_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 ;
    }

    Reinit_scroll_bars();

    // We get the times
    _start_time = _start_time_widget->text().toDouble();
    _end_time   = _end_time_widget->text().toDouble();
    
    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 create a drawer
    Render_stats_svg svg(filename.toStdString());
    DrawStats<Render_stats_svg> *drawer;
    switch (kind_of_diagram) {
    case _HDIAGRAM_INDEX:
        drawer = new DrawHDiagram<Render_stats_svg>();
        break;
    case _DIAGRAM_INDEX:
        drawer = new DrawVDiagram<Render_stats_svg>();
        break;
    case _COUNTER_INDEX:
        drawer = new DrawCounter<Render_stats_svg>();
        break;
    default:
        *Message::get_instance() << _kind_of_diagram_box->currentText().toStdString() 
                                 << tr(" not yet implemented").toStdString() << Message::endw;
        drawer = new DrawHDiagram<Render_stats_svg>();
    }
    drawer->set_times(_start_time, _end_time);
    drawer->build(&svg, _selected_containers);
    
    delete drawer;

}

void Stats_window::close_window(){
    _ui_stats_area->clear();
    _ui_stats_area->doneCurrent();
    hide();
}

void Stats_window::auto_update_stats() {
    if(!_auto_reload_when_zoom)
        return;
    // We update the times on the text bars
    _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);

    // We reload the stats
    on_reload_button_clicked();
}

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

// new_value is between 0 and 99
void Stats_window::on_y_scroll_valueChanged(int new_value) {
    _ui_stats_area->translate_y(new_value);
    _y_translated = new_value;
}

// new_value is between 0 and 99
void Stats_window::on_x_scroll_valueChanged(int new_value) {
    _ui_stats_area->translate_x(new_value);
    _x_translated = new_value;
}


void Stats_window::Reinit_scroll_bars() {
    _ui_stats_area->translate_x(0);
    _ui_stats_area->translate_y(0);
    x_scroll->setSliderPosition(0);
    y_scroll->setSliderPosition(0);
}

void Stats_window::keyPressEvent(QKeyEvent *event) {
    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:
	/*
	 * Unknown key pressed.
	 */
	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 /*argname*/, QVariant * /*argValue*/>) {
}

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

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