#include "global.h"
#include "pokenav.h"
#include "constants/songs.h"
#include "sound.h"
#include "constants/rgb.h"
#include "palette.h"
#include "bg.h"
#include "window.h"
#include "strings.h"
#include "graphics.h"
#include "decompress.h"
#include "gpu_regs.h"
#include "menu.h"
#include "dma3.h"

enum
{
    POKENAV_GFX_MAIN_MENU,
    POKENAV_GFX_CONDITION_MENU,
    POKENAV_GFX_RIBBONS_MENU,
    POKENAV_GFX_MATCH_CALL_MENU,
    // One of these is for the zoomed-in map, and the other is for the
    // zoomed-out map. Don't know which is which yet.
    POKENAV_GFX_MAP_MENU_UNK0,
    POKENAV_GFX_MAP_MENU_UNK1,
    POKENAV_GFX_PARTY_MENU,
    POKENAV_GFX_SEARCH_MENU,
    POKENAV_GFX_COOL_MENU,
    POKENAV_GFX_BEAUTY_MENU,
    POKENAV_GFX_CUTE_MENU,
    POKENAV_GFX_SMART_MENU,
    POKENAV_GFX_TOUGH_MENU,
    POKENAV_GFX_MENUS_END,
};

#define POKENAV_GFX_SUBMENUS_START POKENAV_GFX_PARTY_MENU

struct PokenavMainMenuResources
{
    void (*unk0)(u32);
    u32 (*unk4)(void);
    u32 unk8;
    u32 currentTaskId;
    u32 unk10;
    u32 unk14;
    struct Sprite *spinningPokenav;
    struct Sprite *leftHeaderSprites[2];
    struct Sprite *submenuLeftHeaderSprites[2];
    u8 tilemapBuffer[0x800];
};

// This struct uses a 32bit tag, and doesn't have a size field.
// Needed to match LoadLeftHeaderGfxForSubMenu.
struct CompressedSpriteSheetNoSize
{
    const u32 *data;  // LZ77 compressed palette data
    u32 tag;
};

static void CleanupPokenavMainMenuResources(void);
static void LoadLeftHeaderGfxForSubMenu(u32 arg0);
static void LoadLeftHeaderGfxForMenu(u32 index);
static void HideLeftHeaderSubmenuSprites(bool32 isOnRightSide);
static void HideLeftHeaderSprites(bool32 isOnRightSide);
static void ShowLeftHeaderSprites(u32 startY, bool32 isOnRightSide);
static void ShowLeftHeaderSubmenuSprites(u32 startY, bool32 isOnRightSide);
static void MoveLeftHeader(struct Sprite *sprite, int startX, int endX, int duration);
static void SpriteCB_MoveLeftHeader(struct Sprite *sprite);
static void InitPokenavMainMenuResources(void);
static void InitHoennMapHeaderSprites(void);
static void sub_81C7B74(void);
static u32 LoopedTask_ScrollMenuHeaderDown(int a0);
static u32 LoopedTask_ScrollMenuHeaderUp(int a0);
static void sub_81C7BF8(u32 a0);
static void SpriteCB_SpinningPokenav(struct Sprite* sprite);
static u32 LoopedTask_InitPokenavMenu(int a0);

const u16 gSpinningPokenavPaletteData[] = INCBIN_U16("graphics/pokenav/icon2.gbapal");
const u32 gSpinningPokenavGfx[] = INCBIN_U32("graphics/pokenav/icon2.4bpp.lz");
const u32 gUnused_SpinningPokenavGfx2[] = INCBIN_U32("graphics/pokenav/icon2_unused.4bpp.lz");

const struct BgTemplate gPokenavMainMenuBgTemplates[] =
{
    {
        .bg = 0,
        .charBaseIndex = 0,
        .mapBaseIndex = 5,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 0,
        .baseTile = 0,
    }
};

