/*
** 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
**
*/
/*!
 *\file Render_svg.cpp
 */

#include <QObject>
/* -- */
#include <fstream>
#include <sstream>
#include <cmath>
/* -- */
#include "interface/Interface.hpp"
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
/* -- */
#include "render/Geometry.hpp"
#include "render/Render.hpp"
#include "render/Render_svg.hpp"
#include "render/Ruler.hpp"
/* -- */
#include "core/Core.hpp"
/* -- */

using namespace std;


#define message *Message::get_instance() << "(" << __FILE__ << " l." << __LINE__ << "): "


/***********************************
 *
 *
 *
 * Constructor and destructor.
 *
 *
 *
 **********************************/

Render_svg::Render_svg(string* output_filename){

    _output_file.open(output_filename->c_str(), ofstream::out | ofstream::trunc);

    if (!_output_file.is_open()) {
        *Message::get_instance() << QObject::tr("Unable to open ").toStdString() << output_filename << Message::ende;
    }
}


Render_svg::~Render_svg(){

   if (_output_file.is_open())
       _output_file.close();
}



void Render_svg::set_color(float r, float g, float b){
    _red  = r;
    _green = g;
    _blue = b;
}

void Render_svg::draw_text(const Element_pos x, const Element_pos y, const Element_pos , const std::string s){
    _buffer.str("");
    _buffer << "<text x=\"" << render_to_screen_x(x) <<"\""
            << " y=\"" << render_to_screen_y(y) << "\""
            << " font-size=\"10\""
            << " fill=\"white\">"
            << s
            << "</text>\n";

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}


