#include "sithAIAwareness.h"

#include "stdPlatform.h" // Added: *_ALLOC/*_FREE macros

#include "AI/sithAI.h"
#include "Gameplay/sithEvent.h"
#include "World/sithWorld.h"
#include "World/sithSector.h"
#include "World/sithThing.h"
#include "Dss/sithMulti.h"
#include "jk.h"

int sithAIAwareness_Startup()
{
    sithAIAwareness_g_aSectors = (sithSectorAlloc *)SITH_ALLOC(sizeof(sithSectorAlloc) * sithWorld_g_pCurrentWorld->numSectors);
    if (sithAIAwareness_g_aSectors)
    {
        sithAIAwareness_numEntries = 0;
        if ( sithEvent_RegisterTask(3, sithAIAwareness_Update, 1000, SITHEVENT_TASKPERIODIC) )
        {
            sithAIAwareness_bInitted = 1;
            return 1;
        }
    }

    return 0;
}

void sithAIAwareness_Close()
{
    SITH_FREE(sithAIAwareness_g_aSectors);
    sithAIAwareness_g_aSectors = 0;
    sithEvent_RegisterTask(3, NULL, 0, SITHEVENT_TASKDISABLED);
    sithAIAwareness_bInitted = 0;
}

int sithAIAwareness_CreateTransmittingEvent(SithSector *pSector, rdVector3 *pos, int32_t type, flex_t transmittingLevel, SithThing *pThing)
{
    if (!sithAI_bOpened) {
        return 0;
    }
    if (sithAIAwareness_numEntries == 32) {
        return 0;
    }
    sithAIAwareness_aEntries[sithAIAwareness_numEntries].sector = pSector;
    rdVector_Copy3(&sithAIAwareness_aEntries[sithAIAwareness_numEntries].pos, pos);
    sithAIAwareness_aEntries[sithAIAwareness_numEntries].field_14 = type;
    sithAIAwareness_aEntries[sithAIAwareness_numEntries].field_18 = transmittingLevel;
    sithAIAwareness_aEntries[sithAIAwareness_numEntries].thing = pThing;

    sithAIAwareness_numEntries++;

    return 1;
}

void sithAIAwareness_ProcessEvents()
{
    if ( sithAIAwareness_numEntries )
    {
        for (uint32_t i = 0; i < sithAIAwareness_numEntries; i++)
        {
            sithSectorEntry* entry = &sithAIAwareness_aEntries[i];
            sithAIAwareness_ProcessEvent(entry, entry->sector, &entry->pos, &entry->pos, entry->field_18, entry->field_18, entry->thing);
        }
    }
}

int sithAIAwareness_Update(int32_t msecTime, SithEventParams* pParams)
{
    // Added: co-op
    if (sithNet_isMulti && !sithNet_isServer) {
        return 1;
    }

    ++sithAIAwareness_timerTicks;
    if (!sithAIAwareness_numEntries) {
        return 1;
    }

    for (size_t v1 = 0; v1 < sithAIAwareness_numEntries; v1++)
    {
        sithSectorEntry* v2 = &sithAIAwareness_aEntries[v1];
        // Added: potential crash maybe?
        sithAIAwareness_ProcessEvent(v2, v2->sector, &v2->pos, &v2->pos, v2->field_18, v2->field_18, v2->thing);
    }
    
    // sithAI_inittedActors is an inclusive max index (-1 when empty), so the loop must be `<=`
    // to reach the highest-indexed actor (matches the binary and sithAI_Process). A signed
    // counter keeps the empty (-1) case iterating zero times.
    for (int v3 = 0; v3 <= sithAI_inittedActors; ++v3 )
    {
        // Added: prevent OOB access
        // TODO: define this maximum
        if (v3 >= SITHAI_MAX_ACTORS) break;

        SithAIControlBlock* i = &sithAI_actors[v3];

        if ( i->pClass )
        {
            if ( i->thing )
            {
                if ( (i->thing->flags & (SITH_TF_DEAD|SITH_TF_DESTROYED)) == 0 )
                {
                    SithSector* v6 = i->thing->sector;
                    if ( v6 )
                    {
                        if ( sithAIAwareness_g_aSectors[v6->id].field_0 == sithAIAwareness_timerTicks )
                            sithAI_EmitEvent(i, SITHAI_MODE_ATTACKING, 0);
                    }
                }
            }
        }
    }
    sithAIAwareness_numEntries = 0;
    return 1;
}

void sithAIAwareness_ProcessEvent(sithSectorEntry *pEvent, SithSector *pSector, rdVector3 *startPos, rdVector3 *endPos, flex_t levelAtTransmittingPos, flex_t a6, SithThing *pThing)
{
    // Added: potential crash maybe?
    OPENJKDF2_WARN_NULL_AND_RETURN(pEvent);
    OPENJKDF2_WARN_NULL_AND_RETURN(pSector);
    OPENJKDF2_WARN_NULL_AND_RETURN(startPos);
    OPENJKDF2_WARN_NULL_AND_RETURN(endPos);
    OPENJKDF2_WARN_NULL_AND_RETURN(sithAIAwareness_g_aSectors);

    sithSectorAlloc* pSectorAlloc = &sithAIAwareness_g_aSectors[pSector->id];
    if (pSectorAlloc->field_0 != sithAIAwareness_timerTicks)
    {
        _memset(pSectorAlloc, 0, sizeof(sithSectorAlloc));
        pSectorAlloc->field_0 = sithAIAwareness_timerTicks;
    }

    if (pSectorAlloc->field_4[pEvent->field_14] < (flex_d_t)levelAtTransmittingPos)
    {
        pSectorAlloc->field_4[pEvent->field_14] = levelAtTransmittingPos;
        pSectorAlloc->field_10[pEvent->field_14] = *startPos;
#ifndef OPTIMIZE_AWAY_UNUSED_FIELDS
        pSectorAlloc->field_34[pEvent->field_14] = *endPos;
#endif
        pSectorAlloc->field_58[pEvent->field_14] = pThing;
        if (a6 > 0.0)
        {
            for (SithSurfaceAdjoin* i = pSector->adjoins; i; i = i->next)
            {
                flex_t a6a = (i->mirror ? a6 - i->mirror->dist : a6);

                rdVector3 adjoinUnk;
#ifndef OPTIMIZE_AWAY_UNUSED_FIELDS
                adjoinUnk = i->field_1C; // Maybe this was the pAdjoin center...?
#endif
                sithAIAwareness_ProcessEvent(pEvent, i->sector, startPos, &adjoinUnk, a6, a6a, pThing);
            }
        }
    }
}