const struct WindowTemplate gUnknown_0861FA08[2] =
{
    {
        .bg = 0,
        .tilemapLeft = 1,
        .tilemapTop = 22,
        .width = 16,
        .height = 2,
        .paletteNum = 0,
        .baseBlock = 0x36,
    },
    {
        .bg = 0xFF,
        .tilemapLeft = 0,
        .tilemapTop = 0,
        .width = 0,
        .height = 0,
        .paletteNum = 0,
        .baseBlock = 0,
    },
};

const u8 *const sHelpBarTexts[12] =
{
    gText_Pokenav_ClearButtonList,
    gText_PokenavMap_ZoomedOutButtons,
    gText_PokenavMap_ZoomedInButtons,
    gText_PokenavCondition_MonListButtons,
    gText_PokenavCondition_MonStatusButtons,
    gText_PokenavCondition_MarkingButtons,
    gText_PokenavMatchCall_TrainerListButtons,
    gText_PokenavMatchCall_CallMenuButtons,
    gText_PokenavMatchCall_CheckTrainerButtons,
    gText_PokenavRibbons_MonListButtons,
    gText_PokenavRibbons_RibbonListButtons,
    gText_PokenavRibbons_RibbonCheckButtons,
};

const u8 gMenuButtonReminderColor[3] =
{
    4, 1, 2
};

static const struct CompressedSpriteSheet gSpinningPokenavSpriteSheet[] =
{
    {
        .data = gSpinningPokenavGfx,
        .size = 0x1000,
        .tag = 0,
    }
};

static const struct SpritePalette gSpinningNavgearPalettes[] =
{
    {
        .data = gSpinningPokenavPaletteData,
        .tag = 0,
    },
    {}
};

static const struct CompressedSpriteSheet sPokenavHoennMapLeftHeaderSpriteSheet =
{
    .data = gPokenavLeftHeaderHoennMap_Gfx,
    .size = 0xC00,
    .tag = 2
};

static const struct CompressedSpriteSheet sPokenavMenuLeftHeaderSpriteSheets[] =
{
    [POKENAV_GFX_MAIN_MENU] = {
        .data = gPokenavLeftHeaderMainMenu_Gfx,
        .size = 0x20,
        .tag = 3
    },
    [POKENAV_GFX_CONDITION_MENU] = {
        .data = gPokenavLeftHeaderCondition_Gfx,
        .size = 0x20,
        .tag = 1
    },
    [POKENAV_GFX_RIBBONS_MENU] = {
        .data = gPokenavLeftHeaderRibbons_Gfx,
        .size = 0x20,
        .tag = 2
    },
    [POKENAV_GFX_MATCH_CALL_MENU] = {
        .data = gPokenavLeftHeaderMatchCall_Gfx,
        .size = 0x20,
        .tag = 4
    },
    [POKENAV_GFX_MAP_MENU_UNK0] = {
        .data = gPokenavLeftHeaderHoennMap_Gfx,
        .size = 0x20,
        .tag = 0
    },
    [POKENAV_GFX_MAP_MENU_UNK1] = {
        .data = gPokenavLeftHeaderHoennMap_Gfx,
        .size = 0x40,
        .tag = 0
    }
};

static const struct CompressedSpriteSheetNoSize sPokenavSubMenuLeftHeaderSpriteSheets[] =
{
    [POKENAV_GFX_PARTY_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderParty_Gfx,
        .tag = 1
    },
    [POKENAV_GFX_SEARCH_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderSearch_Gfx,
        .tag = 1
    },
    [POKENAV_GFX_COOL_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderCool_Gfx,
        .tag = 4
    },
    [POKENAV_GFX_BEAUTY_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderBeauty_Gfx,
        .tag = 1
    },
    [POKENAV_GFX_CUTE_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderCute_Gfx,
        .tag = 2
    },
    [POKENAV_GFX_SMART_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderSmart_Gfx,
        .tag = 0
    },
    [POKENAV_GFX_TOUGH_MENU - POKENAV_GFX_SUBMENUS_START] = {
        .data = gPokenavLeftHeaderTough_Gfx,
        .tag = 0
    }
};

