/**
 *
 * @file src/common/Session.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 Philippe Swartvagher
 * @author Olivier Lagrasse
 * @author Augustin Degomme
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
/*!
 *\file Session.cpp
 */

#include <iostream>
/* -- */
#include <QByteArray>
#include <QStringList>
#include <QSettings>
#include <QColor>
#include <QMap>
#include <QVariant>
#include <QCoreApplication>
#include <QDir>
#include <list>
/* -- */
#include "common/common.hpp"
/* -- */
#include "trace/values/Value.hpp"
#include "trace/values/Color.hpp"
#include "common/Palette.hpp"
#include "common/Session.hpp"

using namespace std;

/*
 *
 * Minimap Settings
 *
 */
MinimapSettings::MinimapSettings() {
    // Initialize Minimap default
    _x = 0;
    _y = 0;
    _width = 200;
    _height = 150;
    _pen_size = 3;
    _pen_color = Qt::red;
    _brush_color = QColor::fromRgbF(1, 1, 0, 0.5);
    _is_closed = true;

    load();
}

MinimapSettings::~MinimapSettings() {
    save();
}

void MinimapSettings::load() {
    Session &s = Session::getSession();

    // Session::Minimap::is_closed = isHidden();
    if (s.contains(QStringLiteral("minimap/x"))) {
        _x = s.value(QStringLiteral("minimap/x")).value<int>();
        _y = s.value(QStringLiteral("minimap/y")).value<int>();
        _width = s.value(QStringLiteral("minimap/width")).value<int>();
        _height = s.value(QStringLiteral("minimap/height")).value<int>();
        _pen_size = s.value(QStringLiteral("minimap/pen_size")).value<int>();
        _pen_color = s.value(QStringLiteral("minimap/pen_color")).value<QColor>();
        _brush_color = s.value(QStringLiteral("minimap/brush_color")).value<QColor>();
        _is_closed = s.value(QStringLiteral("minimap/is_closed")).toBool();
    }
}

void MinimapSettings::save() {
    Session &s = Session::getSession();

    s.setValue(QStringLiteral("minimap/x"), _x);
    s.setValue(QStringLiteral("minimap/y"), _y);
    s.setValue(QStringLiteral("minimap/width"), _width);
    s.setValue(QStringLiteral("minimap/height"), _height);
    s.setValue(QStringLiteral("minimap/is_closed"), _is_closed);

    s.sync();
}

void MinimapSettings::save(int x, int y, int w, int h, bool hidden) {
    _x = x;
    _y = y;
    _width = w;
    _height = h;
    _is_closed = hidden;

    save();
}

/*
 *
 * Export Settings
 *
 */
ExportSettings::ExportSettings() {
    file_dialog_state = QByteArray::number(-1);
}

bool ExportSettings::is_default() {
    return file_dialog_state == QByteArray::number(-1);
}

/*
 *
 * Common Settings
 *
 */

// QSettings *Session::_settings    = NULL;
Session *Session::S = nullptr;
Palette *Session::_palettes_state = nullptr;
Palette *Session::_palettes_link = nullptr;
Palette *Session::_palettes_event = nullptr;
bool Session::_use_palettes = false;
bool Session::_use_palettes_is_set = false;

MinimapSettings *Session::_mmSettings = nullptr;
ExportSettings *Session::_exportSettings = nullptr;

Session::Session() :
    QSettings(QStringLiteral(VITE_ORGANISATION_NAME), QStringLiteral(VITE_APPLICATION_NAME)) {
    QCoreApplication::setOrganizationName(QStringLiteral(VITE_ORGANISATION_NAME));
    QCoreApplication::setOrganizationDomain(QStringLiteral(VITE_ORGANISATION_DOMAIN));
    QCoreApplication::setApplicationName(QStringLiteral(VITE_APPLICATION_NAME));
}

Session::~Session() {
    delete _palettes_event;
    delete _palettes_link;
    delete _palettes_state;
    delete _mmSettings;
    delete _exportSettings;
}

void Session::init() {
    _palettes_state = get_palette("palette", get_current_palette("palette"));
    _palettes_link = get_palette("link_types", get_current_palette("link_types"));
    _palettes_event = get_palette("event_types", get_current_palette("event_types"));

    _mmSettings = new MinimapSettings();
    _exportSettings = new ExportSettings();
}

const QStringList Session::get_recent_files() {
    return S->value(QStringLiteral(RECENT_FILES)).toStringList();
}

void Session::add_recent_file(const QString &filename) {
    QStringList files = S->value(QStringLiteral(RECENT_FILES)).toStringList();

    // We delete if the file was already call in order to be on the top of the list
    files.removeAll(filename);
    files.prepend(filename);
    while (files.size() > _MAX_NB_RECENT_FILES) {
        files.removeLast();
    }
    S->setValue(QStringLiteral(RECENT_FILES), files);
}

void Session::clear_recent_files() {
    S->remove(QStringLiteral(RECENT_FILES));
}

bool Session::get_vertical_line_setting() {
    // set 1 as the default value if not set in the file
    return S->value(QStringLiteral(VERTICAL_LINE), 1).toBool();
}

void Session::update_vertical_line_setting(bool b) {
    S->setValue(QStringLiteral(VERTICAL_LINE), b);
}

bool Session::get_hide_warnings_setting() {
    // set 0 as the default value if not set in the file
    return S->value(QStringLiteral(HIDE_WARNINGS), 0).toBool();
}

void Session::update_hide_warnings_settings(bool b) {
    S->setValue(QStringLiteral(HIDE_WARNINGS), b);
}

bool Session::get_shaded_states_setting() {
    // set 1 as the default value if not set in the file
    return S->value(QStringLiteral(SHADED_STATE), 1).toBool();
}

