#include "gba/gba.h"
#include "global.h"
#include "main.h"
#include "rtc.h"
#include "flash.h"

static s32 gInitialWaitTimer;
IntrFunc gIntrTable[16];
u16 gHeldKeys;
u16 gNewKeys;
u8 gIntrVector[0x100];
u32 gUpdateSuccessful;
u32 gUnknown_3001194;
u32 gUnknown_30011A0[0x19];
u32 gMainCallbackState;
u32 gGameVersion;

EWRAM_DATA u8 gSharedMem[0x8000] = {};

void intr_main(void);
void ReadKeys(void);
void dummy_intr_0(void);
void dummy_intr_1(void);
void main_callback(u32 *, void *, void *);


const char gBerryFixGameCode[] = "AGBJ";
const IntrFunc gIntrFuncPointers[] = {
    dummy_intr_0,
    dummy_intr_1,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    dummy_intr_0,
    NULL,
    NULL,
    NULL
};
const char gVersionData[][2] = {
    {'J', 1},
    {'E', 2},
    {'D', 1},
    {'F', 1},
    {'I', 1},
    {'S', 1}
};
const char gRubyTitleAndCode[] = "POKEMON RUBYAXV";
const char gSapphireTitleAndCode[] = "POKEMON SAPPAXP";
const u16 sDebugPals[20] = {
    RGB(00, 00, 00),
    RGB(31, 00, 00),
    RGB(00, 31, 00),
    RGB(00, 00, 31)
};
const u16 sDebugDigitsGfx[] = INCBIN_U16("graphics/debug_digits.4bpp");

void AgbMain(void)
{
    RegisterRamReset(0x1E);
    DmaCopy32(3, gIntrFuncPointers, gIntrTable, sizeof gIntrFuncPointers);
    DmaCopy32(3, intr_main, gIntrVector, sizeof(gIntrVector));
    INTR_VECTOR = gIntrVector;
    REG_IE = INTR_FLAG_VBLANK;
    if (*RomHeaderMagic == 0x96 && *(u32 *)RomHeaderGameCode == *(u32 *)gBerryFixGameCode)
        REG_IE |= INTR_FLAG_GAMEPAK;
    REG_DISPSTAT = DISPSTAT_VBLANK_INTR;
    REG_IME = INTR_FLAG_VBLANK;
    msg_load_gfx();
    gMainCallbackState = MAINCB_INIT;
    gUnknown_3001194 = 0;
    for (;;)
    {
        VBlankIntrWait();
        ReadKeys();
        main_callback(&gMainCallbackState, gUnknown_30011A0, gSharedMem);
    }
}

void dummy_intr_1(void)
{}

void dummy_intr_0(void)
{}

void ReadKeys(void)
{
    u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
    gNewKeys = keyInput & ~gHeldKeys;
    gHeldKeys = keyInput;
}

void fill_palette(const u8 * src, u16 * dest, u8 value)
{
    s32 i;
    for (i = 0; src[i] != 0; i++)
        dest[i] = src[i] | value << 12;
}

bool32 berry_fix_memcmp(const char * src1, const char * src2, size_t size)
{
    s32 i;
    for (i = 0; i < size; i++)
    {
        if (src1[i] != src2[i])
            return FALSE;
    }
    return TRUE;
}

s32 validate_rom_header_internal(void)
{
    char languageCode = *(RomHeaderGameCode + 3);
    s32 softwareVersion = *RomHeaderSoftwareVersion;
    s32 shouldUpdate = -1;
    s32 i;
    for (i = 0; i < ARRAY_COUNT(gVersionData); i++)
    {
        if (languageCode == gVersionData[i][0])
        {
            if (softwareVersion >= gVersionData[i][1])
            {
                shouldUpdate = 0;
            }
            else
            {
                shouldUpdate = 1;
            }
            break;
        }
    }
    if (shouldUpdate != -1)
    {
        if (berry_fix_memcmp(RomHeaderGameTitle, gRubyTitleAndCode, 15) == TRUE)
        {
            if (shouldUpdate == 0)
                return RUBY_NONEED;
            else
            {
                gGameVersion = VERSION_RUBY;
                return RUBY_UPDATABLE;
            }
        }
        else if (berry_fix_memcmp(RomHeaderGameTitle, gSapphireTitleAndCode, 15) == TRUE)
        {
            if (shouldUpdate == 0)
                return SAPPHIRE_NONEED;
            else
            {
                gGameVersion = VERSION_SAPPHIRE;
                return SAPPHIRE_UPDATABLE;
            }
        }
    }
    return INVALID;
}