static const struct OamData sSpinningPokenavSpriteOam =
{
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .bpp = ST_OAM_4BPP,
    .shape = SPRITE_SHAPE(32x32),
    .x = 0,
    .size = SPRITE_SIZE(32x32),
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
};

static const union AnimCmd sSpinningPokenavAnims[] =
{
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_FRAME(16, 8),
    ANIMCMD_FRAME(32, 8),
    ANIMCMD_FRAME(48, 8),
    ANIMCMD_FRAME(64, 8),
    ANIMCMD_FRAME(80, 8),
    ANIMCMD_FRAME(96, 8),
    ANIMCMD_FRAME(112, 8),
    ANIMCMD_JUMP(0)
};

static const union AnimCmd *const sSpinningPokenavAnimTable[] =
{
    sSpinningPokenavAnims
};

static const struct SpriteTemplate sSpinningPokenavSpriteTemplate =
{
    .tileTag = 0,
    .paletteTag = 0,
    .oam = &sSpinningPokenavSpriteOam,
    .anims = sSpinningPokenavAnimTable,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_SpinningPokenav
};

static const struct OamData sPokenavLeftHeaderHoennMapSpriteOam =
{
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .bpp = ST_OAM_4BPP,
    .shape = SPRITE_SHAPE(64x32),
    .x = 0,
    .size = SPRITE_SIZE(64x32),
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
};

static const struct OamData sUnknown_0861FB24 =
{
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .bpp = 0,
    .shape = SPRITE_SHAPE(32x16),
    .x = 0,
    .matrixNum = 0,
    .size = SPRITE_SIZE(32x16),
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
};

static const struct SpriteTemplate sPokenavLeftHeaderHoennMapSpriteTemplate =
{
    .tileTag = 2,
    .paletteTag = 1,
    .oam = &sPokenavLeftHeaderHoennMapSpriteOam,
    .anims = gDummySpriteAnimTable,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCallbackDummy
};

static const struct SpriteTemplate sUnknown_0861FB44 =
{
    .tileTag = 2,
    .paletteTag = 2,
    .oam = &sUnknown_0861FB24,
    .anims = gDummySpriteAnimTable,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCallbackDummy
};

bool32 InitPokenavMainMenu(void)
{
    struct PokenavMainMenuResources *structPtr;
    
    structPtr = AllocSubstruct(0, sizeof(struct PokenavMainMenuResources));
    if (structPtr == NULL)
        return FALSE;

    ResetSpriteData();
    FreeAllSpritePalettes();
    structPtr->currentTaskId = CreateLoopedTask(LoopedTask_InitPokenavMenu, 1);
    return TRUE;
}

u32 PokenavMainMenuLoopedTaskIsActive(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    return IsLoopedTaskActive(structPtr->currentTaskId);
}

void ShutdownPokenav(void)
{
    PlaySE(SE_PN_OFF);
    sub_81CAADC();
    BeginNormalPaletteFade(0xFFFFFFFF, -1, 0, 16, RGB_BLACK);
}

bool32 WaitForPokenavShutdownFade(void)
{
    if (!gPaletteFade.active)
    {
        sub_81C99D4();
        CleanupPokenavMainMenuResources();
        FreeAllWindowBuffers();
        return FALSE;
    }

    return TRUE;
}

static u32 LoopedTask_InitPokenavMenu(int a0)
{
    struct PokenavMainMenuResources *structPtr;

    switch (a0)
    {
    case 0:
        SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
        FreeAllWindowBuffers();
        ResetBgsAndClearDma3BusyFlags(0);
        InitBgsFromTemplates(0, gPokenavMainMenuBgTemplates, ARRAY_COUNT(gPokenavMainMenuBgTemplates));
        ResetBgPositions();
        reset_temp_tile_data_buffers();
        return LT_INC_AND_CONTINUE;
    case 1:
        structPtr = GetSubstructPtr(0);
        decompress_and_copy_tile_data_to_vram(0, &gPokenavHeader_Gfx, 0, 0, 0);
        SetBgTilemapBuffer(0, structPtr->tilemapBuffer);
        CopyToBgTilemapBuffer(0, &gPokenavHeader_Tilemap, 0, 0);
        CopyPaletteIntoBufferUnfaded(gPokenavHeader_Pal, 0, 0x20);
        CopyBgTilemapBufferToVram(0);
        return LT_INC_AND_PAUSE;
    case 2:
        if (free_temp_tile_data_buffers_if_possible())
            return LT_PAUSE;

        sub_81C7B74();
        return LT_INC_AND_PAUSE;
    case 3:
        if (IsDma3ManagerBusyWithBgCopy())
            return LT_PAUSE;

        InitPokenavMainMenuResources();
        InitHoennMapHeaderSprites();
        ShowBg(0);
        return LT_FINISH;
    default:
        return LT_FINISH;
    }
}

