#include "global.h"
#include "event_data.h"
#include "main.h"
#include "menu.h"
#include "palette.h"
#include "rtc.h"
#include "save.h"
#include "script.h"
#include "sprite.h"
#include "constants/songs.h"
#include "sound.h"
#include "string_util.h"
#include "strings.h"
#include "strings2.h"
#include "task.h"
#include "text.h"
#include "scanline_effect.h"

struct ResetRtcStruct
{
    /*0x0*/ u8 dataIndex;
    /*0x2*/ u16 minVal;
    /*0x4*/ u16 maxVal;
    /*0x6*/ u8 left;
    /*0x7*/ u8 right;
    /*0x8*/ u8 unk8;
};

extern u16 gSaveFileStatus;

void CB2_ResetRtcScreen(void);
void VBlankCB_ResetRtcScreen(void);
void Task_ResetRtcScreen(u8);

static const struct ResetRtcStruct gUnknown_08376420[5] =
{
    {
        .dataIndex = 3,
        .minVal = 1,
        .maxVal = 9999,
        .left = 0,
        .right = 2,
        .unk8 = 0,
    },
    {
        .dataIndex = 4,
        .minVal = 0,
        .maxVal = 23,
        .left = 1,
        .right = 3,
        .unk8 = 0,
    },
    {
        .dataIndex = 5,
        .minVal = 0,
        .maxVal = 59,
        .left = 2,
        .right = 4,
        .unk8 = 0,
    },
    {
        .dataIndex = 6,
        .minVal = 0,
        .maxVal = 59,
        .left = 3,
        .right = 5,
        .unk8 = 0,
    },
    {
        .dataIndex = 7,
        .minVal = 0,
        .maxVal = 0,
        .left = 4,
        .right = 0,
        .unk8 = 6,
    },
};

const struct OamData gOamData_837645C =
{
    .y = 0,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 0,
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
    .affineParam = 0,
};

const u8 gSpriteImage_8376464[] = INCBIN_U8("graphics/unknown_sprites/83764AC/0.4bpp");
const u8 gSpriteImage_8376484[] = INCBIN_U8("graphics/unknown_sprites/83764AC/1.4bpp");

const u16 Palette_3764A4[] = INCBIN_U16("graphics/unknown/83764A4.gbapal");

const struct SpriteFrameImage gSpriteImageTable_83764AC[] =
{
    { gSpriteImage_8376464, 0x20 },
    { gSpriteImage_8376484, 0x20 },
};

const struct SpritePalette gUnknown_083764BC =
{
    .data = Palette_3764A4,
    .tag = 0x1000,
};

const union AnimCmd gSpriteAnim_83764C4[] =
{
    ANIMCMD_FRAME(0, 30),
    ANIMCMD_JUMP(0),
};

const union AnimCmd gSpriteAnim_83764CC[] =
{
    ANIMCMD_FRAME(0, 30, .vFlip = TRUE),
    ANIMCMD_JUMP(0),
};

const union AnimCmd gSpriteAnim_83764D4[] =
{
    ANIMCMD_FRAME(1, 30),
    ANIMCMD_JUMP(0),
};

const union AnimCmd *const gSpriteAnimTable_83764DC[] =
{
    gSpriteAnim_83764C4,
    gSpriteAnim_83764CC,
    gSpriteAnim_83764D4,
};

const struct SpriteTemplate gSpriteTemplate_83764E8 =
{
    .tileTag = 0xFFFF,
    .paletteTag = 0x1000,
    .oam = &gOamData_837645C,
    .anims = gSpriteAnimTable_83764DC,
    .images = gSpriteImageTable_83764AC,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCallbackDummy,
};

const u8 gUnknown_08376500[] = _(" : ");

