#include "pch.hpp"
#include "common.hpp"
#include "core/interfaces.hpp"
#include "utils/fnv1a.hpp"
#include "sdk/entity.hpp"
#include "features/features.hpp"

// ─── Class name → weapon type mapping ──────────────────────────────────────
// Converts CS2 schema class names like "C_WeaponAK47" to short icon names
// used by the frontend (e.g. "ak47", "m4a1_silencer").

static const char* class_name_to_weapon_type(const std::string& name)
{
    // ── Non-standard schema names (no "C_Weapon" prefix) ─────────────────
    // These weapons/utilities have schema class names that don't follow the
    // "C_Weapon*" pattern. Map them explicitly — no runtime memory reads.
    //
    // Weapons:
    //   C_AK47   — actual schema is "C_AK47"   not "C_WeaponAK47"
    //   C_DEagle — actual schema is "C_DEagle" not "C_WeaponDeagle"
    //
    // Dropped utilities (parent C_BaseCSGrenade):
    //   C_Flashbang, C_HEGrenade, C_SmokeGrenade,
    //   C_MolotovGrenade, C_IncendiaryGrenade, C_DecoyGrenade
    if (name == "C_AK47")   return "ak47";
    if (name == "C_DEagle") return "deagle";
    if (name == "C_Flashbang")   return "flashbang";
    if (name == "C_HEGrenade")   return "hegrenade";
    if (name == "C_SmokeGrenade")  return "smokegrenade";
    if (name == "C_MolotovGrenade") return "molotov";
    if (name == "C_IncendiaryGrenade") return "incgrenade";
    if (name == "C_DecoyGrenade")  return "decoy";

    // Strip the "C_Weapon" prefix and return the lowercase remainder
    if (name.starts_with("C_Weapon"))
    {
        // Handle special renames to match icon filenames
        constexpr struct { const char* cls; const char* icon; } s_special[] = {
            { "C_WeaponM4A1Silencer", "m4a1_silencer" },
            { "C_WeaponUSPSilencer",  "usp_silencer"  },
            { "C_WeaponHKP2000",      "hkp2000"       },
            { "C_WeaponCZ75a",        "cz75a"         },
            { "C_WeaponGalilAR",      "galilar"       },
            { "C_WeaponSG556",        "sg556"         },
            { "C_WeaponSSG08",        "ssg08"         },
            { "C_WeaponMAC10",        "mac10"         },
            { "C_WeaponUMP45",        "ump45"         },
            { "C_WeaponXM1014",       "xm1014"        },
            { "C_WeaponMP5SD",        "mp5sd"         },
            { "C_WeaponM4A1",         "m4a1"          },
            { "C_WeaponFiveSeven",    "fiveseven"     },
            { "C_WeaponNOVA",         "nova"          },
            { "C_WeaponP250",         "p250"          },
            { "C_WeaponM249",         "m249"          },
            { "C_WeaponNegev",        "negev"         },
            { "C_WeaponSawedoff",     "sawedoff"      },
            { "C_WeaponTec9",         "tec9"          },
            { "C_WeaponBizon",        "bizon"         },
            { "C_WeaponElite",        "elite"         },
            { "C_WeaponFamas",        "famas"         },
            { "C_WeaponGlock",        "glock"         },
            { "C_WeaponP90",          "p90"           },
            { "C_WeaponTaser",        "taser"         },
            { "C_WeaponRevolver",     "revolver"      },
            { "C_WeaponSCAR20",       "scar20"        },
            { "C_WeaponG3SG1",        "g3sg1"         },
            { "C_WeaponMP7",          "mp7"           },
            { "C_WeaponMP9",          "mp9"           },
            { "C_WeaponMag7",         "mag7"          },
            { "C_WeaponAug",          "aug"           },
            { "C_WeaponAK47",         "ak47"          },
            { "C_WeaponAWP",          "awp"           },
            { "C_WeaponDeagle",       "deagle"        },
        };

        for (const auto& sp : s_special)
        {
            if (name == sp.cls)
                return sp.icon;
        }

        // Generic: strip "C_Weapon" and lowercase
        const auto suffix = name.substr(8);
        // Pre-allocate result
        static thread_local std::string result;
        result.clear();
        result.reserve(suffix.size());
        for (char c : suffix)
            result.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
        return result.c_str();
    }

    // Non-weapon items
    if (name == "C_Item_Healthshot")
        return "health";
    if (name == "C_C4")
        return "c4";

    return nullptr;
}

