// player_config.cpp - Implementació del parser de PlayerConfig
// © 2026 JailDesigner

#include "game/entities/player_config.hpp"

#include <cstdint>
#include <exception>
#include <iostream>
#include <string>

namespace {

    // Helper: extreu un SDL_Color d'una seqüència de 3 enters [r, g, b] del YAML.
    // Retorna true si el format és vàlid.
    auto parseColor(const fkyaml::node& node, SDL_Color& out) -> bool {
        if (!node.is_sequence() || node.size() != 3) {
            return false;
        }
        const auto R = node[0].get_value<uint32_t>();
        const auto G = node[1].get_value<uint32_t>();
        const auto B = node[2].get_value<uint32_t>();
        out = SDL_Color{
            .r = static_cast<uint8_t>(R),
            .g = static_cast<uint8_t>(G),
            .b = static_cast<uint8_t>(B),
            .a = 255};
        return true;
    }

}  // namespace

auto PlayerConfig::fromYaml(const fkyaml::node& node) -> std::optional<PlayerConfig> {
    try {
        PlayerConfig cfg;

        cfg.name = node.contains("name") ? node["name"].get_value<std::string>() : "player";

        // shape
        if (!node.contains("shape") || !node["shape"].contains("path")) {
            std::cerr << "[PlayerConfig] Error: falta 'shape.path'" << '\n';
            return std::nullopt;
        }
        const auto& shape = node["shape"];
        cfg.shape.path = shape["path"].get_value<std::string>();
        cfg.shape.scale = shape.contains("scale") ? shape["scale"].get_value<float>() : 1.0F;
        cfg.shape.collision_factor = shape.contains("collision_factor")
            ? shape["collision_factor"].get_value<float>()
            : 1.0F;

        // physics
        if (!node.contains("physics")) {
            std::cerr << "[PlayerConfig] Error: falta 'physics'" << '\n';
            return std::nullopt;
        }
        const auto& physics = node["physics"];
        cfg.physics.mass = physics["mass"].get_value<float>();
        cfg.physics.restitution = physics["restitution"].get_value<float>();
        cfg.physics.linear_damping = physics["linear_damping"].get_value<float>();
        cfg.physics.angular_damping = physics["angular_damping"].get_value<float>();
        cfg.physics.rotation_speed = physics["rotation_speed"].get_value<float>();
        cfg.physics.acceleration = physics["acceleration"].get_value<float>();
        cfg.physics.max_velocity = physics["max_velocity"].get_value<float>();
        cfg.physics.death_impact_factor = physics["death_impact_factor"].get_value<float>();

        // invulnerability
        if (!node.contains("invulnerability")) {
            std::cerr << "[PlayerConfig] Error: falta 'invulnerability'" << '\n';
            return std::nullopt;
        }
        const auto& invul = node["invulnerability"];
        cfg.invulnerability.duration = invul["duration"].get_value<float>();
        cfg.invulnerability.blink_visible = invul["blink_visible"].get_value<float>();
        cfg.invulnerability.blink_invisible = invul["blink_invisible"].get_value<float>();

        // hurt
        if (!node.contains("hurt")) {
            std::cerr << "[PlayerConfig] Error: falta 'hurt'" << '\n';
            return std::nullopt;
        }
        cfg.hurt.duration = node["hurt"]["duration"].get_value<float>();
        cfg.hurt.blink_hz = node["hurt"]["blink_hz"].get_value<float>();

        // visual_thrust
        if (!node.contains("visual_thrust")) {
            std::cerr << "[PlayerConfig] Error: falta 'visual_thrust'" << '\n';
            return std::nullopt;
        }
        cfg.visual_thrust.push_divisor = node["visual_thrust"]["push_divisor"].get_value<float>();
        cfg.visual_thrust.scale_divisor = node["visual_thrust"]["scale_divisor"].get_value<float>();

        // colors
        if (!node.contains("colors") ||
            !parseColor(node["colors"]["normal"], cfg.colors.normal) ||
            !parseColor(node["colors"]["hurt"], cfg.colors.hurt)) {
            std::cerr << "[PlayerConfig] Error: 'colors.normal' / 'colors.hurt' no són seqüències [r,g,b]" << '\n';
            return std::nullopt;
        }

        // weapon
        if (!node.contains("weapon")) {
            std::cerr << "[PlayerConfig] Error: falta 'weapon'" << '\n';
            return std::nullopt;
        }
        cfg.weapon.bullet_speed = node["weapon"]["bullet_speed"].get_value<float>();

        return cfg;
    } catch (const std::exception& e) {
        std::cerr << "[PlayerConfig] Excepció parsejant: " << e.what() << '\n';
        return std::nullopt;
    }
}