void SpriteCB_ResetRtcCusor0(struct Sprite *sprite)
{
    int state = gTasks[sprite->data[0]].data[2];
    if (state != sprite->data[1])
    {
        sprite->data[1] = state;
        switch (state)
        {
        case 1:
            sprite->invisible = FALSE;
            sprite->animNum = 1;
            sprite->animDelayCounter = 0;
            sprite->x = 53;
            sprite->y = 68;
            break;
        case 2:
            sprite->invisible = FALSE;
            sprite->animNum = 1;
            sprite->animDelayCounter = 0;
            sprite->x = 86;
            sprite->y = 68;
            break;
        case 3:
            sprite->invisible = FALSE;
            sprite->animNum = 1;
            sprite->animDelayCounter = 0;
            sprite->x = 107;
            sprite->y = 68;
            break;
        case 4:
            sprite->invisible = FALSE;
            sprite->animNum = 1;
            sprite->animDelayCounter = 0;
            sprite->x = 128;
            sprite->y = 68;
            break;
        case 5:
            sprite->invisible = FALSE;
            sprite->animNum = 2;
            sprite->animDelayCounter = 0;
            sprite->x = 155;
            sprite->y = 80;
            break;
        case 6:
            DestroySprite(sprite);
            break;
        }
    }
}

void SpriteCB_ResetRtcCusor1(struct Sprite *sprite)
{
    int state = gTasks[sprite->data[0]].data[2];
    if (state != sprite->data[1])
    {
        sprite->data[1] = state;
        switch (state)
        {
        case 1:
            sprite->invisible = FALSE;
            sprite->animNum = 0;
            sprite->animDelayCounter = 0;
            sprite->x = 53;
            sprite->y = 92;
            break;
        case 2:
            sprite->invisible = FALSE;
            sprite->animNum = 0;
            sprite->animDelayCounter = 0;
            sprite->x = 86;
            sprite->y = 92;
            break;
        case 3:
            sprite->invisible = FALSE;
            sprite->animNum = 0;
            sprite->animDelayCounter = 0;
            sprite->x = 107;
            sprite->y = 92;
            break;
        case 4:
            sprite->invisible = FALSE;
            sprite->animNum = 0;
            sprite->animDelayCounter = 0;
            sprite->x = 128;
            sprite->y = 92;
            break;
        case 5:
            sprite->invisible = TRUE;
            break;
        case 6:
            DestroySprite(sprite);
            break;
        }
    }
}

void ResetRtcScreen_CreateCursor(u8 taskId)
{
    int spriteId;

    LoadSpritePalette(&gUnknown_083764BC);

    spriteId = CreateSpriteAtEnd(&gSpriteTemplate_83764E8, 53, 68, 0);
    gSprites[spriteId].callback = SpriteCB_ResetRtcCusor0;
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = -1;

    spriteId = CreateSpriteAtEnd(&gSpriteTemplate_83764E8, 53, 68, 0);
    gSprites[spriteId].callback = SpriteCB_ResetRtcCusor1;
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = -1;
}

void ResetRtcScreen_FreeCursorPalette(void)
{
    FreeSpritePaletteByTag(gUnknown_083764BC.tag);
}

void ResetRtcScreen_HideChooseTimeWindow(void)
{
    Menu_EraseWindowRect(3, 8, 25, 11);
}

void ResetRtcScreen_PrintTime(u8 x, u8 y, u16 days, u8 hours, u8 minutes, u8 seconds)
{
    u8 *dest = gStringVar4;
    days %= 10000;
    hours %= 24;
    minutes %= 60;
    seconds %= 60;
    AlignInt2InMenuWindow(gStringVar1, days, 24, 1);
    dest = StringCopy(dest, gStringVar1);
    dest = StringCopy(dest, gOtherText_Day);
    AlignInt2InMenuWindow(gStringVar1, hours, 18, 1);
    dest = StringCopy(dest, gStringVar1);
    dest = StringCopy(dest, gUnknown_08376500);
    dest = ConvertIntToDecimalStringN(dest, minutes, STR_CONV_MODE_LEADING_ZEROS, 2);
    dest = StringCopy(dest, gUnknown_08376500);
    ConvertIntToDecimalStringN(dest, seconds, STR_CONV_MODE_LEADING_ZEROS, 2);
    Menu_PrintText(gStringVar4, x, y);
}

