// cas_fishinc.nss
// Shared constants + all game logic helpers for casino fish math game.

const string TOKEN_TAG           = "I_CAS_CASINOHANDLE";
const string VAR_DIFFICULTY      = "INT_DIFFICULTY";

const string FISH_RESREF =         "cas_fishnpc"; //npc being spawned for game
const string SPEAR_RESREF        = "cas_fishspear";   // spear blueprint resref

const string WAYPOINT_TAG_PREFIX = "iSpawnFishHere";
const int    TOTAL_WAYPOINTS     = 10;

const int    DESIRED_FISH_COUNT  = 3;

const float GAME_DURATION_SECONDS = 90.0;


// -------------------- inventory helpers --------------------

object CasFish_GetFirstItemByResRef(object oPC, string sResRef)
{
    object oItem = GetFirstItemInInventory(oPC);
    while (GetIsObjectValid(oItem))
    {
        if (GetResRef(oItem) == sResRef)
            return oItem;

        oItem = GetNextItemInInventory(oPC);
    }
    return OBJECT_INVALID;
}

void CasFish_GiveAndRegisterSpear(object oPC)
{
    object oSpear = CasFish_GetFirstItemByResRef(oPC, SPEAR_RESREF);

    if (!GetIsObjectValid(oSpear))
    {
        oSpear = CreateItemOnObject(SPEAR_RESREF, oPC);
    }

    if (!GetIsObjectValid(oSpear))
    {
        SendMessageToPC(oPC, "DEBUG: Failed to create spear (resref " + SPEAR_RESREF + ").");
        return;
    }

    SendMessageToPC(oPC, "DEBUG: Spear given (" + SPEAR_RESREF + ").");

    // Register the variable event
    RegisterVariableEvent(oSpear, "EVENT_USE", "cas_fishusespear");
}


void CasFish_DestroySpear(object oPC)
{
    object oSpear = CasFish_GetFirstItemByResRef(oPC, SPEAR_RESREF);
    if (GetIsObjectValid(oSpear))
        DestroyObject(oSpear);
}

// -------------------- fish lifecycle helpers --------------------

void CasFish_DespawnFish(object oFish)
{
    if (GetIsObjectValid(oFish))
        DestroyObject(oFish);
}

void CasFish_CleanupOwnedFish(object oPC)
{
    object oArea = GetArea(oPC);
    object oObj  = GetFirstObjectInArea(oArea);

    while (GetIsObjectValid(oObj))
    {
        int nType = GetObjectType(oObj);

        // Support either placeable fish or creature fish
        if ((nType == OBJECT_TYPE_PLACEABLE || nType == OBJECT_TYPE_CREATURE) &&
            GetResRef(oObj) == FISH_RESREF &&
            GetLocalObject(oObj, "FISH_OWNER") == oPC)
        {
            DestroyObject(oObj);
        }

        oObj = GetNextObjectInArea(oArea);
    }
}

int CasFish_CountOwnedFish(object oPC)
{
    int nCount   = 0;
    object oArea = GetArea(oPC);
    object oObj  = GetFirstObjectInArea(oArea);

    while (GetIsObjectValid(oObj))
    {
        int nType = GetObjectType(oObj);
        if ((nType == OBJECT_TYPE_PLACEABLE || nType == OBJECT_TYPE_CREATURE) &&
            GetResRef(oObj) == FISH_RESREF &&
            GetLocalObject(oObj, "FISH_OWNER") == oPC)
        {
            nCount++;
        }
        oObj = GetNextObjectInArea(oArea);
    }

    return nCount;
}

int CasFish_IsOwnedFishAtLocation(object oPC, location lLoc, float fRadius)
{
    int nShape = SHAPE_SPHERE;
    int bLOS   = FALSE;

    // We'll scan both types by doing two passes (keeps it simple)
    int nType  = OBJECT_TYPE_PLACEABLE;
    object oCheck = GetFirstObjectInShape(nShape, fRadius, lLoc, bLOS, nType);
    while (GetIsObjectValid(oCheck))
    {
        if (GetResRef(oCheck) == FISH_RESREF &&
            GetLocalObject(oCheck, "FISH_OWNER") == oPC)
        {
            return TRUE;
        }
        oCheck = GetNextObjectInShape(nShape, fRadius, lLoc, bLOS, nType);
    }

    nType  = OBJECT_TYPE_CREATURE;
    oCheck = GetFirstObjectInShape(nShape, fRadius, lLoc, bLOS, nType);
    while (GetIsObjectValid(oCheck))
    {
        if (GetResRef(oCheck) == FISH_RESREF &&
            GetLocalObject(oCheck, "FISH_OWNER") == oPC)
        {
            return TRUE;
        }
        oCheck = GetNextObjectInShape(nShape, fRadius, lLoc, bLOS, nType);
    }

    return FALSE;
}

