#include "global.h"
#include "bg.h"
#include "decompress.h"
#include "landmark.h"
#include "main.h"
#include "menu.h"
#include "palette.h"
#include "pokenav.h"
#include "region_map.h"
#include "sound.h"
#include "sprite.h"
#include "string_util.h"
#include "task.h"
#include "text_window.h"
#include "window.h"
#include "constants/songs.h"

struct Pokenav5Struct
{
    u8 filler0[0xC];
    bool32 unkC;
    u32 (*unk10)(struct Pokenav5Struct *);
};

struct Pokenav5Struct_2
{
    bool32 (*unk0)(void);
    u32 unk4;
    u16 unk8;
    struct Sprite *cityFeatureTextSprites[3];
    u8 unk18[0x800];
    u8 cityZoomPics[22][0xC8];
};

struct CityZoomPic
{
    u16 mapSecId;
    u16 unk2;
    const u32 *data;
};

static u32 sub_81CC568(struct Pokenav5Struct *);
static u32 sub_81CC5B4(struct Pokenav5Struct *);
static u32 sub_81CC5DC(struct Pokenav5Struct *);
static u32 sub_81CC6F4(int);
static u32 sub_81CCD34(int);
static bool32 sub_81CC6BC(void);
static void sub_81CC9EC(void);
static void sub_81CC9C0(void);
static void sub_81CCD10(void);
static bool32 sub_81CCD24(void);
static void sub_81CCA1C(struct Pokenav5Struct_2 *);
static bool32 sub_81CCAFC(void);
static void sub_81CCB0C(struct Pokenav5Struct_2 *);
static bool32 sub_81CCC4C(struct Pokenav5Struct_2 *);
static void sub_81CCC5C(int);
static bool32 sub_81CCC88(void);
static void sub_81CCE58(void);
static void sub_81CCD70(struct Pokenav5Struct_2 *, int, int);
static void sub_81CCDE8(struct Pokenav5Struct_2 *, int, int);
static void sub_81CCFA4(int);
static void sub_81CCC9C(u8 taskId);
static void sub_81CCF78(void);

extern const LoopedTask gUnknown_086230E4[];
extern const struct BgTemplate gUnknown_086230D8[2];
extern const struct CompressedSpriteSheet gUnknown_086230F8[1];
extern const struct SpritePalette gUnknown_08623100[];
extern const struct WindowTemplate gUnknown_08623110;
extern const u32 gUnknown_08622888[];
extern const u16 gUnknown_08622868[];
extern const u16 gHoennMapZoomIcons_Pal[];
extern const struct CityZoomPic gUnknown_08623118[22];
extern const struct SpriteTemplate gUnknown_086231D0;

u32 sub_81CC4D4(void)
{
    struct Pokenav5Struct *state = AllocSubstruct(3, sizeof(struct Pokenav5Struct));
    if (!state)
        return FALSE;

    if (!AllocSubstruct(16, sizeof(struct RegionMap)))
        return FALSE;

    state->unkC = sub_8124668(gMapHeader.regionMapSectionId);
    if (!state->unkC)
        state->unk10 = sub_81CC568;
    else
        state->unk10 = sub_81CC5B4;

    return TRUE;
}

void sub_81CC524(void)
{
    gSaveBlock2Ptr->regionMapZoom = sub_8124658();
    FreePokenavSubstruct(16);
    FreePokenavSubstruct(3);
}

u32 sub_81CC554(void)
{
    struct Pokenav5Struct *state = GetSubstructPtr(3);
    state->unk10(state);
}

static u32 sub_81CC568(struct Pokenav5Struct *state)
{
    switch (sub_81230AC())
    {
    case 3:
        return 1;
    case 4:
        if (!sub_8124658())
            return 3;
        return 2;
    case 5:
        state->unk10 = sub_81CC5DC;
        return 4;
    }

    return 0;
}

static u32 sub_81CC5B4(struct Pokenav5Struct *state)
{
    if (gMain.newKeys & B_BUTTON)
    {
        state->unk10 = sub_81CC5DC;
        return 4;
    }

    return 0;
}

static u32 sub_81CC5DC(struct Pokenav5Struct *state)
{
    return POKENAV_MENU_1;
}

bool32 sub_81CC5E4(void)
{
    struct Pokenav5Struct *state = GetSubstructPtr(3);
    return state->unkC;
}

bool32 sub_81CC5F4(void)
{
    struct Pokenav5Struct_2 *state = AllocSubstruct(4, sizeof(struct Pokenav5Struct_2));
    if (!state)
        return FALSE;

    state->unk4 = CreateLoopedTask(sub_81CC6F4, 1);
    state->unk0 = sub_81CC6BC;
    return TRUE;
}