void sub_81C7834(void *func1, void *func2) // Fix types later.
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    structPtr->unk0 = func1;
    structPtr->unk4 = func2;
    structPtr->unk8 = 0;
}

void sub_81C7850(u32 a0)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    structPtr->unk8 = 0;
    structPtr->unk0(a0);
}

u32 sub_81C786C(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    return structPtr->unk4();
}

void sub_81C7880(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    structPtr->currentTaskId = CreateLoopedTask(LoopedTask_ScrollMenuHeaderDown, 4);
}

void sub_81C78A0(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    structPtr->currentTaskId = CreateLoopedTask(LoopedTask_ScrollMenuHeaderUp, 4);
}

bool32 MainMenuLoopedTaskIsBusy(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);
    return IsLoopedTaskActive(structPtr->currentTaskId);
}

static u32 LoopedTask_ScrollMenuHeaderDown(int a0)
{
    switch (a0)
    {
    default:
        return LT_FINISH;
    case 1:
        return LT_INC_AND_PAUSE;
    case 0:
        return LT_INC_AND_PAUSE;
    case 2:
        if (ChangeBgY(0, 384, 1) >= 0x2000u)
        {
            ChangeBgY(0, 0x2000, 0);
            return LT_FINISH;
        }

        return LT_PAUSE;
    }
}

static u32 LoopedTask_ScrollMenuHeaderUp(int a0)
{
    if (ChangeBgY(0, 384, 2) <= 0)
    {
        ChangeBgY(0, 0, 0);
        return 4;
    }
    return 2;
}

void CopyPaletteIntoBufferUnfaded(const u16 *palette, u32 bufferOffset, u32 size)
{
    CpuCopy16(palette, gPlttBufferUnfaded + bufferOffset, size);
}

void Pokenav_AllocAndLoadPalettes(const struct SpritePalette *palettes)
{
    const struct SpritePalette *current;
    u32 index;

    for (current = palettes; current->data != NULL; current++)
    {
        index = AllocSpritePalette(current->tag);
        if (index == 0xFF)
        {
            break;
        }
        else
        {
            index = (index * 16) + 0x100;
            CopyPaletteIntoBufferUnfaded(current->data, index, 0x20);
        }
    }
}

void sub_81C7990(u32 a0, u16 a1)
{
    CpuFill16(a1, gPlttBufferFaded + 0x100 + (a0 * 16), 16 * sizeof(u16));
}

