#include "global.h"
#include "decompress.h"
#include "main.h"
#include "menu.h"
#include "palette.h"
#include "rtc.h"
#include "scanline_effect.h"
#include "sound.h"
#include "strings2.h"
#include "task.h"
#include "trig.h"
#include "wallclock.h"
#include "constants/songs.h"

extern u16 gSpecialVar_0x8004;
extern u8 gMiscClock_Gfx[];
extern u8 gUnknown_08E95774[];
extern u8 gUnknown_08E954B0[];
extern u16 gMiscClockMale_Pal[];
extern u16 gMiscClockFemale_Pal[];

static void WallClockMainCallback(void);
static void WallClockInit(void);
static void Task_SetClock1(u8 taskId);
static void Task_SetClock2(u8 taskId);
static void Task_SetClock3(u8 taskId);
static void Task_SetClock4(u8 taskId);
static void Task_SetClock5(u8 taskId);
static void Task_SetClock6(u8 taskId);
static void Task_ViewClock1(u8 taskId);
static void Task_ViewClock2(u8 taskId);
static void Task_ViewClock3(u8 taskId);
static void Task_ViewClock4(u8 taskId);
static u8 CalcMinHandDelta(u16 speed);
static u16 CalcNewMinHandAngle(u16 angle, u8 direction, u8 speed);
static u8 AdvanceClock(u8 taskId, u8 direction);
static void UpdateClockPeriod(u8 taskId, u8 direction);
static void InitClockWithRtc(u8 taskId);
static void SpriteCB_MinuteHand(struct Sprite *sprite);
static void SpriteCB_HourHand(struct Sprite *sprite);
static void SpriteCB_AMIndicator(struct Sprite *sprite);
static void SpriteCB_PMIndicator(struct Sprite *sprite);

#define TAG_GFX_WALL_CLOCK_HAND 0x1000
#define TAG_PAL_WALL_CLOCK_HAND 0x1000

enum
{
    PERIOD_AM,
    PERIOD_PM,
};

enum
{
    MVMT_NONE,
    MVMT_BACKWARD,
    MVMT_FORWARD,
};

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