void sub_81CC62C(int index)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    state->unk4 = CreateLoopedTask(gUnknown_086230E4[index], 1);
    state->unk0 = sub_81CC6BC;
}

u32 sub_81CC65C(void)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    return state->unk0();
}

void sub_81CC670(void)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    FreeRegionMapIconResources();
    sub_81CC9EC();
    RemoveWindow(state->unk8);
    FreePokenavSubstruct(16);
    FreePokenavSubstruct(4);
    SetPokenavVBlankCallback();
    SetBgMode(0);
}

static void sub_81CC6A4(void)
{
    TransferPlttBuffer();
    LoadOam();
    ProcessSpriteCopyRequests();
    UpdateRegionMapVideoRegs();
}

static bool32 sub_81CC6BC(void)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    return IsLoopedTaskActive(state->unk4);
}

static bool8 sub_81CC6D0(void)
{
    if (sub_81CC5E4())
        return FALSE;

    return gSaveBlock2Ptr->regionMapZoom == 1;
}

static u32 sub_81CC6F4(int taskState)
{
    int var0;
    struct RegionMap *regionMap;
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    switch (taskState)
    {
    case 0:
        SetVBlankCallback_(NULL);
        HideBg(1);
        HideBg(2);
        HideBg(3);
        SetBgMode(1);
        InitBgTemplates(gUnknown_086230D8, ARRAY_COUNT(gUnknown_086230D8));
        regionMap = GetSubstructPtr(16);
        sub_8122CF8(regionMap, &gUnknown_086230D8[1], sub_81CC6D0());
        sub_81CC9C0();
        return 0;
    case 1:
        if (sub_8122DB0())
            return 2;

        if (!sub_81CC5E4())
        {
            CreateRegionMapPlayerIcon(4, 9);
            CreateRegionMapCursor(5, 10);
            sub_812454C();
        }
        else
        {
            sub_8123030(0, 6);
        }
        return 0;
    case 2:
        sub_81CCD10();
        return 1;
    case 3:
        if (sub_81CCD24())
            return 2;

        sub_81CCA1C(state);
        return 1;
    case 4:
        if (sub_81CCAFC())
            return 2;

        sub_81CCB0C(state);
        sub_81C7B40();
        return 0;
    case 5:
        if (sub_81CCC4C(state))
            return 2;

        ShowBg(1);
        ShowBg(2);
        SetVBlankCallback_(sub_81CC6A4);
        return 0;
    case 6:
        if (!sub_81CC6D0())
            var0 = 4;
        else
            var0 = 5;

        LoadLeftHeaderGfxForIndex(var0);
        sub_81C7FA0(var0, 1, 1);
        sub_81C7AC0(1);
        return 0;
    case 7:
        if (IsPaletteFadeActive() || sub_81C8010())
            return 2;
        return 1;
    default:
        return 4;
    }
}

u32 sub_81CC848(int taskState)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    switch (taskState)
    {
    case 0:
        sub_81CCB0C(state);
        return 0;
    case 1:
        if (sub_81CCC4C(state))
            return 2;
        break;
    }

    return 4;
}

u32 sub_81CC878(int taskState)
{
    switch (taskState)
    {
    case 0:
        PlaySE(SE_SELECT);
        sub_81CCC5C(0);
        sub_8123418();
        return 0;
    case 1:
        if (sub_8123514() || sub_81CCC88())
            return 2;

        sub_81C7BA4(1);
        return 0;
    case 2:
        if (IsDma3ManagerBusyWithBgCopy_())
            return 2;

        sub_81C7E14(4);
        break;
    }

    return 4;
}

u32 sub_81CC8D8(int taskState)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    switch (taskState)
    {
    case 0:
        PlaySE(SE_SELECT);
        sub_81CCB0C(state);
        return 0;
    case 1:
        if (sub_81CCC4C(state))
            return 2;

        sub_81CCC5C(1);
        sub_8123418();
        return 0;
    case 2:
        if (sub_8123514() || sub_81CCC88())
            return 2;

        sub_81C7BA4(2);
        return 0;
    case 3:
        if (IsDma3ManagerBusyWithBgCopy_())
            return 2;

        sub_81C7E14(5);
        break;
    }

    return 4;
}

