// border.cpp - Implementació del border del playfield
// © 2026 JailDesigner

#include "core/graphics/border.hpp"

#include <algorithm>
#include <array>

#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"

namespace Graphics {

    Border::Border(Rendering::Renderer* renderer)
        : renderer_(renderer) {}

    void Border::update(float delta_time) {
        for (auto& side : sides_) {
            // Desplaçament decau cap a 0 amb ritme constant (lineal).
            const float DEC = Defaults::Border::DISPLACEMENT_RECOVERY_PER_S * delta_time;
            side.displacement_px = std::max(0.0F, side.displacement_px - DEC);
        }
    }

    void Border::bumpAt(Vec2 contact_point, float strength) {
        const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
        const std::array<float, SIDE_COUNT> DISTANCES = {
            /* TOP    */ std::abs(contact_point.y - zone.y),
            /* RIGHT  */ std::abs((zone.x + zone.w) - contact_point.x),
            /* BOTTOM */ std::abs((zone.y + zone.h) - contact_point.y),
            /* LEFT   */ std::abs(contact_point.x - zone.x)};

        int closest_idx = 0;
        float closest_dist = DISTANCES[0];
        for (int i = 1; i < SIDE_COUNT; i++) {
            if (DISTANCES[i] < closest_dist) {
                closest_dist = DISTANCES[i];
                closest_idx = i;
            }
        }
        applyBump(closest_idx, strength);
    }

    void Border::applyBump(int side_idx, float strength) {
        const float S = std::clamp(strength, 0.0F, 1.0F);
        SideState& side = sides_[static_cast<std::size_t>(side_idx)];
        side.displacement_px = std::min(
            Defaults::Border::MAX_DISPLACEMENT_PX,
            side.displacement_px + (S * Defaults::Border::MAX_DISPLACEMENT_PX));
    }

    namespace {

        // Lerp del color base verd fòsfor cap a un color "flash" en funció de
        // f ∈ [0, 1]. Retorna sempre amb alpha>0 perquè el line_renderer l'usi
        // directament (sense caure al fallback DEFAULT_LINE_COLOR).
        auto lerpColor(SDL_Color flash, float f) -> SDL_Color {
            const float CLAMPED = std::clamp(f, 0.0F, 1.0F);
            constexpr SDL_Color BASE = Rendering::DEFAULT_LINE_COLOR;
            const auto LERP_U8 = [&](unsigned char a, unsigned char b) {
                const float OUT = (static_cast<float>(a) * (1.0F - CLAMPED)) + (static_cast<float>(b) * CLAMPED);
                return static_cast<unsigned char>(OUT);
            };
            return SDL_Color{
                .r = LERP_U8(BASE.r, flash.r),
                .g = LERP_U8(BASE.g, flash.g),
                .b = LERP_U8(BASE.b, flash.b),
                .a = 255};
        }

    }  // namespace

    void Border::draw() const {
        const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
        const int X1 = static_cast<int>(zone.x);
        const int Y1 = static_cast<int>(zone.y);
        const int X2 = static_cast<int>(zone.x + zone.w);
        const int Y2 = static_cast<int>(zone.y + zone.h);

        const int OFF_TOP = static_cast<int>(sides_[SIDE_TOP].displacement_px);
        const int OFF_RIGHT = static_cast<int>(sides_[SIDE_RIGHT].displacement_px);
        const int OFF_BOTTOM = static_cast<int>(sides_[SIDE_BOTTOM].displacement_px);
        const int OFF_LEFT = static_cast<int>(sides_[SIDE_LEFT].displacement_px);

        // Color per costat: lerp(oscil·lador → flash) en funció del desplaçament.
        const SDL_Color FLASH = {
            .r = Defaults::Border::FLASH_COLOR_R,
            .g = Defaults::Border::FLASH_COLOR_G,
            .b = Defaults::Border::FLASH_COLOR_B,
            .a = 255};
        const float MAX_D = Defaults::Border::MAX_DISPLACEMENT_PX;
        const bool DO_FLASH = Defaults::Border::FLASH_ENABLED;

        const SDL_Color C_TOP = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_TOP].displacement_px / MAX_D) : SDL_Color{};
        const SDL_Color C_RIGHT = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_RIGHT].displacement_px / MAX_D) : SDL_Color{};
        const SDL_Color C_BOTTOM = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_BOTTOM].displacement_px / MAX_D) : SDL_Color{};
        const SDL_Color C_LEFT = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_LEFT].displacement_px / MAX_D) : SDL_Color{};

        // Una sola línia per costat (brillo 1.0). Si DO_FLASH = false → alpha = 0 → usa
        // el color global de l'oscil·lador.
        Rendering::linea(renderer_, X1, Y1 - OFF_TOP, X2, Y1 - OFF_TOP, 1.0F, 0.0F, C_TOP);
        Rendering::linea(renderer_, X2 + OFF_RIGHT, Y1, X2 + OFF_RIGHT, Y2, 1.0F, 0.0F, C_RIGHT);
        Rendering::linea(renderer_, X1, Y2 + OFF_BOTTOM, X2, Y2 + OFF_BOTTOM, 1.0F, 0.0F, C_BOTTOM);
        Rendering::linea(renderer_, X1 - OFF_LEFT, Y1, X1 - OFF_LEFT, Y2, 1.0F, 0.0F, C_LEFT);
    }

}  // namespace Graphics