s32 validate_rom_header(void)
{
    if (*RomHeaderMakerCode == '0' && *(RomHeaderMakerCode + 1) == '1' && *RomHeaderMagic == 0x96)
        return validate_rom_header_internal();
    else
        return INVALID;
}

void main_callback(u32 * state, void * unused1, void * unused2)
{
    u8 year;
    switch (*state)
    {
        case MAINCB_INIT:
            msg_display(MSGBOX_WILL_NOW_UPDATE);
            if (++gInitialWaitTimer >= 180)
            {
                gInitialWaitTimer = 0;
                gUpdateSuccessful = 0;
                switch (validate_rom_header())
                {
                    case SAPPHIRE_UPDATABLE:
                    case RUBY_UPDATABLE: // Should Update Ruby
                        ++(*state); // MAINCB_CHECK_RTC
                        break;
                    case INVALID: // Invalid header
                        *state = MAINCB_ERROR;
                        break;
                    case SAPPHIRE_NONEED: // Should not update Sapphire
                    case RUBY_NONEED: // Should not update Ruby
                        *state = MAINCB_NO_NEED_TO_FIX;
                        break;
                }
            }
            break;
        case MAINCB_CHECK_RTC:
            if (!rtc_maincb_is_rtc_working())
                *state = MAINCB_ERROR;
            else
                ++(*state); // MAINCB_CHECK_FLASH
            break;
        case MAINCB_CHECK_FLASH:
            if (flash_maincb_ident_is_valid() == TRUE)
                ++(*state); // MAINCB_READ_SAVE
            else
                *state = MAINCB_ERROR;
            break;
        case MAINCB_READ_SAVE:
            if (flash_maincb_read_save(0) == SAVE_STATUS_OK)
                ++(*state); // MAINCB_CHECK_TIME
            else
                *state = MAINCB_ERROR;
            break;
        case MAINCB_CHECK_TIME:
            if (rtc_maincb_is_time_since_last_berry_update_positive(&year) == TRUE)
            {
                if (year == 0)
                    ++(*state); // MAINCB_FIX_DATE
                else
                    *state = MAINCB_CHECK_PACIFIDLOG_TM;
            }
            else
            {
                if (year != 1)
                    *state = MAINCB_YEAR_MAKES_NO_SENSE;
                else
                    ++(*state); // MAINCB_FIX_DATE
            }
            break;
        case MAINCB_FIX_DATE:
            rtc_maincb_fix_date();
            gUpdateSuccessful |= 1;
            *state = MAINCB_CHECK_PACIFIDLOG_TM;
            break;
        case MAINCB_CHECK_PACIFIDLOG_TM:
            if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE)
                *state = MAINCB_FINISHED;
            else
                *state = MAINCB_FIX_PACIFIDLOG_TM;
            break;
        case MAINCB_FIX_PACIFIDLOG_TM:
            msg_display(MSGBOX_UPDATING);
            if (flash_maincb_reset_pacifidlog_tm() == TRUE)
            {
                gUpdateSuccessful |= 1;
                *state = MAINCB_FINISHED;
            }
            else
                *state = MAINCB_ERROR;
            break;
        case MAINCB_FINISHED:
            if (gUpdateSuccessful == 0)
                *state = MAINCB_NO_NEED_TO_FIX;
            else
                msg_display(MSGBOX_HAS_BEEN_UPDATED);
            break;
        case MAINCB_NO_NEED_TO_FIX:
            msg_display(MSGBOX_NO_NEED_TO_UPDATE);
            break;
        case MAINCB_YEAR_MAKES_NO_SENSE:
            msg_display(MSGBOX_UNABLE_TO_UPDATE);
            break;
        case MAINCB_ERROR:
            msg_display(MSGBOX_UNABLE_TO_UPDATE);
            break;
    }
}

void DBG_LoadDigitsPal(void)
{
    const u16 * src;
    s32 i;
    register vu16 * dest asm("r3") = (vu16 *)BG_PLTT + 1;
    DmaFill16(3, RGB(31, 31, 31), (vu16 *)BG_PLTT, BG_PLTT_SIZE);
    src = sDebugPals;
    for (i = 0; i < 4; i++)
    {
        *dest = *src;
        dest += 16;
        src++;
    }
}

void DBG_LoadDigits(void)
{
    DmaFill16(3, 0x1111, (void *)VRAM + 0x8420, 0x1800);
    DmaCopy32(3, sDebugDigitsGfx, (void *)VRAM + 0x8600, 0x200);
    DBG_LoadDigitsPal();
}