u32 sub_81CC95C(int taskState)
{
    switch (taskState)
    {
    case 0:
        PlaySE(SE_SELECT);
        sub_81C7AC0(0);
        return 0;
    case 1:
        if (IsPaletteFadeActive())
            return 2;

        sub_81C7FDC();
        sub_81C78A0();
        return 0;
    case 2:
        if (MainMenuLoopedTaskIsBusy())
            return 2;

        HideBg(1);
        HideBg(2);
        HideBg(3);
        return 0;
    }

    return 4;
}

static void sub_81CC9C0(void)
{
    int i;
    for (i = 0; i < ARRAY_COUNT(gUnknown_086230F8); i++)
        LoadCompressedSpriteSheet(&gUnknown_086230F8[i]);

    Pokenav_AllocAndLoadPalettes(gUnknown_08623100);
    sub_81CCE58();
}

static void sub_81CC9EC(void)
{
    int i;
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    FreeSpriteTilesByTag(6);
    FreeSpritePaletteByTag(11);
    for (i = 0; i < 3; i++)
        DestroySprite(state->cityFeatureTextSprites[i]);
}

static void sub_81CCA1C(struct Pokenav5Struct_2 *state)
{
    sub_8199DF0(1, PIXEL_FILL(0), 0x40, 1);
    sub_8199DF0(1, PIXEL_FILL(1), 0x41, 1);
    CpuFill16(0x1040, state->unk18, 0x800);
    SetBgTilemapBuffer(1, state->unk18);
    state->unk8 = AddWindow(&gUnknown_08623110);
    LoadUserWindowBorderGfx_(state->unk8, 0x42, 0x40);
    DrawTextBorderOuter(state->unk8, 0x42, 4);
    decompress_and_copy_tile_data_to_vram(1, gUnknown_08622888, 0, 0, 0);
    FillWindowPixelBuffer(state->unk8, PIXEL_FILL(1));
    PutWindowTilemap(state->unk8);
    CopyWindowToVram(state->unk8, 3);
    CopyPaletteIntoBufferUnfaded(gUnknown_08622868, 0x10, 0x20);
    CopyPaletteIntoBufferUnfaded(gHoennMapZoomIcons_Pal, 0x30, 0x20);
    if (!sub_8124658())
        ChangeBgY(1, -0x6000, 0);
    else
        ChangeBgY(1, 0, 0);

    ChangeBgX(1, 0, 0);
}

static bool32 sub_81CCAFC(void)
{
    return free_temp_tile_data_buffers_if_possible();
}

static void sub_81CCB0C(struct Pokenav5Struct_2 *state)
{
    struct RegionMap *regionMap = GetSubstructPtr(16);
    switch (regionMap->iconDrawType)
    {
    case MAPSECTYPE_CITY_CANFLY:
        FillWindowPixelBuffer(state->unk8, PIXEL_FILL(1));
        PutWindowRectTilemap(state->unk8, 0, 0, 12, 2);
        AddTextPrinterParameterized(state->unk8, 7, regionMap->mapSecName, 0, 1, TEXT_SPEED_FF, NULL);
        sub_81CCD70(state, regionMap->mapSecId, regionMap->posWithinMapSec);
        CopyWindowToVram(state->unk8, 3);
        sub_81CCFA4(0);
        break;
    case MAPSECTYPE_CITY_CANTFLY:
        FillWindowPixelBuffer(state->unk8, PIXEL_FILL(1));
        PutWindowRectTilemap(state->unk8, 0, 0, 12, 2);
        AddTextPrinterParameterized(state->unk8, 7, regionMap->mapSecName, 0, 1, TEXT_SPEED_FF, NULL);
        FillBgTilemapBufferRect(1, 0x1041, 17, 6, 12, 11, 17);
        CopyWindowToVram(state->unk8, 3);
        sub_81CCFA4(1);
        break;
    case MAPSECTYPE_PLAIN:
    case MAPSECTYPE_BATTLE_FRONTIER:
        FillWindowPixelBuffer(state->unk8, PIXEL_FILL(1));
        PutWindowTilemap(state->unk8);
        AddTextPrinterParameterized(state->unk8, 7, regionMap->mapSecName, 0, 1, TEXT_SPEED_FF, NULL);
        sub_81CCDE8(state, regionMap->mapSecId, regionMap->posWithinMapSec);
        CopyWindowToVram(state->unk8, 3);
        sub_81CCFA4(1);
        break;
    case MAPSECTYPE_NONE:
        FillBgTilemapBufferRect(1, 0x1041, 17, 4, 12, 13, 17);
        CopyBgTilemapBufferToVram(1);
        sub_81CCFA4(1);
        break;
    }
}