void ResetRtcScreen_ShowChooseTimeWindow(u16 days, u8 hours, u8 minutes, u8 seconds)
{
    Menu_DrawStdWindowFrame(3, 8, 25, 11);
    Menu_PrintText(gOtherText_OK, 20, 9);
    ResetRtcScreen_PrintTime(4, 9, days, hours, minutes, seconds);
}

bool32 ResetRtcScreen_MoveTimeUpDown(s16 *val, int minVal, int maxVal, u16 keys)
{
    if (keys & DPAD_DOWN)
    {
        (*val)--;
        if (*val < minVal)
            *val = maxVal;
    }
    else if (keys & DPAD_UP)
    {
        (*val)++;
        if (*val > maxVal)
            *val = minVal;
    }
    else if (keys & DPAD_LEFT)
    {
        *val -= 10;
        if (*val < minVal)
            *val = maxVal;
    }
    else if (keys & DPAD_RIGHT)
    {
        *val += 10;
        if (*val > maxVal)
            *val = minVal;
    }
    else
    {
        return FALSE;
    }

    return TRUE;
}

void Task_ResetRtc_3(u8 taskId)
{
    gTasks[taskId].data[0] = 1;
}

void Task_ResetRtc_2(u8 taskId)
{
    ResetRtcScreen_HideChooseTimeWindow();
    ResetRtcScreen_FreeCursorPalette();
    gTasks[taskId].func = Task_ResetRtc_3;
}

void Task_ResetRtc_1(u8 taskId)
{
    s16 *data = gTasks[taskId].data;
    u8 selection = data[2];
    const struct ResetRtcStruct *selectionInfo = &gUnknown_08376420[selection - 1];

    if (gMain.newKeys & B_BUTTON)
    {
        gTasks[taskId].func = Task_ResetRtc_2;
        data[1] = 0;
        data[2] = 6;
        PlaySE(SE_SELECT);
        return;
    }

    if (gMain.newKeys & DPAD_RIGHT)
    {
        if (selectionInfo->right)
        {
            data[2] = selectionInfo->right;
            PlaySE(SE_SELECT);
            return;
        }
    }

    if (gMain.newKeys & DPAD_LEFT)
    {
        if (selectionInfo->left)
        {
            data[2] = selectionInfo->left;
            PlaySE(SE_SELECT);
            return;
        }
    }

    if (selection == 5)
    {
        if (gMain.newKeys & A_BUTTON)
        {
            gLocalTime.days = data[3];
            gLocalTime.hours = data[4];
            gLocalTime.minutes = data[5];
            gLocalTime.seconds = data[6];
            PlaySE(SE_SELECT);
            gTasks[taskId].func = Task_ResetRtc_2;
            data[1] = 1;
            data[2] = 6;
        }
    }
    else if (ResetRtcScreen_MoveTimeUpDown(&data[selectionInfo->dataIndex], selectionInfo->minVal, selectionInfo->maxVal, gMain.newAndRepeatedKeys & (DPAD_UP | DPAD_DOWN)))
    {
        PlaySE(SE_SELECT);
        ResetRtcScreen_PrintTime(4, 9, data[3], data[4], data[5], data[6]);
    }
}

void Task_ResetRtc_0(u8 taskId)
{
    s16 *data = gTasks[taskId].data;
    data[0] = 0;
    data[3] = gLocalTime.days;
    data[4] = gLocalTime.hours;
    data[5] = gLocalTime.minutes;
    data[6] = gLocalTime.seconds;
    ResetRtcScreen_ShowChooseTimeWindow(data[3], data[4], data[5], data[6]);
    ResetRtcScreen_CreateCursor(taskId);
    data[2] = 2;
    gTasks[taskId].func = Task_ResetRtc_1;
}

