/**
 *
 * @file src/render/Render_svg.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 Olivier Lagrasse
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
/*!
 *\file Render_svg.cpp
 */

#include <QObject>
/* -- */
#include <fstream>
#include <sstream>
#include <cmath>
/* -- */
#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 "trace/values/Value.hpp"
#include "trace/values/Color.hpp"
#include "trace/EntityValue.hpp"
/* -- */
#include "core/Core.hpp"
/* -- */

using namespace std;

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

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

Render_svg::Render_svg(const std::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;
    }
    _red = 0.0;
    _green = 0.0;
    _blue = 0.0;
}

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) {
        const 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() {

    const Element_pos graduation_diff = Ruler::get_graduation_diff(render_to_trace_x(0), render_to_trace_x(Info::Render::width));
    const std::string common_part_string = Ruler::get_common_part_string(Info::Render::_x_min_visible, Info::Render::_x_max_visible);
    const std::size_t length_after_point = max(0, -int(floor(log10(graduation_diff))));

    _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, common_part_string, length_after_point)
                << "</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() {
}
