#include "global.h"
#include "event_data.h"
#include "event_object_movement.h"
#include "field_weather.h"
#include "fieldmap.h"
#include "metatile_behavior.h"
#include "sprite.h"
#include "constants/event_objects.h"
#include "constants/flags.h"
#include "constants/maps.h"
#include "constants/metatile_behaviors.h"
#include "constants/vars.h"

static u8 sub_81D4890(u8);
static bool8 sub_81D4C14(struct EventObject*, u8);
static u8 sub_81D4C9C(struct EventObject*, u8);
static u8 sub_81D4C58(struct EventObject*, u8);
static u8 sub_81D4CE0(struct EventObject*, u8);
static u8 sub_81D4D24(u8);
static bool8 CanMewWalkToCoords(s16, s16);

static EWRAM_DATA u8 sUnknown_0203CF50 = 0;

static s16 sPlayerToMewDeltaX;
static s16 sPlayerToMewDeltaY;
static u8 sMewDirectionCandidates[4];

extern const struct SpritePalette gFieldEffectObjectPaletteInfo1;
extern const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[];

static const s16 sFarawayIslandRockCoords[4][2] =
{
    {21, 16},
    {25, 16},
    {16, 17},
    {20, 20},
};

static u8 GetMewEventObjectId(void)
{
    u8 eventObjectId;
    TryGetEventObjectIdByLocalIdAndMap(1, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &eventObjectId);
    return eventObjectId;
}

u32 GetMewMoveDirection(void)
{
    u8 i;
    int skip;
    struct EventObject *mew = &gEventObjects[GetMewEventObjectId()];

    sPlayerToMewDeltaX = gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x - mew->currentCoords.x;
    sPlayerToMewDeltaY = gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y - mew->currentCoords.y;
    for (i = 0; i < ARRAY_COUNT(sMewDirectionCandidates); i++)
        sMewDirectionCandidates[i] = DIR_NONE;

    if (gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x == gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.x
     && gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y == gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.y)
    {
        return DIR_NONE;
    }

    if (VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) % 8 == 0)
        mew->invisible = 0;
    else
        mew->invisible = 1;

    if (VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) % 9 == 0)
        return DIR_NONE;

    for (i = 0; i < ARRAY_COUNT(sFarawayIslandRockCoords); i++)
    {
        if (gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x == sFarawayIslandRockCoords[i][0])
        {
            skip = 0;
            if (gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y < sFarawayIslandRockCoords[i][1])
            {
                if (mew->currentCoords.y <= sFarawayIslandRockCoords[i][1])
                    skip = 1;
            }
            else
            {
                if (mew->currentCoords.y >= sFarawayIslandRockCoords[i][1])
                    skip = 1;
            }

            if (!skip)
            {
                if (sPlayerToMewDeltaX > 0)
                {
                    if (mew->currentCoords.x + 1 == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y))
                            return DIR_EAST;
                    }
                }
                else if (sPlayerToMewDeltaX < 0)
                {
                    if (mew->currentCoords.x - 1 == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y))
                            return DIR_WEST;
                    }
                }

                if (mew->currentCoords.x == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x)
                {
                    if (sPlayerToMewDeltaY > 0)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1))
                            return DIR_NORTH;
                    }
                    else
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1))
                            return DIR_SOUTH;
                    }
                }
            }
        }

        if (gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y == sFarawayIslandRockCoords[i][1])
        {
            skip = 0;
            if (gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.x < sFarawayIslandRockCoords[i][0])
            {
                if (mew->currentCoords.x <= sFarawayIslandRockCoords[i][0])
                    skip = 1;
            }
            else
            {
                if (mew->currentCoords.x >= sFarawayIslandRockCoords[i][0])
                    skip = 1;
            }

            if (!skip)
            {
                if (sPlayerToMewDeltaY > 0)
                {
                    if (mew->currentCoords.y + 1 == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1))
                            return DIR_SOUTH;
                    }
                }
                else if (sPlayerToMewDeltaY < 0)
                {
                    if (mew->currentCoords.y - 1 == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1))
                            return DIR_NORTH;
                    }
                }

                if (mew->currentCoords.y == gEventObjects[gPlayerAvatar.eventObjectId].previousCoords.y)
                {
                    if (sPlayerToMewDeltaX > 0)
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y))
                            return DIR_WEST;
                    }
                    else
                    {
                        if (CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y))
                            return DIR_EAST;
                    }
                }
            }
        }
    }

    if (sub_81D4C14(mew, 0))
    {
        if (sub_81D4C58(mew, 1))
            return sub_81D4D24(2);
        else if (sub_81D4CE0(mew, 1))
            return sub_81D4D24(2);
        else
            return DIR_NORTH;
    }

    if (sub_81D4C9C(mew, 0))
    {
        if (sub_81D4C58(mew, 1))
            return sub_81D4D24(2);
        else if (sub_81D4CE0(mew, 1))
            return sub_81D4D24(2);
        else
            return DIR_SOUTH;
    }

    if (sub_81D4C58(mew, 0))
    {
        if (sub_81D4C14(mew, 1))
            return sub_81D4D24(2);
        else if (sub_81D4C9C(mew, 1))
            return sub_81D4D24(2);
        else
            return DIR_EAST;
    }

    if (sub_81D4CE0(mew, 0))
    {
        if (sub_81D4C14(mew, 1))
            return sub_81D4D24(2);
        else if (sub_81D4C9C(mew, 1))
            return sub_81D4D24(2);
        else
            return DIR_WEST;
    }

    if (sPlayerToMewDeltaY == 0)
    {
        if (gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.y > mew->currentCoords.y)
        {
            if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1))
                return DIR_NORTH;
        }

        if (gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.y < mew->currentCoords.y)
        {
            if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1))
                return DIR_SOUTH;
        }

        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1))
            return DIR_NORTH;

        if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1))
            return DIR_SOUTH;
    }

    if (sPlayerToMewDeltaX == 0)
    {
        if (gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.x > mew->currentCoords.x)
        {
            if (CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y))
                return DIR_WEST;
        }

        if (gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.x < mew->currentCoords.x)
        {
            if (CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y))
                return DIR_EAST;
        }

        if (CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y))
            return DIR_EAST;

        if (CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y))
            return DIR_WEST;
    }

    return sub_81D4890(DIR_NONE);
}

