#include "global.h"
#include "roamer.h"
#include "pokemon.h"
#include "rng.h"
#include "species.h"
#include "event_data.h"

enum
{
    MAP_GRP = 0, // map group
    MAP_NUM = 1, // map number
};

EWRAM_DATA static u8 sLocationHistory[3][2] = {0};
EWRAM_DATA static u8 sRoamerLocation[2] = {0};

static const u8 sRoamerLocations[][6] =
{
    { 0x19, 0x1A, 0x20, 0x21, 0x31, 0xFF },
    { 0x1A, 0x19, 0x20, 0x21, 0xFF, 0xFF },
    { 0x20, 0x1A, 0x19, 0x21, 0xFF, 0xFF },
    { 0x21, 0x20, 0x19, 0x1A, 0x22, 0x26 },
    { 0x22, 0x21, 0x23, 0xFF, 0xFF, 0xFF },
    { 0x23, 0x22, 0x24, 0xFF, 0xFF, 0xFF },
    { 0x24, 0x23, 0x25, 0x26, 0xFF, 0xFF },
    { 0x25, 0x24, 0x26, 0xFF, 0xFF, 0xFF },
    { 0x26, 0x25, 0x21, 0xFF, 0xFF, 0xFF },
    { 0x27, 0x24, 0x28, 0x29, 0xFF, 0xFF },
    { 0x28, 0x27, 0x2A, 0xFF, 0xFF, 0xFF },
    { 0x29, 0x27, 0x2A, 0xFF, 0xFF, 0xFF },
    { 0x2A, 0x28, 0x29, 0x2B, 0xFF, 0xFF },
    { 0x2B, 0x2A, 0x2C, 0xFF, 0xFF, 0xFF },
    { 0x2C, 0x2B, 0x2D, 0xFF, 0xFF, 0xFF },
    { 0x2D, 0x2C, 0x2E, 0xFF, 0xFF, 0xFF },
    { 0x2E, 0x2D, 0x2F, 0xFF, 0xFF, 0xFF },
    { 0x2F, 0x2E, 0x30, 0xFF, 0xFF, 0xFF },
    { 0x30, 0x2F, 0x31, 0xFF, 0xFF, 0xFF },
    { 0x31, 0x30, 0x19, 0xFF, 0xFF, 0xFF },
    { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
};

void ClearRoamerData(void)
{
    memset(&gSaveBlock1Ptr->roamer, 0, sizeof(struct Roamer));
    (&gSaveBlock1Ptr->roamer)->species = SPECIES_LATIAS;
}

void ClearRoamerLocationData(void)
{
    u8 i;

    for (i = 0; i < 3; i++)
    {
        sLocationHistory[i][MAP_GRP] = 0;
        sLocationHistory[i][MAP_NUM] = 0;
    }

    sRoamerLocation[MAP_GRP] = 0;
    sRoamerLocation[MAP_NUM] = 0;
}

static void CreateInitialRoamerMon(bool16 createLatios)
{
    if (!createLatios)
        (&gSaveBlock1Ptr->roamer)->species = SPECIES_LATIAS;
    else
        (&gSaveBlock1Ptr->roamer)->species = SPECIES_LATIOS;

    CreateMon(&gEnemyParty[0], (&gSaveBlock1Ptr->roamer)->species, 40, 0x20, 0, 0, 0, 0);
    (&gSaveBlock1Ptr->roamer)->level = 40;
    (&gSaveBlock1Ptr->roamer)->status = 0;
    (&gSaveBlock1Ptr->roamer)->active = TRUE;
    (&gSaveBlock1Ptr->roamer)->ivs = GetMonData(&gEnemyParty[0], MON_DATA_IVS);
    (&gSaveBlock1Ptr->roamer)->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY);
    (&gSaveBlock1Ptr->roamer)->hp = GetMonData(&gEnemyParty[0], MON_DATA_MAX_HP);
    (&gSaveBlock1Ptr->roamer)->cool = GetMonData(&gEnemyParty[0], MON_DATA_COOL);
    (&gSaveBlock1Ptr->roamer)->beauty = GetMonData(&gEnemyParty[0], MON_DATA_BEAUTY);
    (&gSaveBlock1Ptr->roamer)->cute = GetMonData(&gEnemyParty[0], MON_DATA_CUTE);
    (&gSaveBlock1Ptr->roamer)->smart = GetMonData(&gEnemyParty[0], MON_DATA_SMART);
    (&gSaveBlock1Ptr->roamer)->tough = GetMonData(&gEnemyParty[0], MON_DATA_TOUGH);
    sRoamerLocation[MAP_GRP] = 0;
    sRoamerLocation[MAP_NUM] = sRoamerLocations[Random() % 20][0];
}