NAKED
void sub_81C79BC(const u16 *a0, const u16 *a1, u32 a2, u32 a3, u32 a4, u16 *a5)
{
    asm(".syntax unified\n\
    push {r4-r7,lr}\n\
    mov r7, r10\n\
    mov r6, r9\n\
    mov r5, r8\n\
    push {r5-r7}\n\
    sub sp, 0xC\n\
    str r0, [sp]\n\
    str r1, [sp, 0x4]\n\
    mov r10, r2\n\
    str r3, [sp, 0x8]\n\
    ldr r0, [sp, 0x2C]\n\
    cmp r0, 0\n\
    bne _081C79E4\n\
    ldr r2, =0x001fffff\n\
    mov r1, r10\n\
    ands r2, r1\n\
    ldr r0, [sp]\n\
    b _081C79F4\n\
    .pool\n\
_081C79E4:\n\
    ldr r2, [sp, 0x2C]\n\
    ldr r0, [sp, 0x8]\n\
    cmp r2, r0\n\
    blt _081C7A00\n\
    ldr r2, =0x001fffff\n\
    mov r1, r10\n\
    ands r2, r1\n\
    ldr r0, [sp, 0x4]\n\
_081C79F4:\n\
    ldr r1, [sp, 0x30]\n\
    bl CpuSet\n\
    b _081C7AAE\n\
    .pool\n\
_081C7A00:\n\
    movs r2, 0x1\n\
    negs r2, r2\n\
    add r10, r2\n\
    b _081C7AA6\n\
_081C7A08:\n\
    ldr r1, [sp]\n\
    ldrh r0, [r1]\n\
    movs r2, 0x1F\n\
    mov r9, r2\n\
    mov r1, r9\n\
    ands r1, r0\n\
    mov r9, r1\n\
    lsls r0, 16\n\
    lsrs r2, r0, 21\n\
    movs r1, 0x1F\n\
    ands r1, r2\n\
    mov r8, r1\n\
    lsrs r7, r0, 26\n\
    movs r2, 0x1F\n\
    ands r7, r2\n\
    ldr r0, [sp, 0x4]\n\
    ldrh r4, [r0]\n\
    movs r0, 0x1F\n\
    ands r0, r4\n\
    mov r1, r9\n\
    subs r0, r1\n\
    lsls r0, 8\n\
    ldr r1, [sp, 0x8]\n\
    bl __divsi3\n\
    ldr r2, [sp, 0x2C]\n\
    adds r6, r0, 0\n\
    muls r6, r2\n\
    asrs r6, 8\n\
    lsls r4, 16\n\
    lsrs r0, r4, 21\n\
    movs r1, 0x1F\n\
    ands r0, r1\n\
    mov r2, r8\n\
    subs r0, r2\n\
    lsls r0, 8\n\
    ldr r1, [sp, 0x8]\n\
    bl __divsi3\n\
    ldr r1, [sp, 0x2C]\n\
    adds r5, r0, 0\n\
    muls r5, r1\n\
    asrs r5, 8\n\
    lsrs r4, 26\n\
    movs r2, 0x1F\n\
    ands r4, r2\n\
    subs r4, r7\n\
    lsls r4, 8\n\
    adds r0, r4, 0\n\
    ldr r1, [sp, 0x8]\n\
    bl __divsi3\n\
    ldr r1, [sp, 0x2C]\n\
    muls r0, r1\n\
    asrs r0, 8\n\
    add r6, r9\n\
    movs r2, 0x1F\n\
    ands r6, r2\n\
    add r5, r8\n\
    ands r5, r2\n\
    adds r0, r7, r0\n\
    ands r0, r2\n\
    lsls r0, 10\n\
    lsls r5, 5\n\
    orrs r0, r5\n\
    orrs r0, r6\n\
    ldr r1, [sp, 0x30]\n\
    strh r0, [r1]\n\
    ldr r2, [sp]\n\
    adds r2, 0x2\n\
    str r2, [sp]\n\
    ldr r0, [sp, 0x4]\n\
    adds r0, 0x2\n\
    str r0, [sp, 0x4]\n\
    adds r1, 0x2\n\
    str r1, [sp, 0x30]\n\
    movs r1, 0x1\n\
    negs r1, r1\n\
    add r10, r1\n\
_081C7AA6:\n\
    movs r0, 0x1\n\
    negs r0, r0\n\
    cmp r10, r0\n\
    bne _081C7A08\n\
_081C7AAE:\n\
    add sp, 0xC\n\
    pop {r3-r5}\n\
    mov r8, r3\n\
    mov r9, r4\n\
    mov r10, r5\n\
    pop {r4-r7}\n\
    pop {r0}\n\
    bx r0\n\
    .syntax divided");
}