void CasFish_SpawnFishForPlayer(object oPC)
{
    if (!GetIsObjectValid(oPC) || !GetIsPC(oPC) || GetIsDM(oPC))
        return;

    if (!GetLocalInt(oPC, "MATH_GAME_ACTIVE"))
        return;

    if (CasFish_CountOwnedFish(oPC) >= DESIRED_FISH_COUNT)
        return;

    int iTries = 10;
    while (iTries > 0)
    {
        int iWP = Random(TOTAL_WAYPOINTS) + 1;
        string sWPTag = WAYPOINT_TAG_PREFIX + IntToString(iWP);
        object oWP = GetWaypointByTag(sWPTag);

        if (!GetIsObjectValid(oWP))
        {
            iTries--;
            continue;
        }

        location lSpawn = GetLocation(oWP);

        // If one of THIS PC's fish is already here, try another spot
        if (CasFish_IsOwnedFishAtLocation(oPC, lSpawn, 1.0))
        {
            iTries--;
            continue;
        }

        int nFishValue = Random(10); // 0..9

        // You can switch between placeable/creature fish here:
        object oFish = CreateObject(OBJECT_TYPE_CREATURE, FISH_RESREF, lSpawn);

        if (!GetIsObjectValid(oFish))
            return;

        SetName(oFish, IntToString(nFishValue));
        SetLocalInt(oFish, "FISH_VALUE", nFishValue);
        SetLocalObject(oFish, "FISH_OWNER", oPC);

        // Random lifetime 2..10 seconds
        float fDelay = IntToFloat(Random(9) + 2);
        DelayCommand(fDelay, CasFish_DespawnFish(oFish));

        // After despawn, top-up again
        DelayCommand(fDelay + 0.2, CasFish_SpawnFishForPlayer(oPC));
        return;
    }
}

// -------------------- game state helpers --------------------

void CasFish_EndRun(object oPC, string sMsg)
{
    SetLocalInt(oPC, "MATH_GAME_ACTIVE", 0);

    // Clear the starter tracking
    DeleteLocalObject(oPC, "MATH_GAME_STARTER");
    DeleteLocalString(oPC, "MATH_GAME_STARTER_TAG");

    CasFish_CleanupOwnedFish(oPC);
    CasFish_DestroySpear(oPC);

    SendMessageToPC(oPC, sMsg);
}


// Called when spear hits a valid fish owned by the PC
void CasFish_ApplyFishHit(object oPC, object oFish)
{
    int nValue   = GetLocalInt(oFish, "FISH_VALUE");
    int nTarget  = GetLocalInt(oPC, "MATH_GAME_TARGET");
    int nCurrent = GetLocalInt(oPC, "MATH_GAME_CURRENT");

    nCurrent += nValue;
    SetLocalInt(oPC, "MATH_GAME_CURRENT", nCurrent);

    DestroyObject(oFish);

    SendMessageToPC(oPC,
        "Harpoon hit: " + IntToString(nValue) +
        " | Total: " + IntToString(nCurrent) +
        " / " + IntToString(nTarget));

    if (nCurrent == nTarget)
    {
        CasFish_EndRun(oPC, "Exact hit! You win!");
        // TODO: payout here
        return;
    }

    if (nCurrent > nTarget)
    {
        CasFish_EndRun(oPC, "Bust! You went over the target.");
        return;
    }

    // Keep field stocked
    CasFish_SpawnFishForPlayer(oPC);
}

void CasFish_OnUseSpear(object oPC, object oItem, object oTarget)
{
    if (!GetIsPC(oPC) || GetIsDM(oPC))
        return;

    // Must be our spear
    if (!GetIsObjectValid(oItem) || GetResRef(oItem) != SPEAR_RESREF)
        return;

    // Must have an active round
    if (!GetLocalInt(oPC, "MATH_GAME_ACTIVE"))
    {
        SendMessageToPC(oPC, "No active game. Use the table to start.");
        return;
    }

    // If no target at all, give a helpful hint
    if (!GetIsObjectValid(oTarget))
    {
        SendMessageToPC(oPC, "Use the harpoon on a fish (or on the table to cancel).");
        return;
    }

    // --- EARLY CANCEL: spear used on the starting placeable ---
    if (CasFish_IsGameStarterTarget(oPC, oTarget))
    {
        CasFish_EndRun(oPC, "You ended the round early.");
        return;
    }

    // Must be one of our fish (placeable or creature)
    int nType = GetObjectType(oTarget);
    if (nType != OBJECT_TYPE_PLACEABLE && nType != OBJECT_TYPE_CREATURE)
    {
        SendMessageToPC(oPC, "That isn't a fish.");
        return;
    }

    // Must be correct fish resref
    if (GetResRef(oTarget) != FISH_RESREF)
    {
        SendMessageToPC(oPC, "That isn't a fish.");
        return;
    }

    // Must be owned by this PC
    if (GetLocalObject(oTarget, "FISH_OWNER") != oPC)
    {
        SendMessageToPC(oPC, "That fish isn't yours.");
        return;
    }

    // Apply hit (adds value, destroys fish, win/bust checks, top-up spawn)
    CasFish_ApplyFishHit(oPC, oTarget);
}





void CasFish_Timeout(object oPC)
{
    if (!GetIsObjectValid(oPC) || !GetIsPC(oPC))
        return;

    if (!GetLocalInt(oPC, "MATH_GAME_ACTIVE"))
        return;

    CasFish_EndRun(oPC, "Time's up! The round has ended.");
}

void CasFish_SetGameStarter(object oPC, object oStarter)
{
    SetLocalObject(oPC, "MATH_GAME_STARTER", oStarter);
    SetLocalString(oPC, "MATH_GAME_STARTER_TAG", GetTag(oStarter));
}

int CasFish_IsGameStarterTarget(object oPC, object oTarget)
{
    if (!GetIsObjectValid(oTarget))
        return FALSE;

    object oStarter = GetLocalObject(oPC, "MATH_GAME_STARTER");
    if (GetIsObjectValid(oStarter) && oTarget == oStarter)
        return TRUE;

    // Fallback by tag in case the object ref changes
    string sTag = GetLocalString(oPC, "MATH_GAME_STARTER_TAG");
    if (sTag != "" && GetTag(oTarget) == sTag)
        return TRUE;

    return FALSE;
}