// ─── Check if class name should be skipped (handled by other modules) ──────
static bool is_skipped_entity(fnv1a_t hashed)
{
    // Skip grenade projectiles — handled by grenade code
    // Skip inferno — handled by grenade code
    // Skip player controllers & pawns — handled by player code
    return
        hashed == fnv1a::hash("C_SmokeGrenadeProjectile")  ||
        hashed == fnv1a::hash("C_FlashbangProjectile")     ||
        hashed == fnv1a::hash("C_HEGrenadeProjectile")     ||
        hashed == fnv1a::hash("C_MolotovProjectile")       ||
        hashed == fnv1a::hash("C_DecoyProjectile")        ||
        hashed == fnv1a::hash("C_Inferno")                ||
        hashed == fnv1a::hash("CCSPlayerController")      ||
        hashed == fnv1a::hash("CCSPlayerPawnBase")        ||
        hashed == fnv1a::hash("CCSPlayerPawn");
}

void f::dropped_weapons::get_dropped_weapons()
{
    f::m_data["m_dropped_weapons"] = nlohmann::json::array();

    // Scan entity indices 65–2048.
    // Indices 0–64 are reserved for players and core entities;
    // world items (dropped weapons, etc.) are spawned at higher indices.
    constexpr int32_t min_idx = 65;
    constexpr int32_t max_idx = 2048;

    for (int32_t idx = min_idx; idx < max_idx; idx++)
    {
        const auto entity = i::m_game_entity_system->get(idx);
        if (!entity)
            continue;

        const auto entity_handle = entity->get_ref_e_handle();
        if (!entity_handle.is_valid())
            continue;

        const auto class_name = entity->get_schema_class_name();
        if (class_name.empty())
            continue;

        const auto hashed = fnv1a::hash(class_name);

        // Skip entities handled by other modules
        if (is_skipped_entity(hashed))
            continue;

        // ── Planted C4: cache pointer for smooth timer reads ──────
        if (hashed == fnv1a::hash("C_PlantedC4"))
        {
            const auto planted = reinterpret_cast<c_planted_c4*>(entity);
            if (planted->m_bBombTicking())
            {
                f::m_planted_c4_ptr = reinterpret_cast<std::uintptr_t>(planted);
                f::bomb::get_planted_bomb(planted);
                f::m_last_bomb = f::m_data["m_bomb"];
            }
            continue;
        }

        // ── Carried / dropped C4: track carrier + show on radar ──
        if (hashed == fnv1a::hash("C_C4"))
        {
            f::bomb::get_carried_bomb(entity);
            f::m_last_bomb = f::m_data["m_bomb"];

            // Only show on map as dropped weapon when actually dropped
            const bool has_owner = (f::m_bomb_idx != 0xffff && f::m_bomb_idx != 0);
            if (has_owner)
                continue;   // still held — don't add as world weapon drop
            // Fall through → adds dropped C4 to world items
        }

        // Check if this entity is a weapon or droppable item
        const auto weapon_type = class_name_to_weapon_type(class_name);
        if (!weapon_type)
            continue;

        // Skip weapons that are being held by a player (not dropped)
        const auto owner_raw = reinterpret_cast<std::uintptr_t>(entity->m_hOwnerEntity());
        const bool has_owner = ((owner_raw & 0xFFFF) != 0xFFFF && (owner_raw & 0xFFFF) != 0);
        if (has_owner)
            continue;

        // Get position
        const auto pos = entity->get_scene_origin();
        if (pos.is_zero())
            continue;

        // Build JSON entry
        auto entry = nlohmann::json{
            { "type",       weapon_type },
            { "class_name", class_name  },
            { "x",          pos.m_x     },
            { "y",          pos.m_y     },
        };

        f::m_data["m_dropped_weapons"].push_back(std::move(entry));
    }
}