void sub_81C7AC0(int a0)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    switch (a0)
    {
    case 0:
        BeginNormalPaletteFade(structPtr->unk14, -2, 0, 16, RGB_BLACK);
        break;
    case 1:
        BeginNormalPaletteFade(structPtr->unk14, -2, 16, 0, RGB_BLACK);
        break;
    case 2:
        BeginNormalPaletteFade(0xFFFFFFFF, -2, 0, 16, RGB_BLACK);
        break;
    case 3:
        BeginNormalPaletteFade(0xFFFFFFFF, -2, 16, 0, RGB_BLACK);
        break;
    }
}

bool32 IsPaletteFadeActive(void)
{
    return gPaletteFade.active;
}

void sub_81C7B40(void)
{
    BlendPalettes(0xFFFEFFFE, 16, RGB_BLACK);
}

void InitBgTemplates(const struct BgTemplate *templates, int count)
{
    int i;

    for (i = 0; i < count; i++)
        InitBgFromTemplate(templates++);
}

static void sub_81C7B74(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    InitWindows(&gUnknown_0861FA08[0]);
    structPtr->unk10 = 0;
    sub_81C7BF8(structPtr->unk10);
    PutWindowTilemap(structPtr->unk10);
    CopyWindowToVram(structPtr->unk10, 3); // TODO: Use a defined constant here.
}

void sub_81C7BA4(u32 helpBarIndex)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    sub_81C7BF8(structPtr->unk10);
    AddTextPrinterParameterized3(structPtr->unk10, 1, 0, 1, gMenuButtonReminderColor, 0, sHelpBarTexts[helpBarIndex]);
}

bool32 IsDma3ManagerBusyWithBgCopy_(void)
{
    return IsDma3ManagerBusyWithBgCopy();
}

static void sub_81C7BF8(u32 windowId)
{
    FillWindowPixelBuffer(windowId, PIXEL_FILL(4));
    FillWindowPixelRect(windowId, PIXEL_FILL(5), 0, 0, 0x80, 1);
}

static void InitPokenavMainMenuResources(void)
{
    int i;
    u8 spriteId;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    for (i = 0; i < ARRAY_COUNT(gSpinningPokenavSpriteSheet); i++)
        LoadCompressedSpriteSheet(&gSpinningPokenavSpriteSheet[i]);

    Pokenav_AllocAndLoadPalettes(gSpinningNavgearPalettes);
    structPtr->unk14 = ~1 & ~(0x10000 << IndexOfSpritePaletteTag(0));
    spriteId = CreateSprite(&sSpinningPokenavSpriteTemplate, 220, 12, 0);
    structPtr->spinningPokenav = &gSprites[spriteId];
}

static void CleanupPokenavMainMenuResources(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    DestroySprite(structPtr->spinningPokenav);
    FreeSpriteTilesByTag(0);
    FreeSpritePaletteByTag(0);
}

static void SpriteCB_SpinningPokenav(struct Sprite *sprite)
{
    // If the background starts scrolling, follow it.
    sprite->pos2.y = (GetBgY(0) / 256u) * -1;
}

struct Sprite *PauseSpinningPokenavSprite(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    structPtr->spinningPokenav->callback = SpriteCallbackDummy;
    return structPtr->spinningPokenav;
}

void ResumeSpinningPokenavSprite(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    structPtr->spinningPokenav->pos1.x = 220;
    structPtr->spinningPokenav->pos1.y = 12;
    structPtr->spinningPokenav->callback = SpriteCB_SpinningPokenav;
    structPtr->spinningPokenav->invisible = FALSE;
    structPtr->spinningPokenav->oam.priority = 0;
    structPtr->spinningPokenav->subpriority = 0;
}