void CB2_InitResetRtcScreen(void)
{
    REG_DISPCNT = 0;
    SetVBlankCallback(NULL);
    DmaClear16(3, PLTT, PLTT_SIZE);
    DmaFill16Large(3, 0, (u8 *)VRAM, 0x18000, 0x1000);
    ResetOamRange(0, 128);
    LoadOam();
    ScanlineEffect_Stop();
    ScanlineEffect_Clear();
    ResetSpriteData();
    ResetTasks();
    ResetPaletteFade();
    Text_LoadWindowTemplate(&gMenuTextWindowTemplate);
    InitMenuWindow(&gMenuTextWindowTemplate);
    REG_DISPCNT = 4352;
    SetVBlankCallback(VBlankCB_ResetRtcScreen);
    SetMainCallback2(CB2_ResetRtcScreen);
    CreateTask(Task_ResetRtcScreen, 80);
}

void CB2_ResetRtcScreen(void)
{
    RunTasks();
    AnimateSprites();
    BuildOamBuffer();
    UpdatePaletteFade();
}

void VBlankCB_ResetRtcScreen(void)
{
    ProcessSpriteCopyRequests();
    LoadOam();
    TransferPlttBuffer();
}

void ResetRtcScreen_ShowMessage(const u8 *str)
{
    Menu_DisplayDialogueFrame();
    Menu_PrintText(str, 2, 15);
}

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

    switch (data[0])
    {
    case 0:
        Menu_EraseScreen();
        Menu_DrawStdWindowFrame(0, 0, 20, 10);
        Menu_PrintText(gSystemText_PresentTime, 1, 1);
        ResetRtcScreen_PrintTime(
            1,
            3,
            gLocalTime.days,
            gLocalTime.hours,
            gLocalTime.minutes,
            gLocalTime.seconds);
        Menu_PrintText(gSystemText_PreviousTime, 1, 5);
        ResetRtcScreen_PrintTime(
            1,
            7,
            gSaveBlock2.lastBerryTreeUpdate.days,
            gSaveBlock2.lastBerryTreeUpdate.hours,
            gSaveBlock2.lastBerryTreeUpdate.minutes,
            gSaveBlock2.lastBerryTreeUpdate.seconds);
        ResetRtcScreen_ShowMessage(gSystemText_ResetRTCPrompt);
        data[0]++;
    case 1:
        if (gMain.newKeys & B_BUTTON)
        {
            DestroyTask(taskId);
            DoSoftReset();
        }
        else if (gMain.newKeys & A_BUTTON)
        {
            PlaySE(SE_SELECT);
            DestroyTask(taskId);
        }
        break;
    }
}

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

    switch (data[0])
    {
    case 0:
        BeginNormalPaletteFade(0xFFFFFFFF, 1, 16, 0, FADE_COLOR_WHITE);
        data[0] = 1;
        break;
    case 1:
        if (!gPaletteFade.active)
        {
            if (gSaveFileStatus == 0 || gSaveFileStatus == 2)
            {
                ResetRtcScreen_ShowMessage(gSystemText_NoSaveFileNoTime);
                data[0] = 5;
            }
            else
            {
                RtcCalcLocalTime();
                data[1] = CreateTask(Task_ShowResetRtcPrompt, 80);
                data[0] = 2;
            }
        }
        break;
    case 2:
        if (gTasks[data[1]].isActive != TRUE)
        {
            Menu_EraseScreen();
            ResetRtcScreen_ShowMessage(gSystemText_PleaseResetTime);
            gLocalTime = gSaveBlock2.lastBerryTreeUpdate;
            data[1] = CreateTask(Task_ResetRtc_0, 80);
            data[0] = 3;
        }
        break;
    case 3:
        if (gTasks[data[1]].data[0])
        {
            if (!gTasks[data[1]].data[1])
            {
                DestroyTask(data[1]);
                data[0] = 2;
            }
            else
            {
                DestroyTask(data[1]);
                RtcReset();
                RtcCalcLocalTimeOffset(
                    gLocalTime.days,
                    gLocalTime.hours,
                    gLocalTime.minutes,
                    gLocalTime.seconds);
                gSaveBlock2.lastBerryTreeUpdate = gLocalTime;
                VarSet(VAR_DAYS, gLocalTime.days);
                DisableResetRTC();
                ResetRtcScreen_ShowMessage(gSystemText_ClockResetDataSave);
                data[0] = 4;
            }
        }
        break;
    case 4:
        if (Save_WriteData(0) == SAVE_STATUS_OK)
        {
            ResetRtcScreen_ShowMessage(gSystemText_SaveCompleted);
            PlaySE(SE_DING_DONG);
        }
        else
        {
            ResetRtcScreen_ShowMessage(gSystemText_SaveFailed);
            PlaySE(SE_BOO);
        }
        data[0] = 5;
        // fall through
    case 5:
        if (gMain.newKeys & A_BUTTON)
        {
            BeginNormalPaletteFade(0xFFFFFFFF, 1, 0, 16, FADE_COLOR_WHITE);
            data[0] = 6;
        }
        else
        {
            break;
        }
        // fall through
    case 6:
        if (!gPaletteFade.active)
        {
            DestroyTask(taskId);
            DoSoftReset();
        }
    }
}

