/*  ImGui utils */

/* Fichier contenant quelques fonctionnalités basées sur ImGui :
 * VToggleButton() dérivé de ToggleButton
 * DrawVToggleButton
 *
 * Copyright Eric Bachard  2026/01/14  18h20
 * License GPL v2
 */

#include <iostream>
#include <cmath>
#include <string>

#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif

#include "digital_modulation_fr.hpp"
#include "imgui.h"
#include "vtoggle_button.h"
#include "imgui_utils.h"
#include "imgui_themes.h"

#ifdef M_PI
#undef M_PI
#define M_PI 3.14159265358979323846f
#endif

void selectThemeMenu(Application * p_app)
{
    static THEME selected_menutheme = p_app->get_current_theme();

    if (ImGui::BeginMenu(THEME_MENU_ENTRY))
    {
        if (ImGui::Selectable(TRADITIONAL_GREEN_THEME_MENU_ENTRY))
            selected_menutheme = LIGHT_GREEN_THEME;

        if (ImGui::Selectable(DARK_THEME_MENU_ENTRY))
            selected_menutheme = DARK_THEME;

        if (ImGui::Selectable(CLASSIC_THEME_MENU_ENTRY))
            selected_menutheme = CLASSIC_THEME;

        if (ImGui::Selectable(LIGHT_BLUE_THEME_MENU_ENTRY))
            selected_menutheme = LIGHT_BLUE_THEME;

        if (ImGui::Selectable(WINDOWS_THEME_MENU_ENTRY))
            selected_menutheme = WINDOWS_THEME;

        if (selected_menutheme != p_app->get_current_theme())
            p_app->setTheme(selected_menutheme);

        ImGui::EndMenu();
    }
}

void selectFrameSize(DigitalModulation * p_Dmod)
{
    // by default, we recopy the current value
    int frame_length = p_Dmod->getWordSize();

    if (ImGui::BeginMenu(FRAME_SIZE))
    {
        if (ImGui::Selectable(HEIGHT_BITS_FRAME_SIZE))
            frame_length = 8;

        if (ImGui::Selectable(SIXTEEN_BITS_FRAME_SIZE))
            frame_length = 16;

        if (ImGui::Selectable(TWENTY_FOUR_BITS_FRAME_SIZE))
            frame_length = 24;

        if (frame_length != p_Dmod->getWordSize())
            p_Dmod->setWordSize(frame_length);

        ImGui::EndMenu();
    }
}


// ToogleButton transformé en VToggleButton
bool VToggleButton(const char* str_id, bool* v)
{
    ImVec2 p = ImGui::GetCursorScreenPos();
    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    // Taille du toggle
    // FIXME : elaborate
    float width  = ImGui::GetFrameHeight();
    //float width = VTOGGLEBUTTON_WIDTH;
    float height = width * 1.8f;
    float radius = width * 0.5f;

    ImGui::InvisibleButton(str_id, ImVec2(width, height));

    // Vérifie si le bouton a été cliqué
    bool clicked = ImGui::IsItemClicked();
    if (clicked)
        *v = !*v;

    // Choix de la couleur de fond selon l'état
    ImU32 col_bg = ImGui::IsItemHovered()
        ? (*v ? IM_COL32(165,231,88,255) : IM_COL32(200,200,200,255))   //ON
        : (*v ? IM_COL32(145,211,68,255) : IM_COL32(218,218,218,255));  //OFF

    draw_list->AddRectFilled( p, ImVec2(p.x + width, p.y + height), col_bg, radius ); // Dessine le rectangle du Toggle
    float cy = *v ? (p.y + radius) : (p.y + height - radius); // Position verticale du petit rond
    draw_list->AddCircleFilled( ImVec2(p.x + radius, cy), radius - 2.0f, IM_COL32(255,255,255,255) ); // Dessine le rond blanc du Toggle

    return clicked; 
}