void Session::update_shaded_states_setting(bool b) {
    S->setValue(QStringLiteral(SHADED_STATE), b);
}

bool Session::get_reload_type_setting() {
    // set 1 as the default value if not set in the file
    return S->value(QStringLiteral(RELOAD_TYPE), 0).toBool();
}

void Session::update_reload_type_setting(bool b) {
    S->setValue(QStringLiteral(RELOAD_TYPE), b);
}

/*** Plugins ***/
void Session::load_plugin_directories(QStringList &list) {
    list = S->value(QStringLiteral(PLUGIN_DIR)).toStringList();

    /* We add the $HOME/.vite if not exists as a default path */
    const QString home_dir = QDir::toNativeSeparators(QDir::homePath() + QStringLiteral("/.vite"));
    if (!list.contains(home_dir)) {
        list.append(home_dir);
    }
}

void Session::save_plugin_directories(const QStringList &list) {
    S->setValue(QStringLiteral(PLUGIN_DIR), list);
}

/*** States color ***/
void Session::set_use_palette(const std::string &type, bool use) {
    S->setValue(QString::fromStdString(type + "/is_used"), use);
    _use_palettes = use;
}

bool Session::get_use_palette(const std::string &type) {
    // quick way, if we use the palettes, we don't need to check each time that we use them in the _settings file, only the first time
    if (_use_palettes_is_set)
        return _use_palettes;
    else {
        _use_palettes = S->value(QString::fromStdString(type + "/is_used")).toBool();
        _use_palettes_is_set = true;
        return _use_palettes;
    }
}

std::string Session::get_current_palette(const std::string &type) {
    std::string tmp = S->value(QString::fromStdString(type + CUR_PALETTE)).toString().toStdString();
    if (tmp == "")
        tmp = "default";
    return tmp;
}

void Session::set_current_palette(const std::string &type, const std::string &name) {
    S->setValue(QString::fromStdString(type + CUR_PALETTE), QString::fromStdString(name));
}

void Session::get_palettes_name(const std::string &type, QStringList &list) {
    list = S->value(QString::fromStdString(type + PALETTE_NAMES)).toStringList();
    if (!list.contains(QStringLiteral("default"))) {
        list.append(QStringLiteral("default"));
    }
}

Palette *Session::get_palette(const std::string &type, const std::string &palette_name) {
    Palette *p = NULL, **where_from = NULL;
    if (type == "palette") {
        p = _palettes_state;
        where_from = &_palettes_state;
    }
    else if (type == "link_types") {
        p = _palettes_link;
        where_from = &_palettes_link;
    }
    else if (type == "event_types") {
        p = _palettes_event;
        where_from = &_palettes_event;
    }

    if ((!p || (p->get_name() != palette_name)) && (where_from != NULL)) {
        delete p;

        QMap<QString, QVariant> qmap = S->value(QString::fromStdString(type + "/" + palette_name + "/map")).toMap();

        p = new Palette(string(type + "/" + palette_name));
        *where_from = p;
        for (QMap<QString, QVariant>::const_iterator it = qmap.constBegin();
             it != qmap.constEnd(); ++it) {
            const QColor qc = it.value().value<QColor>();
            Color c = Color(qc.red() / 255., qc.green() / 255., qc.blue() / 255.);
            p->add_state(it.key().toStdString(), c, qc.alpha() == 255.);
        }
    }
    return p;
}

void Session::create_palette(const std::string &type, const std::string &name) {
    QStringList list = S->value(QString::fromStdString(type + PALETTE_NAMES)).toStringList();
    list.append(QString::fromStdString(name));
    S->setValue(QString::fromStdString(type + PALETTE_NAMES), list);
}

void Session::remove_palette(const std::string &type, const std::string &name) {
    QStringList list = S->value(QString::fromStdString(type + PALETTE_NAMES)).toStringList();
    list.removeOne(QString::fromStdString(name));
    S->setValue(QString::fromStdString(type + PALETTE_NAMES), list);

    QString _name = QString::fromStdString(type + "/" + name + "/map");
    S->setValue(_name, QMap<QString, QVariant>());
}

void Session::copy_palette(const std::string &type, const std::string &src, const std::string &dest) {
    // Save the palette
    QStringList list = S->value(QString::fromStdString(type + PALETTE_NAMES)).toStringList();
    list.append(QString::fromStdString(dest));
    S->setValue(QString::fromStdString(type + PALETTE_NAMES), list);

    // Copy the states
    QString name_src = QString::fromStdString(type + "/" + src + "/map");
    QString name_dest = QString::fromStdString(type + "/" + dest + "/map");

    QMap<QString, QVariant> qmap = S->value(name_src).toMap();

    S->setValue(name_dest, qmap);
}

void Session::add_state_to_palette(const std::string &type,
                                   const std::string &palette_name,
                                   const std::string &state_name,
                                   const Color &c, bool visible) {
    QString name = QString::fromStdString(type + "/" + palette_name + "/map");
    QMap<QString, QVariant> qmap = S->value(name).toMap();
    qmap[QString::fromStdString(state_name)] = QColor((int)(c.get_red() * 255),
                                                      (int)(c.get_green() * 255),
                                                      (int)(c.get_blue() * 255),
                                                      visible == true ? 255 : 0);
    S->setValue(name, qmap);
}

void Session::remove_state_to_palette(const std::string &type,
                                      const std::string &palette_name,
                                      const std::string &state_name) {
    QString name = QString::fromStdString(type + "/" + palette_name + "/map");
    QMap<QString, QVariant> qmap = S->value(name).toMap();
    qmap.remove(QString::fromStdString(state_name));
    S->setValue(name, qmap);
}