#if DEBUG
void debug_sub_806F8F8(void)
{
    SetMainCallback2(CB2_InitResetRtcScreen);
}

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

    switch (data[0])
    {
    case 0:
        data[1] = CreateTask(Task_ResetRtc_0, 80);
        data[0]++;
        break;
    case 1:
        if (gTasks[data[1]].data[0] != 0)
        {
            if (gTasks[data[1]].data[1] == 1)
                RtcCalcLocalTimeOffset(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, gLocalTime.seconds);
            DestroyTask(data[1]);
            Menu_EraseScreen();
            ScriptContext2_Disable();
            DestroyTask(taskId);
        }
        break;
    }
}

void debug_sub_806F99C(void)
{
    RtcCalcLocalTime();
    CreateTask(debug_sub_806F908, 80);
    ScriptContext2_Enable();
}

void debug_sub_806F9B8(void)
{
    gLocalTime = gSaveBlock2.lastBerryTreeUpdate;
    CreateTask(debug_sub_806F908, 80);
    ScriptContext2_Enable();
}

static const u8 sDebugText_Days[] = DTR("にっすう", "days");
static const u8 sDebugText_Time[] = DTR("じかん", "time");
static const u8 sDebugText_GameTime[] = DTR("ゲームない　じかん", "game time");
static const u8 sDebugText_RTCTime[] = DTR("RTC　じかん", "RTC time");

void debug_sub_806F9E4(u8 taskId)
{
    s16 *data = gTasks[taskId].data;
    u8 *str = gStringVar4;

    switch (data[0])
    {
    case 0:
        data[0]++;
        break;
    case 1:
        Menu_DrawStdWindowFrame(0, 9, 29, 19);
        ConvertIntToHexStringN(str + 80, RtcGetErrorStatus(), 2, 4);
        Menu_PrintText(str + 80, 2, 10);
        Menu_PrintText(sDebugText_Days, 12, 12);
        Menu_PrintText(sDebugText_Time, 20, 12);
        Menu_PrintText(sDebugText_RTCTime, 1, 14);
        Menu_PrintText(sDebugText_GameTime, 1, 16);
        data[0]++;
        break;
    case 2:
        RtcCalcLocalTime();
        FormatHexRtcTime(str);
        debug_sub_8009894(str + 20);
        FormatDecimalTime(str + 40, gLocalTime.hours, gLocalTime.minutes, gLocalTime.seconds);
        ConvertIntToDecimalStringN(str + 60, gLocalTime.days, 1, 4);
        if (gSaveBlock2.playTimeVBlanks == 0)
        {
            Menu_PrintText(str, 20, 14);
            Menu_PrintText(str + 20, 12, 14);
            Menu_PrintText(str + 40, 20, 16);
            Menu_PrintText(str + 60, 12, 16);
        }
        if (data[1] > 19)  // Did you mean < 19?
        {
            data[1]++;
        }
        else
        {
            if (gMain.newKeys & A_BUTTON)
            {
                Menu_EraseScreen();
                DestroyTask(taskId);
                ScriptContext2_Disable();
            }
        }
        break;
    }
}
#endif