static bool8 CanMewWalkToCoords(s16 x, s16 y)
{
    if (gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.x == x
     && gEventObjects[gPlayerAvatar.eventObjectId].currentCoords.y == y)
    {
        return FALSE;
    }

    return MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y));
}

static u8 sub_81D4890(u8 ignoredDir)
{
    u8 i;
    u8 count = 0;
    struct EventObject *mew = &gEventObjects[GetMewEventObjectId()];

    for (i = 0; i < ARRAY_COUNT(sMewDirectionCandidates); i++)
        sMewDirectionCandidates[i] = DIR_NONE;

    if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1) == TRUE && ignoredDir != DIR_NORTH)
    {
        sMewDirectionCandidates[count] = DIR_NORTH;
        count++;
    }

    if (CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y) == TRUE && ignoredDir != DIR_EAST)
    {
        sMewDirectionCandidates[count] = DIR_EAST;
        count++;
    }

    if (CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1) == TRUE && ignoredDir != DIR_SOUTH)
    {
        sMewDirectionCandidates[count] = DIR_SOUTH;
        count++;
    }

    if (CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y) == TRUE && ignoredDir != DIR_WEST)
    {
        sMewDirectionCandidates[count] = DIR_WEST;
        count++;
    }

    if (count > 1)
        return sMewDirectionCandidates[VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) % count];
    else
        return sMewDirectionCandidates[0];
}

void UpdateFarawayIslandStepCounter(void)
{
    u16 steps = VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER);
    if (gSaveBlock1Ptr->location.mapNum == MAP_NUM(FARAWAY_ISLAND_INTERIOR)
     && gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(FARAWAY_ISLAND_INTERIOR))
    {
        steps++;
        if (steps >= 9999)
            VarSet(VAR_FARAWAY_ISLAND_STEP_COUNTER, 0);
        else
            VarSet(VAR_FARAWAY_ISLAND_STEP_COUNTER, steps);
    }
}

