#include "global.h"
#include "starter_choose.h"
#include "data2.h"
#include "decompress.h"
#include "main.h"
#include "menu.h"
#include "palette.h"
#include "pokedex.h"
#include "constants/songs.h"
#include "sound.h"
#include "constants/species.h"
#include "sprite.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "trig.h"
#include "scanline_effect.h"

extern u16 gSpecialVar_Result;
extern struct SpriteTemplate gCreatingSpriteTemplate;

//--------------------------------------------------
// Graphics Data
//--------------------------------------------------

const u16 gBirchBagGrassPal[2][16] =
{
    INCBIN_U16("graphics/misc/birch_bag.gbapal"),
    INCBIN_U16("graphics/misc/birch_grass.gbapal"),
};
static const u16 gBirchBallarrow_Pal[] = INCBIN_U16("graphics/misc/birch_ballarrow.gbapal");
static const u16 gBirchCircle_Pal[] = INCBIN_U16("graphics/misc/birch_circle.gbapal");
const u8 gBirchBagTilemap[] = INCBIN_U8("graphics/misc/birch_bag_map.bin.lz");
const u8 gBirchGrassTilemap[] = INCBIN_U8("graphics/misc/birch_grass_map.bin.lz");
const u8 gBirchHelpGfx[] = INCBIN_U8("graphics/misc/birch_help.4bpp.lz");
static const u8 gBirchBallarrow_Gfx[] = INCBIN_U8("graphics/misc/birch_ballarrow.4bpp.lz");
static const u8 gBirchCircle_Gfx[] = INCBIN_U8("graphics/misc/birch_circle.4bpp.lz");
static const u8 gStarterChoose_PokeballCoords[][2] =
{
    {60, 64},
    {120, 88},
    {180, 64},
};
static const u8 gStarterChoose_LabelCoords[][2] =
{
    {0, 9},
    {16, 10},
    {8, 4},
};
static const u16 sStarterMons[] = {SPECIES_TREECKO, SPECIES_TORCHIC, SPECIES_MUDKIP};
static const struct OamData gOamData_83F76CC =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 2,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83F76D4 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 2,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83F76DC =
{
    .y = 160,
    .affineMode = 3,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 3,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const u8 gUnknown_083F76E4[][2] =
{
    {60, 32},
    {120, 56},
    {180, 32},
    {0, 0},
};
static const union AnimCmd gSpriteAnim_83F76EC[] =
{
    ANIMCMD_FRAME(48, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F76F4[] =
{
    ANIMCMD_FRAME(0, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F76FC[] =
{
    ANIMCMD_FRAME(16, 4),
    ANIMCMD_FRAME(0, 4),
    ANIMCMD_FRAME(32, 4),
    ANIMCMD_FRAME(0, 4),
    ANIMCMD_FRAME(16, 4),
    ANIMCMD_FRAME(0, 4),
    ANIMCMD_FRAME(32, 4),
    ANIMCMD_FRAME(0, 4),
    ANIMCMD_FRAME(0, 32),
    ANIMCMD_FRAME(16, 8),
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_FRAME(32, 8),
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_FRAME(16, 8),
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_FRAME(32, 8),
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_JUMP(0),
};
static const union AnimCmd gSpriteAnim_83F7744[] =
{
    ANIMCMD_FRAME(0, 8),
    ANIMCMD_END,
};
static const union AnimCmd *const gSpriteAnimTable_83F774C[] =
{
    gSpriteAnim_83F76EC,
};
static const union AnimCmd *const gSpriteAnimTable_83F7750[] =
{
    gSpriteAnim_83F76F4,
    gSpriteAnim_83F76FC,
};
static const union AnimCmd *const gSpriteAnimTable_83F7758[] =
{
    gSpriteAnim_83F7744,
};
static const union AffineAnimCmd gSpriteAffineAnim_83F775C[] =
{
    AFFINEANIMCMD_FRAME(16, 16, 0, 0),
    AFFINEANIMCMD_FRAME(16, 16, 0, 15),
    AFFINEANIMCMD_END,
};
static const union AffineAnimCmd gSpriteAffineAnim_83F7774[] =
{
    AFFINEANIMCMD_FRAME(20, 20, 0, 0),
    AFFINEANIMCMD_FRAME(20, 20, 0, 15),
    AFFINEANIMCMD_END,
};
static const union AffineAnimCmd *const gSpriteAffineAnimTable_83F778C[] =
{
    gSpriteAffineAnim_83F775C,
};
static const union AffineAnimCmd *const gSpriteAffineAnimTable_83F7790[] =
{
    gSpriteAffineAnim_83F7774,
};
static const struct CompressedSpriteSheet gUnknown_083F7794[] =
{
    {gBirchBallarrow_Gfx, 0x0800, 0x1000},
    {NULL},
};
static const struct CompressedSpriteSheet gUnknown_083F77A4[] =
{
    {gBirchCircle_Gfx, 0x0800, 0x1001},
    {NULL},
};
const struct SpritePalette gUnknown_083F77B4[] =
{
    {gBirchBallarrow_Pal, 0x1000},
    {gBirchCircle_Pal, 0x1001},
    {NULL},
};
static void sub_810A62C(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77CC =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83F76CC,
    .anims = gSpriteAnimTable_83F774C,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = sub_810A62C,
};
static void sub_810A68C(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77E4 =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83F76D4,
    .anims = gSpriteAnimTable_83F7750,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = sub_810A68C,
};
static void StarterPokemonSpriteAnimCallback(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77FC =
{
    .tileTag = 4097,
    .paletteTag = 4097,
    .oam = &gOamData_83F76DC,
    .anims = gSpriteAnimTable_83F7758,
    .images = NULL,
    .affineAnims = gSpriteAffineAnimTable_83F7790,
    .callback = StarterPokemonSpriteAnimCallback,
};

static void MainCallback2(void);
static void Task_StarterChoose1(u8 taskId);
static void Task_StarterChoose2(u8 taskId);
static void Task_StarterChoose3(u8 taskId);
static void Task_StarterChoose4(u8 taskId);
static void Task_StarterChoose5(u8 taskId);
static void Task_StarterChoose6(u8 taskId);

static void CreateStarterPokemonLabel(u8, u8);
static u8 CreatePokemonFrontSprite(u16, u8, u8);

//Position of the sprite of the selected starter Pokemon
#define STARTER_PKMN_POS_X 120
#define STARTER_PKMN_POS_Y 64

//Retrieves one of the available starter Pokemon
u16 GetStarterPokemon(u16 n)
{
    if (n > 3)
        n = 0;
    return sStarterMons[n];
}

static void VblankCallback(void)
{
    LoadOam();
    ProcessSpriteCopyRequests();
    TransferPlttBuffer();
}

#define tStarterSelection data[0]
#define tPkmnSpriteId data[1]
#define tCircleSpriteId data[2]

void CB2_ChooseStarter(void)
{
    u16 savedIme;
    u8 taskId;
    u8 spriteId;

    SetVBlankCallback(NULL);

    REG_DISPCNT = 0;
    REG_BG3CNT = 0;
    REG_BG2CNT = 0;
    REG_BG1CNT = 0;
    REG_BG0CNT = 0;
    REG_BG3HOFS = 0;
    REG_BG3VOFS = 0;
    REG_BG2HOFS = 0;
    REG_BG2VOFS = 0;
    REG_BG1HOFS = 0;
    REG_BG1VOFS = 0;
    REG_BG0HOFS = 0;
    REG_BG0VOFS = 0;

    DmaFill16(3, 0, VRAM, VRAM_SIZE);
    DmaFill32(3, 0, OAM, OAM_SIZE);
    DmaFill16(3, 0, PLTT, PLTT_SIZE);

    LZ77UnCompVram(&gBirchHelpGfx, (void *)VRAM);
    LZ77UnCompVram(&gBirchBagTilemap, (void *)(VRAM + 0x3000));
    LZ77UnCompVram(&gBirchGrassTilemap, (void *)(VRAM + 0x3800));
    ScanlineEffect_Stop();
    ResetTasks();
    ResetSpriteData();
    ResetPaletteFade();
    FreeAllSpritePalettes();
    LoadPalette(gBirchBagGrassPal, 0, sizeof(gBirchBagGrassPal));
    LoadCompressedObjectPic(&gUnknown_083F7794[0]);
    LoadCompressedObjectPic(&gUnknown_083F77A4[0]);
    LoadSpritePalettes(gUnknown_083F77B4);
    Text_LoadWindowTemplate(&gWindowTemplate_81E6C3C);
    InitMenuWindow(&gMenuTextWindowTemplate);
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));

    savedIme = REG_IME;
    REG_IME = 0;
    REG_IE |= INTR_FLAG_VBLANK;
    REG_IME = savedIme;
    REG_DISPSTAT |= DISPSTAT_VBLANK_INTR;

    SetVBlankCallback(VblankCallback);
    SetMainCallback2(MainCallback2);

    REG_WININ = 0x3F;
    REG_WINOUT = 0x1F;
    REG_WIN0H = 0;
    REG_WIN0V = 0;
    REG_BLDCNT = 0xFE;
    REG_BLDALPHA = 0;
    REG_BLDY = 0x7;
    REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(7)  | BGCNT_16COLOR | BGCNT_TXT256x256;
    REG_BG2CNT = BGCNT_PRIORITY(2) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(6)  | BGCNT_16COLOR | BGCNT_TXT256x256;
    REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(2) | BGCNT_SCREENBASE(31) | BGCNT_16COLOR | BGCNT_TXT256x256;
    REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON;

    taskId = CreateTask(Task_StarterChoose1, 0);
    gTasks[taskId].tStarterSelection = 1;

    //Create hand sprite
    spriteId = CreateSprite(&gSpriteTemplate_83F77CC, 120, 56, 2);
    gSprites[spriteId].data[0] = taskId;

    //Create three Pokeball sprites
    spriteId = CreateSprite(
      &gSpriteTemplate_83F77E4,
      gStarterChoose_PokeballCoords[0][0], gStarterChoose_PokeballCoords[0][1], 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 0;
    spriteId = CreateSprite(
      &gSpriteTemplate_83F77E4,
      gStarterChoose_PokeballCoords[1][0], gStarterChoose_PokeballCoords[1][1], 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 1;
    spriteId = CreateSprite(
      &gSpriteTemplate_83F77E4,
      gStarterChoose_PokeballCoords[2][0], gStarterChoose_PokeballCoords[2][1], 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 2;
}

static void MainCallback2(void)
{
    RunTasks();
    AnimateSprites();
    BuildOamBuffer();
    UpdatePaletteFade();
}

static void Task_StarterChoose1(u8 taskId)
{
    CreateStarterPokemonLabel(0xFF, gTasks[taskId].tStarterSelection);
    Menu_DrawStdWindowFrame(2, 14, 27, 19);
    Menu_PrintText(gOtherText_BirchInTrouble, 3, 15);
    gTasks[taskId].func = Task_StarterChoose2;
}

static void Task_StarterChoose2(u8 taskId)
{
    u8 selection = gTasks[taskId].tStarterSelection;

    if (gMain.newKeys & A_BUTTON)
    {
        u8 spriteId;

        Menu_EraseWindowRect(
          gStarterChoose_LabelCoords[selection][0],
          gStarterChoose_LabelCoords[selection][1],
          gStarterChoose_LabelCoords[selection][0] + 13,
          gStarterChoose_LabelCoords[selection][1] + 3);

        REG_WIN0H = 0;
        REG_WIN0V = 0;

        //Create white circle background
        spriteId = CreateSprite(
          &gSpriteTemplate_83F77FC,
          gStarterChoose_PokeballCoords[selection][0],
          gStarterChoose_PokeballCoords[selection][1],
          1);
        gTasks[taskId].tCircleSpriteId = spriteId;

        //Create Pokemon sprite
        spriteId = CreatePokemonFrontSprite(
          GetStarterPokemon(gTasks[taskId].tStarterSelection),
          gStarterChoose_PokeballCoords[selection][0],
          gStarterChoose_PokeballCoords[selection][1]);
        gSprites[spriteId].affineAnims = gSpriteAffineAnimTable_83F778C;
        gSprites[spriteId].callback = StarterPokemonSpriteAnimCallback;
        gTasks[taskId].tPkmnSpriteId = spriteId;

        gTasks[taskId].func = Task_StarterChoose3;
    }
    else
    {
        if ((gMain.newKeys & DPAD_LEFT) && selection > 0)
        {
            gTasks[taskId].tStarterSelection--;
            CreateStarterPokemonLabel(selection, gTasks[taskId].tStarterSelection);
        }
        else if ((gMain.newKeys & DPAD_RIGHT) && selection < 2)
        {
            gTasks[taskId].tStarterSelection++;
            CreateStarterPokemonLabel(selection, gTasks[taskId].tStarterSelection);
        }
    }
}

static void Task_StarterChoose3(u8 taskId)
{
    if (gSprites[gTasks[taskId].tCircleSpriteId].affineAnimEnded &&
      gSprites[gTasks[taskId].tCircleSpriteId].x == STARTER_PKMN_POS_X &&
      gSprites[gTasks[taskId].tCircleSpriteId].y == STARTER_PKMN_POS_Y)
    {
        gTasks[taskId].func = Task_StarterChoose4;
    }
}

static void Task_StarterChoose4(u8 taskId)
{
    PlayCry1(GetStarterPokemon(gTasks[taskId].tStarterSelection), 0);
    Menu_DrawStdWindowFrame(2, 14, 27, 19);
    //"Do you choose this POKEMON?"
    Menu_PrintText(gOtherText_DoYouChoosePoke, 3, 15);
    DisplayYesNoMenu(21, 7, 1);
    gTasks[taskId].func = Task_StarterChoose5;
}

static void Task_StarterChoose5(u8 taskId)
{
    u8 spriteId;

    switch (Menu_ProcessInputNoWrap_())
    {
    case 0:  // YES
        //Return the starter choice and exit.
        gSpecialVar_Result = gTasks[taskId].tStarterSelection;
        SetMainCallback2(gMain.savedCallback);
        break;
    case 1:  // NO
    case -1: // B button
        PlaySE(SE_SELECT);
        Menu_EraseWindowRect(21, 7, 27, 12);

        spriteId = gTasks[taskId].tPkmnSpriteId;
        FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(gSprites[spriteId].oam.paletteNum));
        FreeOamMatrix(gSprites[spriteId].oam.matrixNum);
        DestroySprite(&gSprites[spriteId]);

        spriteId = gTasks[taskId].tCircleSpriteId;
        FreeOamMatrix(gSprites[spriteId].oam.matrixNum);
        DestroySprite(&gSprites[spriteId]);
        gTasks[taskId].func = Task_StarterChoose6;
        break;
    }
}

static void Task_StarterChoose6(u8 taskId)
{
    gTasks[taskId].func = Task_StarterChoose1;
}

void AddTextColorCtrlCode(u8 *string, u8 bgColor, u8 textColor, u8 shadowColor)
{
    *(string++) = EXT_CTRL_CODE_BEGIN;
    *(string++) = 4;
    *(string++) = textColor;
    *(string++) = bgColor;
    *(string++) = shadowColor;
}

#define SET_CHAR(str, index, c) \
{                               \
    u8 *p = str + index;        \
    *p = c;                     \
}

static void CreateStarterPokemonLabel(u8 prevSelection, u8 selection)
{

    u8 labelText[72];
    const u8 *category;
    u8 srcIndex;
    u8 dstIndex;
    u16 species;

    u8 labelLeft;
    u8 labelRight;
    u8 labelTop;
    u8 labelBottom;

    if (prevSelection != 0xFF)
    {
        //Remove the old Pokemon label
        Menu_EraseWindowRect(
            gStarterChoose_LabelCoords[prevSelection][0],
            gStarterChoose_LabelCoords[prevSelection][1],
            gStarterChoose_LabelCoords[prevSelection][0] + 13,
            gStarterChoose_LabelCoords[prevSelection][1] + 3);
        REG_WIN0H = 0;
        REG_WIN0V = 0;
    }

    species = GetStarterPokemon(selection);
    category = GetPokemonCategory(SpeciesToNationalPokedexNum(species));
    AddTextColorCtrlCode(labelText, 0, 15, 8);
#if ENGLISH
    dstIndex = 5;
    SET_CHAR(labelText, 5, EXT_CTRL_CODE_BEGIN);
    SET_CHAR(labelText, 6, EXT_CTRL_CODE_CLEAR);
    SET_CHAR(labelText, 7, dstIndex);
#endif

    //Copy category string to label
    dstIndex = 8;
    srcIndex = 0;
    while (category[srcIndex] != EOS && srcIndex <= 10)
    {
        labelText[dstIndex] = category[srcIndex];
        srcIndex++;
        dstIndex++;
    }
#if ENGLISH
    labelText[dstIndex++] = CHAR_SPACE;

    //Copy POKEMON string to label
    StringCopy(labelText + dstIndex, gOtherText_Poke);
#elif GERMAN
    labelText[dstIndex] = EOS;
    labelText[5] = EXT_CTRL_CODE_BEGIN;
    labelText[6] = EXT_CTRL_CODE_CLEAR;
    labelText[7] = (0x70 - 6 * srcIndex) >> 1;
#endif
    Menu_PrintText(
        labelText,
        gStarterChoose_LabelCoords[selection][0],
        gStarterChoose_LabelCoords[selection][1]);
    AddTextColorCtrlCode(labelText, 0, 15, 8);

    //Copy Pokemon name to label
#if ENGLISH
    AlignStringInMenuWindow(labelText + 5, gSpeciesNames[species], 0x6B, 1);
#elif GERMAN
    AlignStringInMenuWindow(labelText + 5, gSpeciesNames[species], 0x70, 2);
#endif
    Menu_PrintText(
        labelText,
        gStarterChoose_LabelCoords[selection][0],
        gStarterChoose_LabelCoords[selection][1] + 2);

    labelLeft = gStarterChoose_LabelCoords[selection][0] * 8 + 4;
    labelRight = (gStarterChoose_LabelCoords[selection][0] + 13) * 8 + 4;
    labelTop = gStarterChoose_LabelCoords[selection][1] * 8;
    labelBottom = (gStarterChoose_LabelCoords[selection][1] + 4) * 8;
    REG_WIN0H = WIN_RANGE(labelLeft, labelRight);
    REG_WIN0V = WIN_RANGE(labelTop, labelBottom);
}

static void nullsub_72(struct Sprite *sprite)
{
}

static u8 CreatePokemonFrontSprite(u16 species, u8 x, u8 y)
{
    u8 spriteId;

    DecompressPicFromTable_2(
      &gMonFrontPicTable[species],
      gMonFrontPicCoords[species].coords, gMonFrontPicCoords[species].y_offset,
      gMonSpriteGfx_Sprite_ptr[0], gMonSpriteGfx_Sprite_ptr[1],
      species);
    LoadCompressedObjectPalette(&gMonPaletteTable[species]);
    GetMonSpriteTemplate_803C56C(species, 1);
    spriteId = CreateSprite(&gCreatingSpriteTemplate, x, y, 0);
    gSprites[spriteId].callback = nullsub_72;
    gSprites[spriteId].oam.priority = 0;
    return spriteId;
}

//Sprite callback
static void sub_810A62C(struct Sprite *sprite)
{
    sprite->x = gUnknown_083F76E4[gTasks[sprite->data[0]].tStarterSelection][0];
    sprite->y = gUnknown_083F76E4[gTasks[sprite->data[0]].tStarterSelection][1];
    sprite->y2 = Sin(sprite->data[1], 8);
    sprite->data[1] = (u8)sprite->data[1] + 4;
}

//Sprite callback
static void sub_810A68C(struct Sprite *sprite)
{
    if (gTasks[sprite->data[0]].tStarterSelection == sprite->data[1])
        StartSpriteAnimIfDifferent(sprite, 1);
    else
        StartSpriteAnimIfDifferent(sprite, 0);
}

//Sprite callback
static void StarterPokemonSpriteAnimCallback(struct Sprite *sprite)
{
    //Move sprite to upper center of screen
    if (sprite->x > STARTER_PKMN_POS_X)
        sprite->x -= 4;
    if (sprite->x < STARTER_PKMN_POS_X)
        sprite->x += 4;
    if (sprite->y > STARTER_PKMN_POS_Y)
        sprite->y -= 2;
    if (sprite->y < STARTER_PKMN_POS_Y)
        sprite->y += 2;
}