void drawVToggleButtons(int * dm_bits, int wordSize)
{
    // CHILD2_WIDTH = 300.0f 
    float drawSize = ImGui::GetContentRegionAvail().x - CHILD2_WIDTH;

    // FIXME : calculer sérieusement ces valeurs
//TEST

    // La formule :
    // draw_size = (P+B)wordSize + P + marges  // Il y a 1 padding de plus que de boutons dessinés
    // 
    // 20.0f because fo the left/right margins
    // On dessine dans la child window 1,
    // et le nombre de pixels n'est plus le même en plein écran
    // exemple : drawSize = 664.0f contre 1304.0f en plein écran.
    // 2 solutions : soit dessiner a valeur définie et testée (pas très pro)
    // soit calculer précisément le padding. Or la formule ne semble pas fonctionner ...
    // La valeur de 20.0f est soustraite car il faut tenir compte d'une marge à gauche
    // et d'une marge à droite pour le canvas. 
    // current best value : 20.0f
    float padding = ((drawSize - 20.0f) - VTOGGLEBUTTON_WIDTH * (float)wordSize)/((float)wordSize + 1);

    // hack ... le temps de corriger la formule
    if (wordSize == 8)
        padding += 32.0f;

    if (wordSize == 16)
        padding += 7.5f;

    if (drawSize > 1000.0F)
    {
        if (wordSize == 8)
            padding += 7.3f; // best value 7.3f

        if (wordSize == 16)
            padding += 2.3f; // best value 2.3f
        else
            padding += 1.2f; // best value 1.2f
    }
    // end hack

    ImGui::Dummy(ImVec2(padding / 2.0f, 0.0f)); ImGui::SameLine(); // Padding pour le premier bouton

    for (int i = 0; i < wordSize ; i++)
    {
        ImGui::PushID(i);
        bool bit = (dm_bits[i] != 0);

        if (VToggleButton("##Button", &bit))
            dm_bits[i] = bit ? 1 : 0;
        ImGui::PopID();

        if (i < (wordSize - 1))   // Pas de padding pour le dernier
        {
            ImGui::SameLine();
            ImGui::Dummy(ImVec2(padding, 0.0f)); ImGui::SameLine();
        }
    }
// debug purpose
//#define DEBUG_ME
#ifdef DEBUG_ME
    ImGui::Text(" wordSize : %d", wordSize);
    ImGui::SameLine();
    ImGui::Text(" padding : %f", padding);
    ImGui::SameLine();
    ImGui::Text(" drawSize : %f", drawSize);
    ImGui::SameLine();
    ImGui::Text("VTOGGLEBUTTON_WIDTH : %f", VTOGGLEBUTTON_WIDTH);
#endif /* DEBUG_ME */
}

void highlightFrame(int * dm_bits, int wordSize)
{
    ImVec2 text_pos = ImGui::GetCursorScreenPos();
    // pour avoir la surbrillance de longueur variable
    std::string text_trame = "Trame :";

    for (int i = 0; i < wordSize; i++)
    {
        if (0 == i % 8 && i != 0)  // Découpage par octets
            text_trame += "  ";

        text_trame += " ";
        text_trame += std::to_string(dm_bits[i]);
    }

    ImVec2 text_size = ImGui::CalcTextSize(text_trame.c_str());
    ImGui::GetWindowDrawList()->AddRectFilled(
        text_pos,
        ImVec2(text_pos.x + text_size.x, text_pos.y + text_size.y),
        IM_COL32(255, 255, 0, 128) // Jaune semi-transparent
    );
    ImGui::Text("%s", text_trame.c_str());
    ImGui::NewLine();
    ImGui::Separator();
}

// Helper to draw a small modulation example (Témoin visuel). Code proposé par 
void DrawIndicator(const char * label,
                   float * carrier_freq,
                   DigitalModulationType * p_modulation_type,
                   int * samples_per_bit,
                   float amp_scale,
                   float phase_offset,
                   ImU32 color,
                   ImVec2 size)
{
    ImGui::Text("%s", label);
    ImVec2 p = ImGui::GetCursorScreenPos();
    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    // Fond gris clair pour le témoin
    draw_list->AddRectFilled(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(245, 245, 245, 255));

    // Bordure
    draw_list->AddRect(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(180, 180, 180, 255));

    float cy = p.y + size.y * 0.5f;     // Centre vertical
    float base_amp = (size.y * 0.35f);  // Amplitude de base (laisse une marge)

    ImVec2 prev;
    bool first = true;

    // On dessine 2 périodes (ou le double si un symbole est codé sur 2 bits),
    // pour bien visualiser la forme
    static float k2 = 1.0f;

    switch (*p_modulation_type)
    {
        case MOD_MASK_TYPE :
        case MOD_M_QAM_TYPE:
        case MOD_4_QAM_TYPE:
        case MOD_MFSK_TYPE :
            k2 = 2.0f;
        break;

        default:
            k2 = 1.0f;
        break;
    }

    // tracé de la courbe
    for (int i = 0; i <= *samples_per_bit; i++)
    {
        float t = (float)i / (float)*samples_per_bit; // 0 à 1
        // Formule du signal : sin(2*PI * f * t + phase)
        float y = cy - sinf(k2 * 2.0f * M_PI * (* carrier_freq) * t + phase_offset) * (base_amp * amp_scale);
        float x = p.x + t * size.x;
        ImVec2 curr(x, y);

        if (!first)
            draw_list->AddLine(prev, curr, color, 1.5f); // Ligne un peu plus épaisse
        prev = curr;
        first = false;
    }

    // Réserve l'espace pour qu'ImGui ne dessine pas par dessus
    ImGui::Dummy(size);
    // Petit espacement vertical après
    ImGui::Dummy(ImVec2(0, 5)); 
}