static void InitHoennMapHeaderSprites(void)
{
    int i, spriteId;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    LoadCompressedSpriteSheet(&sPokenavHoennMapLeftHeaderSpriteSheet);
    AllocSpritePalette(1);
    AllocSpritePalette(2);
    for (i = 0; i < (int)ARRAY_COUNT(structPtr->leftHeaderSprites); i++)
    {
        spriteId = CreateSprite(&sPokenavLeftHeaderHoennMapSpriteTemplate, 0, 0, 1);
        structPtr->leftHeaderSprites[i] = &gSprites[spriteId];
        structPtr->leftHeaderSprites[i]->invisible = TRUE;
        structPtr->leftHeaderSprites[i]->pos2.x = i * 64;

        spriteId = CreateSprite(&sUnknown_0861FB44, 0, 0, 2);
        structPtr->submenuLeftHeaderSprites[i] = &gSprites[spriteId];
        structPtr->submenuLeftHeaderSprites[i]->invisible = TRUE;
        structPtr->submenuLeftHeaderSprites[i]->pos2.x = i * 32;
        structPtr->submenuLeftHeaderSprites[i]->pos2.y = 18;
        structPtr->submenuLeftHeaderSprites[i]->oam.tileNum += (i * 8) + 64;
    }
}

void LoadLeftHeaderGfxForIndex(u32 arg0)
{
    if (arg0 < POKENAV_GFX_SUBMENUS_START)
        LoadLeftHeaderGfxForMenu(arg0);
    else
        LoadLeftHeaderGfxForSubMenu(arg0 - POKENAV_GFX_SUBMENUS_START);
}

void sub_81C7E14(u32 arg0)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (arg0 == 4)
        structPtr->leftHeaderSprites[1]->oam.tileNum = GetSpriteTileStartByTag(2) + 32;
    else
        structPtr->leftHeaderSprites[1]->oam.tileNum = GetSpriteTileStartByTag(2) + 64;
}

static void LoadLeftHeaderGfxForMenu(u32 index)
{
    struct PokenavMainMenuResources *structPtr;
    u32 size, tag;

    if (index >= POKENAV_GFX_SUBMENUS_START)
        return;

    structPtr = GetSubstructPtr(0);
    tag = sPokenavMenuLeftHeaderSpriteSheets[index].tag;
    size = GetDecompressedDataSize(sPokenavMenuLeftHeaderSpriteSheets[index].data);
    LoadPalette(&gPokenavLeftHeader_Pal[tag * 16], (IndexOfSpritePaletteTag(1) * 16) + 0x100, 0x20);
    LZ77UnCompWram(sPokenavMenuLeftHeaderSpriteSheets[index].data, gDecompressionBuffer);
    RequestDma3Copy(gDecompressionBuffer, (void *)VRAM + 0x10000 + (GetSpriteTileStartByTag(2) * 32), size, 1);
    structPtr->leftHeaderSprites[1]->oam.tileNum = GetSpriteTileStartByTag(2) + sPokenavMenuLeftHeaderSpriteSheets[index].size;

    if (index == POKENAV_GFX_MAP_MENU_UNK0 || index == POKENAV_GFX_MAP_MENU_UNK1)
        structPtr->leftHeaderSprites[1]->pos2.x = 56;
    else
        structPtr->leftHeaderSprites[1]->pos2.x = 64;
}

static void LoadLeftHeaderGfxForSubMenu(u32 arg0)
{
    u32 size, tag;

    if (arg0 >= POKENAV_GFX_MENUS_END - POKENAV_GFX_SUBMENUS_START)
        return;

    tag = sPokenavSubMenuLeftHeaderSpriteSheets[arg0].tag;
    size = GetDecompressedDataSize(sPokenavSubMenuLeftHeaderSpriteSheets[arg0].data);
    LoadPalette(&gPokenavLeftHeader_Pal[tag * 16], (IndexOfSpritePaletteTag(2) * 16) + 0x100, 0x20);
    LZ77UnCompWram(sPokenavSubMenuLeftHeaderSpriteSheets[arg0].data, &gDecompressionBuffer[0x1000]);
    RequestDma3Copy(&gDecompressionBuffer[0x1000], (void *)VRAM + 0x10800 + (GetSpriteTileStartByTag(2) * 32), size, 1);
}

