#include "global.h"
#include "gpu_regs.h"
#include "multiboot.h"
#include "graphics.h"
#include "main.h"
#include "sprite.h"
#include "task.h"
#include "scanline_effect.h"
#include "help_system.h"
#include "m4a.h"

// Static type declarations

typedef struct {
    u8 state;
    u8 unk1;
    u16 unk2;
    struct MultiBootParam mb;
} berryfix_t;

// Static RAM declarations

const void * gUnknown_3005EF0;
int gUnknown_3005EF4;
size_t gUnknown_3005EF8;
struct MultiBootParam gUnknown_3005F00;

// Static ROM declarations

static void mb_berry_fix_maincb(void);
static void mb_berry_fix_task(u8 taskId);

// .rodata

static const void *const gUnknown_847A890[][3] = {
    {
        gBerryFixGameboy_Gfx,
        gBerryFixGameboy_Tilemap,
        gBerryFixGameboy_Pal
    }, {
        gBerryFixGameboyLogo_Gfx,
        gBerryFixGameboyLogo_Tilemap,
        gBerryFixGameboyLogo_Pal
    }, {
        gBerryFixGbaTransfer_Gfx,
        gBerryFixGbaTransfer_Tilemap,
        gBerryFixGbaTransfer_Pal
    }, {
        gBerryFixGbaTransferHighlight_Gfx,
        gBerryFixGbaTransferHighlight_Tilemap,
        gBerryFixGbaTransferHighlight_Pal
    }, {
        gBerryFixGbaTransferError_Gfx,
        gBerryFixGbaTransferError_Tilemap,
        gBerryFixGbaTransferError_Pal
    }, {
        gBerryFixWindow_Gfx,
        gBerryFixWindow_Tilemap,
        gBerryFixWindow_Pal
    },
};

extern const u8 gMultiBootProgram_BerryGlitchFix_Start[0x3BF4];
extern const u8 gMultiBootProgram_BerryGlitchFix_End[];

// .text

static void mb_berry_fix_print(int scene)
{
    REG_DISPCNT = 0;
    REG_BG0HOFS = 0;
    REG_BG0VOFS = 0;
    REG_BLDCNT = 0;
    LZ77UnCompVram(gUnknown_847A890[scene][0], (void *)BG_CHAR_ADDR(0));
    LZ77UnCompVram(gUnknown_847A890[scene][1], (void *)BG_SCREEN_ADDR(31));
    CpuCopy16(gUnknown_847A890[scene][2], (void *)BG_PLTT, 0x200);
    REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_16COLOR | BGCNT_SCREENBASE(31) | BGCNT_TXT256x256;
    REG_DISPCNT = DISPCNT_BG0_ON;
}

void mb_berry_fix_serve(void) // noreturn
{
    u8 taskId;
    DisableInterrupts(0xFFFF);
    EnableInterrupts(INTR_FLAG_VBLANK);
    m4aSoundVSyncOff();
    SetVBlankCallback(NULL);
    DmaFill32(3, 0, (void *)VRAM, VRAM_SIZE);
    DmaFill32(3, 0, (void *)PLTT, PLTT_SIZE);
    ResetSpriteData();
    ResetTasks();
    ScanlineEffect_Stop();
    gHelpSystemEnabled = FALSE;
    taskId = CreateTask(mb_berry_fix_task, 0);
    gTasks[taskId].data[0] = 0;
    SetMainCallback2(mb_berry_fix_maincb);
}

static void mb_berry_fix_maincb(void)
{
    RunTasks();
}

static void mb_berry_fix_task(u8 taskId)
{
    s16 * data = gTasks[taskId].data;

    switch (data[0])
    {
    case 0:
        mb_berry_fix_print(5);
        data[0] = 1;
        break;
    case 1:
        if (JOY_NEW(A_BUTTON))
        {
            mb_berry_fix_print(0);
            data[0] = 2;
        }
        break;
    case 2:
        if (JOY_NEW(A_BUTTON))
        {
            mb_berry_fix_print(1);
            data[0] = 4;
        }
        break;
    case 4:
        gUnknown_3005EF0 = gMultiBootProgram_BerryGlitchFix_Start;
        gUnknown_3005EF8 = gMultiBootProgram_BerryGlitchFix_End - gMultiBootProgram_BerryGlitchFix_Start;
        gUnknown_3005F00.masterp = (void *)gMultiBootProgram_BerryGlitchFix_Start;
        gUnknown_3005F00.server_type = MULTIBOOT_SERVER_TYPE_NORMAL;
        MultiBootInit(&gUnknown_3005F00);
        data[1] = 0;
        data[0] = 5;
        break;
    case 5:
        if (gUnknown_3005F00.probe_count == 0 && gUnknown_3005F00.response_bit & 0x2 && gUnknown_3005F00.client_bit & 0x2)
        {
            data[1]++;
            if (data[1] > 180)
            {
                mb_berry_fix_print(2);
                MultiBootStartMaster(&gUnknown_3005F00, gUnknown_3005EF0 + MULTIBOOT_HEADER_SIZE, gUnknown_3005EF8 - MULTIBOOT_HEADER_SIZE, 4, 1);
                data[1] = 0;
                data[0] = 6;
            }
            else
                gUnknown_3005EF4 = MultiBootMain(&gUnknown_3005F00);
        }
        else
        {
            data[1] = 0;
            gUnknown_3005EF4 = MultiBootMain(&gUnknown_3005F00);
        }
        break;
    case 6:
        gUnknown_3005EF4 = MultiBootMain(&gUnknown_3005F00);
        if (MultiBootCheckComplete(&gUnknown_3005F00))
        {
            mb_berry_fix_print(3);
            data[0] = 7;
        }
        else if (!(gUnknown_3005F00.client_bit & 2))
            data[0] = 9;
        break;
    case 7:
        data[0] = 8;
        break;
    case 8:
        if (JOY_NEW(A_BUTTON))
        {
            DestroyTask(taskId);
            DoSoftReset();
        }
        break;
    case 9:
        mb_berry_fix_print(4);
        data[0] = 10;
        break;
    case 10:
        if (JOY_NEW(A_BUTTON))
            data[0] = 0;
        break;
    }
}