static bool32 sub_81CCC4C(struct Pokenav5Struct_2 *state)
{
    return IsDma3ManagerBusyWithBgCopy();
}

static void sub_81CCC5C(int arg0)
{
    u8 taskId = CreateTask(sub_81CCC9C, 3);
    gTasks[taskId].data[0] = arg0;
}

static bool32 sub_81CCC88(void)
{
    return FuncIsActiveTask(sub_81CCC9C);
}

static void sub_81CCC9C(u8 taskId)
{
    if (gTasks[taskId].data[0])
    {
        if (ChangeBgY(1, 0x480, 1) >= 0)
        {
            ChangeBgY(1, 0, 0);
            DestroyTask(taskId);
        }
        
        sub_81CCF78();
    }
    else
    {
        if (ChangeBgY(1, 0x480, 2) <= -0x6000)
        {
            ChangeBgY(1, -0x6000, 0);
            DestroyTask(taskId);
        }

        sub_81CCF78();
    }
}

static void sub_81CCD10(void)
{
    CreateLoopedTask(sub_81CCD34, 1);
}

static bool32 sub_81CCD24(void)
{
    return FuncIsActiveLoopedTask(sub_81CCD34);
}

static u32 sub_81CCD34(int taskState)
{
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    if (taskState < (int)ARRAY_COUNT(gUnknown_08623118))
    {
        LZ77UnCompWram(gUnknown_08623118[taskState].data, state->cityZoomPics[taskState]);
        return 1;
    }

    return 4;
}

static void sub_81CCD70(struct Pokenav5Struct_2 *state, int mapSecId, int pos)
{
    int i;
    for (i = 0; i < (int)ARRAY_COUNT(gUnknown_08623118) && (gUnknown_08623118[i].mapSecId != mapSecId || gUnknown_08623118[i].unk2 != pos); i++)
        ;

    if (i == ARRAY_COUNT(gUnknown_08623118))
        return;

    FillBgTilemapBufferRect_Palette0(1, 0x1041, 17, 6, 12, 11);
    CopyToBgTilemapBufferRect(1, state->cityZoomPics[i], 18, 6, 10, 10);
}

static void sub_81CCDE8(struct Pokenav5Struct_2 *state, int mapSecId, int pos)
{
    int i = 0;
    while (1)
    {
        const u8 *landmarkName = GetLandmarkName(mapSecId, pos, i);
        if (!landmarkName)
            break;

        StringCopyPadded(gStringVar1, landmarkName, CHAR_SPACE, 12);
        AddTextPrinterParameterized(state->unk8, 7, gStringVar1, 0, i * 16 + 17, TEXT_SPEED_FF, NULL);
        i++;
    }
}

static void sub_81CCE58(void)
{
    int i;
    int y;
    struct Sprite *sprite;
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);

    if (!sub_8124658())
        y = 228;
    else
        y = 132;

    for (i = 0; i < 3; i++)
    {
        u8 spriteId = CreateSprite(&gUnknown_086231D0, 152 + i * 32, y, 8);
        sprite = &gSprites[spriteId];
        sprite->data[0] = 0;
        sprite->data[1] = i * 4;
        sprite->data[2] = sprite->oam.tileNum;
        sprite->data[3] = 150;
        sprite->data[4] = i * 4;
        sprite->oam.tileNum += i * 4;
        state->cityFeatureTextSprites[i] = sprite;
    }
}

void sub_81CCEF4(struct Sprite *sprite)
{
    if (sprite->data[3])
    {
        sprite->data[3]--;
        return;
    }

    if (++sprite->data[0] > 11)
        sprite->data[0] = 0;

    if (++sprite->data[1] > 60)
        sprite->data[1] = 0;

    sprite->oam.tileNum = sprite->data[2] + sprite->data[1];
    if (sprite->data[5] < 4)
    {
        if (sprite->data[0] == 0)
        {
            sprite->data[5]++;
            sprite->data[3] = 120;
        }
    }
    else
    {
        if (sprite->data[1] == sprite->data[4])
        {
            sprite->data[5] = 0;
            sprite->data[0] = 0;
            sprite->data[3] = 120;
        }
    }
}

static void sub_81CCF78(void)
{
    int i;
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    int y = 132 - (GetBgY(1) >> 8);
    for (i = 0; i < 3; i++)
        state->cityFeatureTextSprites[i]->pos1.y = y;
}

static void sub_81CCFA4(int invisible)
{
    int i;
    struct Pokenav5Struct_2 *state = GetSubstructPtr(4);
    for (i = 0; i < 3; i++)
        state->cityFeatureTextSprites[i]->invisible = invisible;
}