void Render_svg::draw_quad(Element_pos x, Element_pos y, Element_pos , Element_pos w, Element_pos h){

    if (render_to_screen_x(w) < 0.1)/* less than 0.1 pixel */
        return;

    _buffer.str("");
    _buffer  << "<rect"
             << " x=\""      << render_to_screen_x(x) << "\""
             << " y=\""      << render_to_screen_y(y) << "\""
             << " width=\""  << render_to_screen_x(w) << "\""
             << " height=\"" << render_to_screen_y(h) << "\""
             << " fill=\"rgb(" << floor(_red*256) << "," << floor(_green*256) << "," << floor(_blue*256) << ")\""/* TODO: choice better scale than '*256' */
             << " style=\"stroke:white;stroke-width:0.05\""
             << " />\n";

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::draw_triangle(Element_pos x, Element_pos y,
                               Element_pos , Element_pos r){
    _buffer.str("");
    _buffer  << "<polygon points=\""
             << render_to_screen_x(x-1.2) << "," << render_to_screen_y(y-1.0) << ","
             << render_to_screen_x(x) << "," << render_to_screen_y(y) << ","
             << render_to_screen_x(x-1.2) << "," << render_to_screen_y(y+1.0) << "\""
             << " transform = \"rotate("<< r << " " << render_to_screen_x(x) << " " << render_to_screen_y(y) << " )\""
             << " fill=\"rgb(" << floor(_red*256) << "," << floor(_green*256) << "," << floor(_blue*256) << ")\""/* TODO: choice better scale than '*256' */
             << "/>\n";

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::draw_line(Element_pos x1, Element_pos y1, Element_pos x2, Element_pos y2, Element_pos ){
    _buffer.str("");
    _buffer << "<line"
            << " x1=\"" << render_to_screen_x(x1) << "\""
            << " y1=\"" << render_to_screen_y(y1) << "\""
            << " x2=\"" << render_to_screen_x(x2) << "\""
            << " y2=\"" << render_to_screen_y(y2) << "\""
            << " stroke=\"rgb(" << floor(_red*256) << "," << floor(_green*256) << "," << floor(_blue*256) << ")\""/* TODO: choice better scale than '*256' */
            << "/>\n";

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::draw_circle(Element_pos x, Element_pos y, Element_pos , Element_pos r){
    _buffer.str("");
    _buffer << "<circle"
            << " cx=\"" << render_to_screen_x(x) << "\""
            << " cy=\"" << render_to_screen_y(y) << "\""
            << " r=\"" << max(render_to_screen_x(r), render_to_screen_y(r)) << "\""
            << " fill=\"rgb(" << floor(_red*256) << "," << floor(_green*256) << "," << floor(_blue*256) << ")\""/* TODO: choice better scale than '*256' */
            << " stroke=\"rgb(" << floor((1.0-_red)*256) << "," << floor((1.0-_green)*256) << "," << floor((1.0-_blue)*256) << ")\""/* TODO: choice better scale than '*256' */
            << "/>\n";

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}


void Render_svg::start_draw(){
    _buffer.str("");
    _buffer << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n"
            <<  "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"  \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
            <<      "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" << Info::Screen::width << "\" height=\"" << Info::Screen::height << "\" >\n"
            << "<rect "
            << " x=\"0\" y=\"0\""
            << " width=\"" << Info::Screen::width << "\" height=\"" << Info::Screen::height << "\""
            << " fill=\"rgb(128, 128, 150)\""
            << " />\n"
            << "<clipPath id=\"stateArea\">"/* Add a clipping area for state, arrow, event and counter */
            << "<path d = \"M "<< render_to_screen_x(_default_entity_x_translate) << " " << render_to_screen_y(9)
            << "L " << render_to_screen_x(_default_entity_x_translate) << " " << Info::Screen::height
            << "L " << Info::Screen::width << " " << Info::Screen::height
            << "L " << Info::Screen::width << " " << render_to_screen_y(9) << " Z\"/>"
            << "</clipPath>"
            << "<g clip-path=\"url(#stateArea)\">";/* Start the group which will be clipped */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::start_draw_containers(){
    _buffer.str("");
    _buffer << "</g>";/* Stop clipping */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::draw_container(const Element_pos , const Element_pos , const Element_pos , const Element_pos ){
}

void Render_svg::draw_container_text(const Element_pos , const Element_pos , const std::string &){
}

void Render_svg::end_draw_containers(){
    _buffer.str("");
    _buffer << "<g clip-path=\"url(#stateArea)\">";/* Start again clipping */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::start_draw_states(){

}

void Render_svg::draw_state(const Element_pos x, const Element_pos y, const Element_pos z, const Element_pos w, const Element_pos h, EntityValue* v){
    if(v){
        Color *c = v->get_file_color();
        if(c){
            set_color(c->get_red(), c->get_green(), c->get_blue());
        }
    }
    draw_quad(x, y, z, w, h);
}

void Render_svg::end_draw_states(){
}

void Render_svg::start_draw_arrows(){
}

void Render_svg::draw_arrow(const Element_pos start_time,
                            const Element_pos end_time,
                            const Element_pos start_height,
                            const Element_pos end_height,
                            const Element_col r,
                            const Element_col g,
                            const Element_col b,
                            EntityValue *value)
{
    const Element_pos triangle_size = 2.0;
    const Element_pos coeff         = 180.0f/M_PI;
    Element_pos angle;
    (void)value;

    if (start_time != end_time)
        angle = atan2((end_height - start_height), (end_time - start_time))*coeff;
    else
        angle = 90;

    draw_triangle(end_time, end_height, triangle_size, angle);
    draw_line(start_time, start_height, end_time, end_height, _z_arrow);

    (void)r; (void)g; (void)b;
}

void Render_svg::end_draw_arrows(){
}

void Render_svg::start_draw_events(){
}

void Render_svg::draw_event(const Element_pos , const Element_pos ,  const Element_pos, EntityValue* ){
}

void Render_svg::end_draw_events(){
}

void Render_svg::start_draw_counter(){
}

void Render_svg::draw_counter(const Element_pos , const Element_pos ){
}

void Render_svg::end_draw_counter(){
}

void Render_svg::start_ruler(){
    _buffer.str("");
    _buffer << "</g>";/* Stop clipping */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::end_ruler(){
    Element_pos graduation_diff;
    Element_pos coeff_prefix;


    graduation_diff = Ruler::get_graduation_diff(Info::Render::_x_min_visible, Info::Render::_x_max_visible);
    coeff_prefix    = Ruler::get_coeff_for_common_prefix(Info::Render::_x_min_visible, Info::Render::_x_max_visible);

    _buffer.str("");/* flush the buffer */

    _buffer << "<text x=\"" << render_to_screen_x(trace_to_render_x(0)) <<"\""
            << " y=\""      << render_to_screen_y(3) << "\""
            << " font-size=\"" << 14 << "\""
            << " fill=\"white\">"
            << "min: " << (double)Info::Render::_x_min_visible
            << "</text>\n";

    _buffer << "<text x=\"" << render_to_screen_x(trace_to_render_x(Info::Render::_x_max_visible))- render_to_screen_x(trace_to_render_x(Info::Render::_x_min_visible)) <<"\""
            << " y=\""      << render_to_screen_y(3) << "\""
            << " font-size=\"" << 14 << "\""
            << " fill=\"white\">"
            << "max: " << (double)Info::Render::_x_max_visible
            << "</text>\n";


    for (Element_pos i = Info::Render::_x_min_visible ;
         i < Info::Render::_x_max_visible ;
         i+=graduation_diff){

        _buffer << "<text x=\"" << render_to_screen_x(trace_to_render_x(i - Info::Render::_x_min_visible)+1) <<"\""
                << " y=\""      << render_to_screen_y(8) << "\""
                << " font-size=\"" << 10 << "\""
                << " fill=\"white\">"
                << Ruler::get_variable_part(i, coeff_prefix, 2)
                << "</text>\n";
    }

    _buffer << "<g clip-path=\"url(#stateArea)\">";/* Start again clipping */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

void Render_svg::end_draw(){
    _buffer.str("");/* flush the buffer */
    _buffer << "</g>";/* Stop clipping */
    _buffer << "</svg>";/* Close the svg file */

    _output_file.write(_buffer.str().c_str(), _buffer.str().size());
}

/*!
 * \brief draws the vertical helper line
 */

void Render_svg::draw_vertical_line(){
}

/*!
 * \brief slot connected to the simple click event
 */
void Render_svg::update_vertical_line(){
}

void Render_svg::set_vertical_line(Element_pos l){
    (void)l;
}

void Render_svg::call_ruler(){
}