bool8 EventObjectIsFarawayIslandMew(struct EventObject *eventObject)
{
    if (gSaveBlock1Ptr->location.mapNum == MAP_NUM(FARAWAY_ISLAND_INTERIOR)
     && gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(FARAWAY_ISLAND_INTERIOR))
    {
        if (eventObject->graphicsId == EVENT_OBJ_GFX_MEW)
            return TRUE;
    }

    return FALSE;
}

bool8 IsMewPlayingHideAndSeek(void)
{
    if (gSaveBlock1Ptr->location.mapNum == MAP_NUM(FARAWAY_ISLAND_INTERIOR)
     && gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(FARAWAY_ISLAND_INTERIOR))
    {
        if (FlagGet(FLAG_CAUGHT_MEW) != TRUE && FlagGet(FLAG_HIDE_MEW) != TRUE)
            return TRUE;
    }

    return FALSE;
}

bool8 sub_81D4A58(struct EventObject *eventObject)
{
    if (VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) != 0xFFFF
     && VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) % 4 == 0)
        return TRUE;

    return FALSE;
}

void sub_81D4A90(void)
{
    s16 x;
    s16 y;
    u8 spriteId;
    struct EventObject *mew = &gEventObjects[GetMewEventObjectId()];

    mew->invisible = 0;
    if (gSpecialVar_0x8004 == 1)
    {
        mew->fixedPriority = 1;
        gSprites[mew->spriteId].subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
        gSprites[mew->spriteId].subpriority = 1;
    }
    else
    {
        VarSet(VAR_FARAWAY_ISLAND_STEP_COUNTER, 0xFFFF);
        mew->fixedPriority = 1;
        gSprites[mew->spriteId].subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
        if (gSpecialVar_Facing != DIR_NORTH)
            gSprites[mew->spriteId].subpriority = 1;

        LoadSpritePalette(&gFieldEffectObjectPaletteInfo1);
        UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(gFieldEffectObjectPaletteInfo1.tag));

        x = mew->currentCoords.x;
        y = mew->currentCoords.y;
        sub_80930E0(&x, &y, 8, 8);
        sUnknown_0203CF50 = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[15], x, y, gSprites[mew->spriteId].subpriority - 1);
        if (sUnknown_0203CF50 != MAX_SPRITES)
        {
            struct Sprite *sprite = &gSprites[sUnknown_0203CF50];
            sprite->coordOffsetEnabled = 1;
            sprite->oam.priority = 2;
            sprite->callback = SpriteCallbackDummy;
        }
    }
}

void sub_81D4BEC(void)
{
    if (sUnknown_0203CF50 != MAX_SPRITES)
        DestroySprite(&gSprites[sUnknown_0203CF50]);
}

static bool8 sub_81D4C14(struct EventObject *mew, u8 index)
{
    if (sPlayerToMewDeltaY > 0 && CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y - 1))
    {
        sMewDirectionCandidates[index] = DIR_NORTH;
        return TRUE;
    }

    return FALSE;
}

static u8 sub_81D4C58(struct EventObject *mew, u8 index)
{
    if (sPlayerToMewDeltaX < 0 && CanMewWalkToCoords(mew->currentCoords.x + 1, mew->currentCoords.y))
    {
        sMewDirectionCandidates[index] = DIR_EAST;
        return TRUE;
    }

    return FALSE;
}

static u8 sub_81D4C9C(struct EventObject *mew, u8 index)
{
    if (sPlayerToMewDeltaY < 0 && CanMewWalkToCoords(mew->currentCoords.x, mew->currentCoords.y + 1))
    {
        sMewDirectionCandidates[index] = DIR_SOUTH;
        return TRUE;
    }

    return FALSE;
}

static u8 sub_81D4CE0(struct EventObject *mew, u8 index)
{
    if (sPlayerToMewDeltaX > 0 && CanMewWalkToCoords(mew->currentCoords.x - 1, mew->currentCoords.y))
    {
        sMewDirectionCandidates[index] = DIR_WEST;
        return TRUE;
    }

    return FALSE;
}

static u8 sub_81D4D24(u8 mod)
{
    return sMewDirectionCandidates[VarGet(VAR_FARAWAY_ISLAND_STEP_COUNTER) % mod];
}