static const u8 ClockGfx_Misc[] = INCBIN_U8("graphics/misc/clock_misc.4bpp.lz");
static const struct CompressedSpriteSheet gUnknown_083F7A90[] =
{
    {ClockGfx_Misc, 0x2000, 0x1000},
    {NULL},
};
static const struct SpritePalette gUnknown_083F7AA0[] =
{
    {gMiscClockMale_Pal, 0x1000},
    {gMiscClockFemale_Pal, 0x1001},
    {NULL},
};
static const struct OamData gOamData_83F7AB8 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 3,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const union AnimCmd gSpriteAnim_83F7AC0[] =
{
    ANIMCMD_FRAME(0, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F7AC8[] =
{
    ANIMCMD_FRAME(64, 30),
    ANIMCMD_END,
};
static const union AnimCmd *const gSpriteAnimTable_83F7AD0[] =
{
    gSpriteAnim_83F7AC0,
};
static const union AnimCmd *const gSpriteAnimTable_83F7AD4[] =
{
    gSpriteAnim_83F7AC8,
};

static const struct SpriteTemplate gSpriteTemplate_83F7AD8 =
{
    .tileTag = TAG_GFX_WALL_CLOCK_HAND,
    .paletteTag = TAG_PAL_WALL_CLOCK_HAND,
    .oam = &gOamData_83F7AB8,
    .anims = gSpriteAnimTable_83F7AD0,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_MinuteHand,
};

static const struct SpriteTemplate gSpriteTemplate_83F7AF0 =
{
    .tileTag = TAG_GFX_WALL_CLOCK_HAND,
    .paletteTag = TAG_PAL_WALL_CLOCK_HAND,
    .oam = &gOamData_83F7AB8,
    .anims = gSpriteAnimTable_83F7AD4,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_HourHand,
};
static const struct OamData gOamData_83F7B08 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 1,
    .tileNum = 0,
    .priority = 2,
    .paletteNum = 0,
    .affineParam = 0,
};
static const union AnimCmd gSpriteAnim_83F7B10[] =
{
    ANIMCMD_FRAME(132, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F7B18[] =
{
    ANIMCMD_FRAME(128, 30),
    ANIMCMD_END,
};
static const union AnimCmd *const gSpriteAnimTable_83F7B20[] =
{
    gSpriteAnim_83F7B10,
};
static const union AnimCmd *const gSpriteAnimTable_83F7B24[] =
{
    gSpriteAnim_83F7B18,
};

static const struct SpriteTemplate gSpriteTemplate_83F7B28 =
{
    .tileTag = TAG_GFX_WALL_CLOCK_HAND,
    .paletteTag = TAG_PAL_WALL_CLOCK_HAND,
    .oam = &gOamData_83F7B08,
    .anims = gSpriteAnimTable_83F7B20,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_AMIndicator,
};

static const struct SpriteTemplate gSpriteTemplate_83F7B40 =
{
    .tileTag = TAG_GFX_WALL_CLOCK_HAND,
    .paletteTag = TAG_PAL_WALL_CLOCK_HAND,
    .oam = &gOamData_83F7B08,
    .anims = gSpriteAnimTable_83F7B24,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_PMIndicator,
};

static const s8 sClockHandCoords[][2] =
{
    {  0, -24},
    {  1, -25},
    {  1, -25},
    {  2, -25},
    {  2, -25},
    {  2, -25},
    {  3, -24},
    {  3, -25},
    {  4, -25},
    {  4, -25},
    {  4, -25},
    {  5, -25},
    {  5, -25},
    {  6, -24},
    {  6, -24},
    {  6, -24},
    {  7, -24},
    {  7, -24},
    {  7, -24},
    {  8, -24},
    {  8, -24},
    {  9, -24},
    {  9, -24},
    { 10, -23},
    { 10, -23},
    { 11, -22},
    { 11, -22},
    { 11, -22},
    { 12, -22},
    { 12, -21},
    { 13, -21},
    { 13, -21},
    { 13, -21},
    { 14, -21},
    { 14, -21},
    { 14, -20},
    { 14, -20},
    { 15, -20},
    { 15, -19},
    { 16, -19},
    { 16, -19},
    { 16, -19},
    { 16, -18},
    { 16, -18},
    { 17, -18},
    { 17, -17},
    { 17, -17},
    { 18, -17},
    { 18, -17},
    { 18, -16},
    { 18, -16},
    { 19, -16},
    { 19, -15},
    { 19, -15},
    { 20, -15},
    { 20, -14},
    { 20, -14},
    { 20, -13},
    { 20, -13},
    { 21, -13},
    { 21, -13},
    { 21, -12},
    { 22, -12},
    { 22, -12},
    { 22, -11},
    { 22, -11},
    { 22, -10},
    { 23, -10},
    { 23,  -9},
    { 23,  -9},
    { 23,  -9},
    { 23,  -9},
    { 23,  -8},
    { 23,  -8},
    { 23,  -7},
    { 23,  -7},
    { 23,  -6},
    { 24,  -6},
    { 24,  -6},
    { 25,  -5},
    { 25,  -5},
    { 24,  -4},
    { 25,  -4},
    { 24,  -3},
    { 25,  -3},
    { 25,  -3},
    { 25,  -2},
    { 25,  -2},
    { 24,  -1},
    { 25,  -1},
    { 24,   0},
    { 24,   0},
    { 24,   0},
    { 24,   1},
    { 24,   1},
    { 25,   2},
    { 24,   2},
    { 25,   2},
    { 24,   3},
    { 24,   3},
    { 25,   4},
    { 24,   4},
    { 24,   5},
    { 24,   5},
    { 24,   5},
    { 24,   6},
    { 23,   6},
    { 23,   6},
    { 23,   7},
    { 23,   8},
    { 23,   8},
    { 23,   8},
    { 23,   9},
    { 23,   9},
    { 23,  10},
    { 22,  10},
    { 22,  10},
    { 22,  11},
    { 22,  11},
    { 22,  11},
    { 22,  12},
    { 21,  12},
    { 21,  12},
    { 21,  13},
    { 20,  13},
    { 20,  13},
    { 19,  13},
    { 19,  13},
    { 19,  14},
    { 19,  14},
    { 19,  15},
    { 19,  15},
    { 18,  15},
    { 18,  16},
    { 17,  16},
    { 17,  16},
    { 17,  17},
    { 17,  17},
    { 16,  17},
    { 16,  18},
    { 16,  18},
    { 15,  18},
    { 14,  18},
    { 15,  19},
    { 14,  19},
    { 14,  19},
    { 13,  19},
    { 13,  20},
    { 13,  20},
    { 13,  20},
    { 12,  20},
    { 12,  20},
    { 12,  21},
    { 11,  21},
    { 11,  21},
    { 11,  21},
    { 10,  21},
    { 10,  22},
    { 10,  22},
    {  9,  22},
    {  9,  22},
    {  8,  22},
    {  7,  22},
    {  7,  23},
    {  7,  23},
    {  6,  23},
    {  6,  23},
    {  5,  23},
    {  5,  23},
    {  5,  24},
    {  4,  24},
    {  4,  24},
    {  4,  24},
    {  3,  24},
    {  2,  24},
    {  2,  24},
    {  1,  24},
    {  1,  24},
    {  0,  24},
    {  0,  24},
    { -1,  23},
    {  0,  24},
    {  0,  24},
    { -1,  24},
    { -1,  24},
    { -2,  24},
    { -2,  24},
    { -3,  24},
    { -3,  24},
    { -4,  24},
    { -4,  24},
    { -5,  24},
    { -5,  23},
    { -5,  23},
    { -6,  23},
    { -6,  23},
    { -7,  23},
    { -7,  23},
    { -7,  23},
    { -8,  23},
    { -8,  22},
    { -9,  22},
    { -9,  22},
    {-10,  22},
    {-10,  22},
    {-10,  21},
    {-11,  21},
    {-11,  21},
    {-11,  21},
    {-11,  20},
    {-12,  20},
    {-12,  20},
    {-13,  20},
    {-13,  20},
    {-13,  19},
    {-14,  19},
    {-14,  19},
    {-14,  19},
    {-14,  18},
    {-15,  18},
    {-15,  18},
    {-15,  17},
    {-16,  17},
    {-16,  17},
    {-17,  17},
    {-17,  16},
    {-17,  16},
    {-18,  16},
    {-17,  15},
    {-18,  15},
    {-18,  15},
    {-19,  15},
    {-19,  14},
    {-19,  14},
    {-19,  13},
    {-19,  13},
    {-20,  13},
    {-20,  12},
    {-20,  12},
    {-21,  12},
    {-21,  12},
    {-21,  11},
    {-21,  11},
    {-21,  10},
    {-21,  10},
    {-21,   9},
    {-22,   9},
    {-22,   9},
    {-22,   8},
    {-22,   8},
    {-22,   7},
    {-23,   7},
    {-23,   7},
    {-23,   6},
    {-23,   6},
    {-23,   5},
    {-24,   5},
    {-23,   4},
    {-23,   4},
    {-24,   4},
    {-24,   4},
    {-24,   3},
    {-24,   3},
    {-24,   2},
    {-24,   2},
    {-24,   1},
    {-24,   1},
    {-24,   1},
    {-24,   0},
    {-25,   0},
    {-24,  -1},
    {-25,  -1},
    {-24,  -1},
    {-24,  -2},
    {-24,  -2},
    {-24,  -3},
    {-24,  -3},
    {-24,  -4},
    {-24,  -4},
    {-24,  -4},
    {-24,  -5},
    {-24,  -5},
    {-24,  -6},
    {-24,  -6},
    {-23,  -6},
    {-23,  -7},
    {-23,  -7},
    {-23,  -8},
    {-23,  -8},
    {-23,  -9},
    {-23,  -9},
    {-22,  -9},
    {-22,  -9},
    {-22, -10},
    {-22, -10},
    {-21, -10},
    {-21, -11},
    {-22, -11},
    {-22, -12},
    {-21, -12},
    {-21, -13},
    {-21, -13},
    {-20, -13},
    {-21, -14},
    {-20, -14},
    {-20, -14},
    {-19, -14},
    {-19, -15},
    {-19, -15},
    {-18, -16},
    {-18, -16},
    {-18, -16},
    {-18, -17},
    {-18, -17},
    {-17, -17},
    {-17, -18},
    {-17, -18},
    {-16, -18},
    {-16, -18},
    {-16, -19},
    {-16, -19},
    {-15, -19},
    {-15, -19},
    {-15, -20},
    {-14, -20},
    {-14, -20},
    {-14, -21},
    {-13, -21},
    {-13, -21},
    {-13, -21},
    {-12, -21},
    {-12, -22},
    {-11, -22},
    {-11, -22},
    {-11, -22},
    {-10, -22},
    {-10, -22},
    { -9, -22},
    { -9, -23},
    { -9, -23},
    { -8, -23},
    { -8, -23},
    { -7, -23},
    { -7, -23},
    { -7, -24},
    { -6, -24},
    { -6, -24},
    { -5, -24},
    { -5, -24},
    { -4, -24},
    { -4, -24},
    { -4, -24},
    { -4, -25},
    { -3, -25},
    { -2, -25},
    { -2, -24},
    { -2, -24},
    { -1, -25},
    { -1, -25},
    {  0, -25},
};

// text

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

static void LoadWallClockGraphics(void)
{
    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;

    DmaFill16Large(3, 0, (void *)(VRAM + 0x0), 0x18000, 0x1000);
    DmaClear32(3, OAM, OAM_SIZE);
    DmaClear16(3, PLTT, PLTT_SIZE);

    LZ77UnCompVram(gMiscClock_Gfx, (void *)VRAM);
    if (gSpecialVar_0x8004 == MALE)
        LoadPalette(gMiscClockMale_Pal, 0, 32);
    else
        LoadPalette(gMiscClockFemale_Pal, 0, 32);
    ScanlineEffect_Stop();
    ResetTasks();
    ResetSpriteData();
    ResetPaletteFade();
    FreeAllSpritePalettes();
    LoadCompressedObjectPic(&gUnknown_083F7A90[0]);
    LoadSpritePalettes(gUnknown_083F7AA0);
    Text_LoadWindowTemplate(&gWindowTemplate_81E6C3C);
    InitMenuWindow(&gMenuTextWindowTemplate);
}

static void WallClockInit(void)
{
    u16 ime;

    BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));
    ime = REG_IME;
    REG_IME = 0;
    REG_IE |= INTR_FLAG_VBLANK;
    REG_IME = ime;
    REG_DISPSTAT |= DISPSTAT_VBLANK_INTR;
    SetVBlankCallback(WallClockVblankCallback);
    SetMainCallback2(WallClockMainCallback);
    REG_BLDCNT = 0;
    REG_BLDALPHA = 0;
    REG_BLDY = 0;
    REG_BG3CNT = BGCNT_PRIORITY(1) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(7)  | 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_BG3_ON | DISPCNT_OBJ_ON;
}

#define tMinuteHandAngle data[0]
#define tHourHandAngle data[1]
#define tHours data[2]
#define tMinutes data[3]
#define tMvmtDir data[4]
#define tPeriod data[5]
#define tMvmtSpeed data[6]

//Allow player to set the clock
void CB2_StartWallClock(void)
{
    u8 taskId;
    u8 spriteId;

    LoadWallClockGraphics();
    LZ77UnCompVram(&gUnknown_08E954B0, (void *)(VRAM + 0x3800));

    taskId = CreateTask(Task_SetClock1, 0);
    gTasks[taskId].tHours = 10;
    gTasks[taskId].tMinutes = 0;
    gTasks[taskId].tMvmtDir = MVMT_NONE;
    gTasks[taskId].tPeriod = PERIOD_AM;
    gTasks[taskId].tMvmtSpeed = 0;
    gTasks[taskId].tMinuteHandAngle = 0;
    gTasks[taskId].tHourHandAngle = 300;

    spriteId = CreateSprite(&gSpriteTemplate_83F7AD8, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, 1);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
    gSprites[spriteId].oam.matrixNum = 0;

    spriteId = CreateSprite(&gSpriteTemplate_83F7AF0, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, 0);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
    gSprites[spriteId].oam.matrixNum = 1;

    spriteId = CreateSprite(&gSpriteTemplate_83F7B28, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 45;

    spriteId = CreateSprite(&gSpriteTemplate_83F7B40, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 90;

    WallClockInit();
}

//View, but don't set, the clock
void CB2_ViewWallClock(void)
{
    u8 taskId;
    s16 angle1;
    s16 angle2;
    u8 spriteId;

    LoadWallClockGraphics();
    LZ77UnCompVram(gUnknown_08E95774, (void *)(VRAM + 0x3800));

    taskId = CreateTask(Task_ViewClock1, 0);
    InitClockWithRtc(taskId);
    if (gTasks[taskId].tPeriod == PERIOD_AM)
    {
        angle1 = 45;
        angle2 = 90;
    }
    else
    {
        angle1 = 90;
        angle2 = 135;
    }

    spriteId = CreateSprite(&gSpriteTemplate_83F7AD8, 120, 80, 1);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
    gSprites[spriteId].oam.matrixNum = 0;

    spriteId = CreateSprite(&gSpriteTemplate_83F7AF0, 120, 80, 0);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
    gSprites[spriteId].oam.matrixNum = 1;

    spriteId = CreateSprite(&gSpriteTemplate_83F7B28, 120, 80, 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = angle1;

    spriteId = CreateSprite(&gSpriteTemplate_83F7B40, 120, 80, 2);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = angle2;

    WallClockInit();
}

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

static void Task_SetClock1(u8 taskId)
{
    if (!gPaletteFade.active)
        gTasks[taskId].func = Task_SetClock2;
}

//Handle keypresses when setting clock
static void Task_SetClock2(u8 taskId)
{
    if (gTasks[taskId].tMinuteHandAngle % 6)
    {
        gTasks[taskId].tMinuteHandAngle = CalcNewMinHandAngle(
          gTasks[taskId].tMinuteHandAngle,
          gTasks[taskId].tMvmtDir,
          gTasks[taskId].tMvmtSpeed);
    }
    else
    {
        gTasks[taskId].tMinuteHandAngle = gTasks[taskId].tMinutes * 6;
        gTasks[taskId].tHourHandAngle = (gTasks[taskId].tHours % 12) * 30 + (gTasks[taskId].tMinutes / 10) * 5;
        if (gMain.newKeys & A_BUTTON)
        {
            gTasks[taskId].func = Task_SetClock3;
            return;
        }
        else
        {
            gTasks[taskId].tMvmtDir = gMain.newKeys & A_BUTTON;
            if (gMain.heldKeys & DPAD_LEFT)
                gTasks[taskId].tMvmtDir = MVMT_BACKWARD;
            if (gMain.heldKeys & DPAD_RIGHT)
                gTasks[taskId].tMvmtDir = MVMT_FORWARD;
            if (gTasks[taskId].tMvmtDir)
            {
                if (gTasks[taskId].tMvmtSpeed < 0xFF)
                    gTasks[taskId].tMvmtSpeed++;
                gTasks[taskId].tMinuteHandAngle = CalcNewMinHandAngle(
                  gTasks[taskId].tMinuteHandAngle,
                  gTasks[taskId].tMvmtDir,
                  gTasks[taskId].tMvmtSpeed);
                AdvanceClock(taskId, gTasks[taskId].tMvmtDir);
            }
            else
            {
                gTasks[taskId].tMvmtSpeed = 0;
            }
        }
    }
}

//Ask player "Is this the correct time?"
static void Task_SetClock3(u8 taskId)
{
    Menu_DrawStdWindowFrame(2, 16, 27, 19);
    Menu_PrintText(gOtherText_CorrectTimePrompt, 3, 17);
    Menu_DrawStdWindowFrame(23, 8, 29, 13);
    Menu_PrintItems(24, 9, 2, gMenuYesNoItems);
    InitMenu(0, 24, 9, 2, 1, 5);
    gTasks[taskId].func = Task_SetClock4;
}

//Get menu selection
static void Task_SetClock4(u8 taskId)
{
    switch (Menu_ProcessInputNoWrap_())
    {
    case 0:     //YES
        PlaySE(SE_SELECT);
        gTasks[taskId].func = Task_SetClock5;   //Move on
        return;
    case -1:    //B button
    case 1:     //NO
        Menu_DestroyCursor();
        PlaySE(SE_SELECT);
        Menu_EraseWindowRect(23, 8, 29, 13);
        Menu_EraseWindowRect(2, 16, 27, 19);
        gTasks[taskId].func = Task_SetClock2;   //Go back and let player adjust clock
    }
}

//Set the time offset based on the wall clock's time
static void Task_SetClock5(u8 taskId)
{
    RtcInitLocalTimeOffset(gTasks[taskId].tHours, gTasks[taskId].tMinutes);
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
    gTasks[taskId].func = Task_SetClock6;
}

static void Task_SetClock6(u8 taskId)
{
    if (!gPaletteFade.active)
        SetMainCallback2((MainCallback)gMain.savedCallback);
}

static void Task_ViewClock1(u8 taskId)
{
    if (!gPaletteFade.active)
        gTasks[taskId].func = Task_ViewClock2;
}

//Wait for A or B press
static void Task_ViewClock2(u8 taskId)
{
    InitClockWithRtc(taskId);
    if (gMain.newKeys & (A_BUTTON | B_BUTTON))
        gTasks[taskId].func = Task_ViewClock3;
}

static void Task_ViewClock3(u8 taskId)
{
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
    gTasks[taskId].func = Task_ViewClock4;
}

static void Task_ViewClock4(u8 taskId)
{
    if (!gPaletteFade.active)
        SetMainCallback2((MainCallback)gMain.savedCallback);
}

static u8 CalcMinHandDelta(u16 speed)
{
    if (speed > 60)
        return 6;
    else if (speed > 30)
        return 3;
    else if (speed > 10)
        return 2;
    else
        return 1;
}

//Calculates the new angle of the minute hand when setting the clock
static u16 CalcNewMinHandAngle(u16 angle, u8 direction, u8 speed)
{
    u8 delta = CalcMinHandDelta(speed);

    switch (direction)
    {
    case MVMT_BACKWARD:
        if (angle)
            angle -= delta;
        else
            angle = 360 - delta;
        break;
    case MVMT_FORWARD:
        if (angle < 360 - delta)
            angle = angle + delta;
        else
            angle = 0;
        break;
    }
    return angle;
}

//Advances clock forward or backward by 1 minute
static u8 AdvanceClock(u8 taskId, u8 direction)
{
    switch (direction)
    {
    case MVMT_BACKWARD:
        if (gTasks[taskId].tMinutes > 0)
            gTasks[taskId].tMinutes--;
        else
        {
            gTasks[taskId].tMinutes = 59;
            if (gTasks[taskId].tHours > 0)
                gTasks[taskId].tHours--;
            else
                gTasks[taskId].tHours = 23;
            UpdateClockPeriod(taskId, direction);
        }
        break;
    case MVMT_FORWARD:
        if (gTasks[taskId].tMinutes < 59)
            gTasks[taskId].tMinutes++;
        else
        {
            gTasks[taskId].tMinutes = 0;
            if (gTasks[taskId].tHours < 23)
                gTasks[taskId].tHours++;
            else
                gTasks[taskId].tHours = 0;
            UpdateClockPeriod(taskId, direction);
        }
        break;
    }
    return 0;
}

//Updates the clock period (AM/PM) if it needs to change
static void UpdateClockPeriod(u8 taskId, u8 direction)
{
    u8 hours = gTasks[taskId].tHours;

    switch (direction)
    {
    case MVMT_BACKWARD:
        switch (hours)
        {
        case 11:
            gTasks[taskId].tPeriod = PERIOD_AM;
            break;
        case 23:
            gTasks[taskId].tPeriod = PERIOD_PM;
            break;
        }
        break;
    case MVMT_FORWARD:
        switch (hours)
        {
        case 0:
            gTasks[taskId].tPeriod = PERIOD_AM;
            break;
        case 12:
            gTasks[taskId].tPeriod = PERIOD_PM;
            break;
        }
        break;
    }
}

static void InitClockWithRtc(u8 taskId)
{
    RtcCalcLocalTime();
    gTasks[taskId].tHours = gLocalTime.hours;
    gTasks[taskId].tMinutes = gLocalTime.minutes;
    gTasks[taskId].tMinuteHandAngle = gTasks[taskId].tMinutes * 6;
    gTasks[taskId].tHourHandAngle = (gTasks[taskId].tHours % 12) * 30 + (gTasks[taskId].tMinutes / 10) * 5;
    if (gLocalTime.hours <= 11)
        gTasks[taskId].tPeriod = PERIOD_AM;
    else
        gTasks[taskId].tPeriod = PERIOD_PM;
}

static void SpriteCB_MinuteHand(struct Sprite *sprite)
{
    u16 angle = gTasks[sprite->data[0]].tMinuteHandAngle;
    s16 sin = Sin2(angle) / 16;
    s16 cos = Cos2(angle) / 16;
    u16 x, y;

    SetOamMatrix(0, cos, sin, -sin, cos);
    x = sClockHandCoords[angle][0];
    y = sClockHandCoords[angle][1];

    //Manual sign extension
    if (x > 0x80)
        x |= 0xFF00;
    if (y > 0x80)
        y |= 0xFF00;

    sprite->x2 = x;
    sprite->y2 = y;
}

static void SpriteCB_HourHand(struct Sprite *sprite)
{
    u16 angle = gTasks[sprite->data[0]].tHourHandAngle;
    s16 sin = Sin2(angle) / 16;
    s16 cos = Cos2(angle) / 16;
    u16 x, y;

    SetOamMatrix(1, cos, sin, -sin, cos);
    x = sClockHandCoords[angle][0];
    y = sClockHandCoords[angle][1];

    //Manual sign extension
    if (x > 0x80)
        x |= 0xFF00;
    if (y > 0x80)
        y |= 0xFF00;

    sprite->x2 = x;
    sprite->y2 = y;
}

static void SpriteCB_AMIndicator(struct Sprite *sprite)
{
    if (gTasks[sprite->data[0]].tPeriod != PERIOD_AM)
    {
        if (sprite->data[1] >= 60 && sprite->data[1] < 90)
            sprite->data[1] += 5;
        if (sprite->data[1] < 60)
            sprite->data[1]++;
    }
    else
    {
        if (sprite->data[1] > 45 && sprite->data[1] <= 75)
            sprite->data[1] -= 5;
        if (sprite->data[1] > 75)
            sprite->data[1]--;
    }
    sprite->x2 =  Cos2(sprite->data[1]) * 30 / 4096;
    sprite->y2 = Sin2(sprite->data[1]) * 30 / 4096;
}

static void SpriteCB_PMIndicator(struct Sprite *sprite)
{
    if (gTasks[sprite->data[0]].tPeriod != PERIOD_AM)
    {
        if (sprite->data[1] >= 105 && sprite->data[1] < 135)
            sprite->data[1] += 5;
        if (sprite->data[1] < 105)
            sprite->data[1]++;
    }
    else
    {
        if (sprite->data[1] > 90 && sprite->data[1] <= 120)
            sprite->data[1] -= 5;
        if (sprite->data[1] > 120)
            sprite->data[1]--;
    }
    sprite->x2 = Cos2(sprite->data[1]) * 30 / 4096;
    sprite->y2 = Sin2(sprite->data[1]) * 30 / 4096;
}