void InitRoamer(void)
{
    ClearRoamerData();
    ClearRoamerLocationData();
    CreateInitialRoamerMon(gSpecialVar_0x8004);
}

void UpdateLocationHistoryForRoamer(void)
{
    sLocationHistory[2][MAP_GRP] = sLocationHistory[1][MAP_GRP];
    sLocationHistory[2][MAP_NUM] = sLocationHistory[1][MAP_NUM];

    sLocationHistory[1][MAP_GRP] = sLocationHistory[0][MAP_GRP];
    sLocationHistory[1][MAP_NUM] = sLocationHistory[0][MAP_NUM];

    sLocationHistory[0][MAP_GRP] = gSaveBlock1Ptr->location.mapGroup;
    sLocationHistory[0][MAP_NUM] = gSaveBlock1Ptr->location.mapNum;
}

void RoamerMoveToOtherLocationSet(void)
{
    u8 val = 0;
    struct Roamer *roamer = &gSaveBlock1Ptr->roamer;

    if (!roamer->active)
        return;

    sRoamerLocation[MAP_GRP] = val;

    while (1)
    {
        val = sRoamerLocations[Random() % 20][0];
        if (sRoamerLocation[MAP_NUM] != val)
        {
            sRoamerLocation[MAP_NUM] = val;
            return;
        }
    }
}

void RoamerMove(void)
{
    u8 locSet = 0;

    if ((Random() % 16) == 0)
    {
        RoamerMoveToOtherLocationSet();
    }
    else
    {
        struct Roamer *roamer = &gSaveBlock1Ptr->roamer;

        if (!roamer->active)
            return;

        while (locSet < 20)
        {
            if (sRoamerLocation[MAP_NUM] == sRoamerLocations[locSet][0])
            {
                u8 mapNum;
                while (1)
                {
                    mapNum = sRoamerLocations[locSet][(Random() % 5) + 1];
                    if (!(sLocationHistory[2][MAP_GRP] == 0 && sLocationHistory[2][MAP_NUM] == mapNum) && mapNum != 0xFF)
                        break;
                }
                sRoamerLocation[MAP_NUM] = mapNum;
                return;
            }
            locSet++;
        }
    }
}

bool8 IsRoamerAt(u8 mapGroup, u8 mapNum)
{
    struct Roamer *roamer = &gSaveBlock1Ptr->roamer;

    if (roamer->active && mapGroup == sRoamerLocation[MAP_GRP] && mapNum == sRoamerLocation[MAP_NUM])
        return TRUE;
    else
        return FALSE;
}

void CreateRoamerMonInstance(void)
{
    struct Pokemon *mon;
    struct Roamer *roamer;

    mon = &gEnemyParty[0];
    ZeroEnemyPartyMons();
    roamer = &gSaveBlock1Ptr->roamer;
    CreateMonWithIVsPersonality(mon, roamer->species, roamer->level, roamer->ivs, roamer->personality);
    SetMonData(mon, MON_DATA_STATUS, &gSaveBlock1Ptr->roamer.status);
    SetMonData(mon, MON_DATA_HP, &gSaveBlock1Ptr->roamer.hp);
    SetMonData(mon, MON_DATA_COOL, &gSaveBlock1Ptr->roamer.cool);
    SetMonData(mon, MON_DATA_BEAUTY, &gSaveBlock1Ptr->roamer.beauty);
    SetMonData(mon, MON_DATA_CUTE, &gSaveBlock1Ptr->roamer.cute);
    SetMonData(mon, MON_DATA_SMART, &gSaveBlock1Ptr->roamer.smart);
    SetMonData(mon, MON_DATA_TOUGH, &gSaveBlock1Ptr->roamer.tough);
}

bool8 TryStartRoamerEncounter(void)
{
    if (IsRoamerAt(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == TRUE && (Random() % 4) == 0)
    {
        CreateRoamerMonInstance();
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void UpdateRoamerHPStatus(struct Pokemon *mon)
{
    (&gSaveBlock1Ptr->roamer)->hp = GetMonData(mon, MON_DATA_HP);
    (&gSaveBlock1Ptr->roamer)->status = GetMonData(mon, MON_DATA_STATUS);

    RoamerMoveToOtherLocationSet();
}

void SetRoamerInactive(void)
{
    struct Roamer *roamer = &gSaveBlock1Ptr->roamer;
    roamer->active = FALSE;
}

void GetRoamerLocation(u8 *mapGroup, u8 *mapNum)
{
    *mapGroup = sRoamerLocation[MAP_GRP];
    *mapNum = sRoamerLocation[MAP_NUM];
}