void sub_81C7FA0(u32 arg0, bool32 arg1, bool32 arg2)
{
    u32 var;

    if (!arg1)
        var = 0x30;
    else
        var = 0x10;

    if (arg0 < 6)
        ShowLeftHeaderSprites(var, arg2);
    else
        ShowLeftHeaderSubmenuSprites(var, arg2);
}

void sub_81C7FC4(u32 arg0, bool32 arg1)
{
    if (arg0 < 6)
        HideLeftHeaderSprites(arg1);
    else
        HideLeftHeaderSubmenuSprites(arg1);
}

void sub_81C7FDC(void)
{
    int i;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    for (i = 0; i < (int)ARRAY_COUNT(structPtr->leftHeaderSprites); i++)
    {
        structPtr->leftHeaderSprites[i]->invisible = TRUE;
        structPtr->submenuLeftHeaderSprites[i]->invisible = TRUE;
    }
}

bool32 sub_81C8010(void)
{
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (structPtr->leftHeaderSprites[0]->callback == SpriteCallbackDummy && structPtr->submenuLeftHeaderSprites[0]->callback == SpriteCallbackDummy)
        return FALSE;
    else
        return TRUE;
}

static void ShowLeftHeaderSprites(u32 startY, bool32 isOnRightSide)
{
    int start, end, i;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (!isOnRightSide)
        start = -96, end = 32;
    else
        start = 256, end = 160;

    for (i = 0; i < (int)ARRAY_COUNT(structPtr->leftHeaderSprites); i++)
    {
        structPtr->leftHeaderSprites[i]->pos1.y = startY;
        MoveLeftHeader(structPtr->leftHeaderSprites[i], start, end, 12);
    }
}

static void ShowLeftHeaderSubmenuSprites(u32 startY, bool32 isOnRightSide)
{
    int start, end, i;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (!isOnRightSide)
        start = -96, end = 16;
    else
        start = 256, end = 192;

    for (i = 0; i < (int)ARRAY_COUNT(structPtr->submenuLeftHeaderSprites); i++)
    {
        structPtr->submenuLeftHeaderSprites[i]->pos1.y = startY;
        MoveLeftHeader(structPtr->submenuLeftHeaderSprites[i], start, end, 12);
    }
}

static void HideLeftHeaderSprites(bool32 isOnRightSide)
{
    int start, end, i;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (!isOnRightSide)
        start = 32, end = -96;
    else
        start = 192, end = 256;

    for (i = 0; i < (int)ARRAY_COUNT(structPtr->leftHeaderSprites); i++)
    {
        MoveLeftHeader(structPtr->leftHeaderSprites[i], start, end, 12);
    }
}

static void HideLeftHeaderSubmenuSprites(bool32 isOnRightSide)
{
    int start, end, i;
    struct PokenavMainMenuResources *structPtr = GetSubstructPtr(0);

    if (!isOnRightSide)
        start = 16, end = -96;
    else
        start = 192, end = 256;

    for (i = 0; i < (int)ARRAY_COUNT(structPtr->submenuLeftHeaderSprites); i++)
    {
        MoveLeftHeader(structPtr->submenuLeftHeaderSprites[i], start, end, 12);
    }
}

static void MoveLeftHeader(struct Sprite *sprite, int startX, int endX, int duration)
{
    sprite->pos1.x = startX;
    sprite->data[0] = startX * 16;
    sprite->data[1] = (endX - startX) * 16 / duration;
    sprite->data[2] = duration;
    sprite->data[7] = endX;
    sprite->callback = SpriteCB_MoveLeftHeader;
}

static void SpriteCB_MoveLeftHeader(struct Sprite *sprite)
{
    if (sprite->data[2] != 0)
    {
        sprite->data[2]--;
        sprite->data[0] += sprite->data[1];
        sprite->pos1.x = sprite->data[0] >> 4;
        if (sprite->pos1.x < -16 || sprite->pos1.x > 256)
            sprite->invisible = TRUE;
        else
            sprite->invisible = FALSE;
    }
    else
    {
        sprite->pos1.x = sprite->data[7];
        sprite->callback = SpriteCallbackDummy;
    }
}
