
#define _SUPPRESS_PLIB_WARNING
#define _DISABLE_OPENADC10_CONFIGPORT_WARNING
#include <plib.h>
#include "menu.h"
#include "mainmenu.h"
#include "Infrared.h"
#include "Timezone/tzmaths.h"
#include "Timezone/tz_coords.h"
#include <string.h>

cycleitem tz_cycle[sizeof(timezones)/sizeof(*timezones)+2] = {
   { " NONE " },
   { "AU EAS" },
   { "AU QLD" },
   { "AU  SA" },
   { "AU N7T" },
   { "AU u:A" }, // AU WA
   { "AU EUC" },
   { "AU LHI" },
   { "AU COC" },
   { "AU;@AC" }, // AUMAC
   { "NZ  NZ" },
   { "NZ CHA" },
   { "INTHAI" },
   { "JAPKOR" },
   { " FIJI " },
   { "USA HI" },
   { "USA AK" },
   { "NA ;@E" }, // NA WE
   { "NA ;@O" }, // NA MO
   { "ARIZON" },
   { "NA CEN" },
   { "NA EAS" },
   { "ASA;@O" }, // ASAMO
   { "SA BOL" },
   { "CAN NL" },
   { "CAN NB" },
   { " PERU " },
   { "CAN SK" },
   { "EUu:ES" }, // EUWES
   { "EU  IS" },
   { "EU  UK" },
   { "EU EAS" },
   { "EU;@OS" }, // EUMOS
   { "AS NKO" },
   { "AS BAN" },
   { "AS NEP" },
   { "RUu:ES" }, // RUWES
   { "AF;@OR" }, // AFMOR
   { "AF ALG" },
   { "AF LIB" },
   { "AF EGY" },
   { "AFNA;@" }, // AFNAM
   { "AF AZO" },
   { "AF;@AU" }, // AFMAU
   { " IRAN " },
   { "AFGHAN" },
   { "ISRAEL" },
   { "GAZA S" },
   { "JORLEB" },
   { "SA SEB" },
   { "SA NEB" },
   { "SA PAR" },
   { "BRAZIL" },
   { "SA URA" },
   { "SA VEN" },
   { "MEXBJC" },
   { "MEX u:" }, // MEX W
   { "MEX YU" },
   { "MEX EA" },
   { "RU EAS" },
   { "GEORGI" },
   { "INDIAS" },
   { ";@ONGO" }, // MONGO
   { "GRQAAN" },
   { "GREENL" },
   { "ATLSSI" },
   { "PA BAK" },
   { "PASA;@" }, // PASAM
   { "PA TON" },
   { "PA KIR" },
   { "FR PON" },
   { "PA;@AR" }, // PAMAR
   { "PAGA;@" }, // PAGAM
   { "PAPITC" },
   { "\0\0\0\0\0\0" }
};

cycleitem dst_cycle[] = {
    { "NONE  " },
    { "AUST  " },
    { "NZ    " },
    { "FIJI  " },
    { "NTHA;@" }, // NTHAM
    { "EURO  " },
    { "BRAZIL" },
    { "PARAGU" },
    { "URUGUA" },
    { ";@EXIC" }, // MEXIC
    { ";@ONGO" }, // MONGO
    { "SA;@OA" }, // SAMOA
    { "NA;@IB" }, // NAMIB
    { ";@OROC" }, // MOROC
    { "EGYPT " },
    { ";@IDEA" }, // MIDEA
    { "ISRAEL" },
    { "PALEST" },
    { "IRAN  " },
    { "GRNLND" },
    { "\0\0\0\0\0\0" }
};

menuitem tz_name_submenu[] = {
    { " NO TZ", toggle, { .altname = " NO TZ" } },
    { "\0\0\0\0\0\0" }
};

menuitem tz_submenu[] = {
    { "[UR7TZ", show_tznum }, //numeric, { .b = { .step = 0, .c = { .num = { 0, 0, 0 } } } } }, // CUR TZ
    { "TZNA;@", show_tzname }, //submenu, { .submenu = tz_name_submenu } }, // TZNAM
    { "LA7TIT", show_lat },//submenu, { .submenu = tz_name_submenu } }, // LATIT
    { "LONGIT", show_lon },//submenu, { .submenu = tz_name_submenu } }, // LATIT
    { "N SA7T", show_num_sats },
    { "0FFSET", numeric_tz, { .b = { .step = 15, .c = { .num = { -1, -2200, 2200 } } } } },
    { "DAYDEL", numeric, { .b = { .step = 30, .c = { .num = { 0, 0, 60 } } } } },
    { "DAYRUL", updownlist, { .a = { .cycleitems = dst_cycle } } },
    { "\0\0\0\0\0\0" }
};

cycleitem month_cycle[] = {
    { "01 JAN" },
    { "02 FEB" },
    { "03;@AR" }, // 03MAR
    { "04 APR" },
    { "05;@AY" }, // 05MAY
    { "06 JUN" },
    { "07 JUL" },
    { "08 AUG" },
    { "09 SEP" },
    { "100[7T" }, // 10OCT
    { "11 NOV" },
    { "12 DEC" },
    { "\0\0\0\0\0\0" }
};

cycleitem na_cycle[] = {
    { "  NA  " },
    { "\0\0\0\0\0\0" }
};

cycleitem week_cycle[] = {
    { "1S7T  " }, // 1ST
    { "2ND   " },
    { "3RD   " },
    { "47TH  " }, // 4TH
    { "LAS7T " },
    { "\0\0\0\0\0\0" }
};

cycleitem day_cycle[] = {
    { "SUNDAY" },
    { ";@ONDA" }, // MONDA
    { "7TUESD" }, // TUESD
    { "U:EDNE" }, // WEDNE
    { "7THURS" }, // THURS
    { "FRIDAY" },
    { "SA7TUR" }, // SATUR
    { "\0\0\0\0\0\0" }
};

cycleitem dsmod_cycle[] = {
    { "HDU:;@" }, // HDWM
    { "NOCARN" },
    { "NORA;@" }, // NORAM
    { "NOROSH" },
    { "EQUINO" },
    { "\0\0\0\0\0\0" }
};

menuitem dst_submenu[] = {
    { "STA;@0", updownlist, { .a = { .cycleitems = month_cycle } } }, // STAMO
    { "STAU:E", updownlist, { .a = { .cycleitems = week_cycle } } }, // STAWE
    { "STADAY", updownlist, { .a = { .cycleitems = day_cycle } } },
    { "STA HR", numeric,    { .b = { .step = 1, .c = { .num = { 0, -48, 17 } } } } },
    { "END;@0", updownlist, { .a = { .cycleitems = month_cycle } } }, // ENDMO
    { "ENDU:E", updownlist, { .a = { .cycleitems = week_cycle } } }, // ENDWE
    { "ENDDAY", updownlist, { .a = { .cycleitems = day_cycle } } },
    { "END HR", numeric,    { .b = { .step = 1, .c = { .num = { 0, -48, 17 } } } } },
    { "DS;@OD", updownlist, { .a = { .cycleitems = dsmod_cycle } } }, // DSMOD
    { "\0\0\0\0\0\0" }
};

menuitem alarm_submenu[] = {
    { "ALA ON", alarm_toggle, { .altname = "ALAOFF" } },
    { ";@ONOF", alarm_time, { .b = { .step = 1 } } }, // MONON
    { "7TUEOF", alarm_time, { .b = { .step = 1 } } }, // TUEON
    { "U:EDOF", alarm_time, { .b = { .step = 1 } } }, // WEDON
    { "7THUOF", alarm_time, { .b = { .step = 1 } } }, // THUON
    { "FRIOFF", alarm_time, { .b = { .step = 1 } } },
    { "SA7TOF", alarm_time, { .b = { .step = 1 } } }, // SATON
    { "SUNOFF", alarm_time, { .b = { .step = 1 } } },
    { "U:D ON", alarm_time_group, { .b = { .step = 1 } } }, // WD ON
    { "U:E ON", alarm_time_group, { .b = { .step = 1 } } }, // WE ON
    { "ALL ON", alarm_time_group, { .b = { .step = 1 } } },
    { "\0\0\0\0\0\0" }
};

menuitem autodim_submenu[] = {
    { "BRIGHT", numeric_pct, { .b = { .step = 1, .c = { .num = { 100, 5, 100 } } } } },
    { " ULI;@", numeric_pct, { .b = { .step = 1, .c = { .num = { 75, 0, 100 } } } } }, //  ULIM
    { " LLI;@", numeric_pct, { .b = { .step = 1, .c = { .num = { 25, 0, 100 } } } } }, //  LLIM
    { ";@INBR", numeric_pct, { .b = { .step = 1, .c = { .num = { 25, 0, 100 } } } } }, // MINBR
    { "CUR RD", show_autodim_vals },
    { "\0\0\0\0\0\0" }
};

menuitem piezo_submenu[] = {
    { "DURA  ", numeric_sec, { .b = { .step = 1, .c = { .num = { 10, 0, 900 } } } } },
    { "HZ    ", numeric_hz, { .b = { .step = 1, .c = { .num = { 1, 0, 10 } } } } },
    { "DUTY  ", numeric_pct, { .b = { .step = 1, .c = { .num = { 50, 1, 100 } } } } },
    { "CADENC", numeric_any, { .b = { .step = 1, .c = { .val = 100000 } } } },
    { "\0\0\0\0\0\0" }
};

cycleitem pm_dp_cycle[] = {
    { "P;@  -" }, // PM  -
    { "P;@  H" }, // PM  H
    { "P;@ ;@" }, // PM M
    { "P;@  S" }, // PM S
    { "\0\0\0\0\0\0" }
};

cycleitem column_led_cycle[] = {
    { "COLFLA" },
    { "COL ON" },
    { "COLOFF" },
    { "DP  ON" },
    { "DP FLA" },
    { "\0\0\0\0\0\0" }
};

cycleitem date_format_cycle[] = {
    { "DD;@YY" }, // DDMYY
    { ";@DDYY" }, // MDDYY
    { "YY;@DD" }, // YYMDD
    { "\0\0\0\0\0\0" }
};

cycleitem gps_lock_cycle[] = {
    { "REQUIR" },
    { "IGNORE" },
    { "\0\0\0\0\0\0" }
};

menuitem options_submenu[] = {
    { "24 HR ", toggle,     { .altname = "12 HR " } },
    { "P;@  -", cycle,      { .a = { .cycleitems = pm_dp_cycle } } }, // PM  -
    { "LZBOFF", toggle,     { .altname = "LZBON " } },
    { "COLFLA", cycle,      { .a = { .cycleitems = column_led_cycle } } },
    { "DD;@YY", cycle,      { .a = { .cycleitems = date_format_cycle } } }, // DDMYY
    { "DI;@  ", submenu,    { .submenu = autodim_submenu } }, // DIM
    { "PIEZO ", submenu,    { .submenu = piezo_submenu } },
    { "X7TAL ", numeric,    { .b = { .step = 1, .c = { .num = { 0, -512, 511, } } } } },
    { "GPSLOC", updownlist, { .a = { .cycleitems = gps_lock_cycle } } },
    { "\0\0\0\0\0\0" }
};

#ifdef SHOW_UTC_FEATURE
menuitem ir_codes_submenu[30] = {
#else
menuitem ir_codes_submenu[29] = {
#endif
    { "^U;@ 0", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_0 } } } }, // NUM 0
    { "^U;@ 1", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_1 } } } }, // NUM 1
    { "^U;@ 2", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_2 } } } }, // NUM 2
    { "^U;@ 3", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_3 } } } }, // NUM 3
    { "^U;@ 4", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_4 } } } }, // NUM 4
    { "^U;@ 5", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_5 } } } }, // NUM 5
    { "^U;@ 6", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_6 } } } }, // NUM 6
    { "^U;@ 7", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_7 } } } }, // NUM 7
    { "^U;@ 8", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_8 } } } }, // NUM 8
    { "^U;@ 9", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_9 } } } }, // NUM 9
    { "ES[APE", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_STANDBY } } } }, // ESCAPE
    { "DA7TED", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_MUTE } } } }, // DATED
    { "[^7TUP", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_CHUP } } } }, // CNTUP
    { "[^7TDN", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_CHDN } } } }, // CNTDN
    { "BRI UP", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_VOLUP } } } },
    { "BRI DN", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_VOLDN } } } },
    { "UP    ", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_UP } } } },
    { "DOU:N ", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_DOWN } } } }, // DOWN
    { "LEF7T ", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_LEFT } } } }, // LEFT
    { "RIGH7T", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_RIGHT } } } }, // RIGHT
    { "SELECT", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_OK } } } },
    { "7TISUB", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_RW } } } }, // TISUB
    { "7TIRES", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_PLAY } } } }, // TIRES
    { "7TIADD", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_FF } } } }, // TIADD
    { "7TILAP", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_STOP } } } }, // TILAP
    { "7TIPAU", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_PAUSE } } } }, // TIPAU
    { "ALA ON", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_RECORD } } } },
    { "DISPON", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_OSD } } } },
#ifdef SHOW_UTC_FEATURE
    { "SHOUTC", ircode, { .b = { .c = { .val = IR_RC5|RC5_A1012_TV170_TELETEXT } } } },
#endif
    { "\0\0\0\0\0\0" }
};

menuitem ir_submenu[] = {
    { "DISP  ", set_clockmode, { .b = { .c = { .mode = show_ir_code } } } },
    { "CHANGE", submenu, { .submenu = ir_codes_submenu } },
    { "\0\0\0\0\0\0" }
};

menuitem mainmenu[] = {
    { "ALAR;@", submenu, { .submenu = alarm_submenu } },
    { "0P7TS ", submenu, { .submenu = options_submenu } }, // OPTS
    { "IR    ", submenu, { .submenu = ir_submenu } },
    { "SE7TDA", set_date, { .b = { .step = 1 } } }, // SETDA
    { "SE7TT1", set_time, { .b = { .step = 1 } } }, // SETTI
    { "SE7TTZ", updownlist, { .a = { .cycleitems = tz_cycle } } }, // SETTZ
    { "\0\0\0\0\0\0" }
};

int tz_menu_timezone;
signed long* tz_menu_tzinfo;
daylight_savings_info* tz_menu_dstinfo;

#define DST_FULL_MASK (DST_MASK|DST_DELTA_30)
__attribute__((mips16)) static void mainmenu_update_tz_menu(int timezone, signed long* tzinfo, daylight_savings_info* dstinfo) {
    int offset = tzinfo[tz_map[timezone]];
    int flags = offset&DST_FULL_MASK;
    cycleitem* dstnam;

    if( offset < 0 ) {
        offset |= DST_FULL_MASK;
        flags ^= DST_FULL_MASK;
    } else {
        offset &= ~DST_FULL_MASK;
        flags &= DST_FULL_MASK;
    }

    dstnam = dst_cycle;
    tz_submenu[5].data.b.c.val = offset;
    tz_submenu[6].data.b.c.val = (flags&DST_DELTA_30) ? 30 : ( (flags&DST_MASK) ? 60 : 0 );

    if( flags&DST_MASK ) {
        int i;
        for( i = 0; i < sizeof(daylight_savings)/sizeof(*daylight_savings); ++i ) {
            ++dstnam;
            if( (daylight_savings[i].flag&DST_FLAG_TYPE_MASK) == (flags&DST_MASK) )
                break;
        }
        if( i == sizeof(daylight_savings)/sizeof(*daylight_savings) )
            dstnam = dst_cycle;
    }
    tz_submenu[7].data.a.selected = dstnam - dst_cycle;
}

__attribute__((mips16)) static void mainmenu_update_dst_week_day(menuitem* items, int week, int dow){
    if( dst_submenu[8].name[0] == 'E' ) {
        items[0].data.a.cycleitems = na_cycle;
        items[0].data.a.selected = 0;
        memcpy(items[0].name, na_cycle[0].altname, 6);
        items[1].type = numeric;
        items[1].data.b.step = 1;
        items[1].data.b.c.num.min = 0;
        items[1].data.b.c.num.max = 6;
        items[1].data.b.c.num.val = dow;
    } else {
        items[0].data.a.cycleitems = week_cycle;
        items[0].data.a.selected = (week == WEEK_LAST ? 4 : week-1);
        items[1].type = updownlist;
        items[1].data.a.cycleitems = day_cycle;
        items[1].data.a.selected = dow;
    }
}

__attribute__((mips16)) static unsigned char mainmenu_update_dst_menu(int timezone, const signed long* tzinfo, const daylight_savings_info* dstinfo) {
    const daylight_savings_info* info = dstinfo;
    const daylight_savings_info* end  = dstinfo + (sizeof(daylight_savings)/sizeof(*daylight_savings));
    while( info < end ) {
        if( tzinfo[timezone] & info->flag ) {
            int mode;

            dst_submenu[0].data.a.selected = info->start_month-1;
            mainmenu_update_dst_week_day(&dst_submenu[1], info->start_week, info->start_dow);
            dst_submenu[3].data.b.c.num.val = info->start_hour;

            dst_submenu[4].data.a.selected = info->finish_month-1;
            mainmenu_update_dst_week_day(&dst_submenu[5], info->finish_week, info->finish_dow);
            dst_submenu[7].data.b.c.num.val = info->finish_hour;

            mode = 0;
            if( info->flag & EXCEPT_CARNAVAL )
                mode = 1;
            else if( info->flag & EXCEPT_RAMADAN )
                mode = 2;
            else if( info->flag & EXCEPT_ROSH_HAS )
                mode = 3;
            else if( info->start_week == WEEK_EQUI )
                mode = 4;
            dst_submenu[8].data.a.selected = mode;
            return 1;
        }

        ++info;
    }
    return 0;
}

extern int gps_detected;
__attribute__((mips16)) void mainmenu_set_mode(int timezone, signed long* tzinfo, daylight_savings_info* dstinfo) {
    tz_menu_timezone = timezone;
    tz_menu_tzinfo = tzinfo;
    tz_menu_dstinfo = dstinfo;

    if( !gps_detected ) {
        memcpy(mainmenu[3].name, "SE7TDA", 6);
        memcpy(mainmenu[4].name, "SE7TT1", 6);
        memcpy(tz_cycle[0].altname, " NONE ", 6);
        mainmenu[3].type = set_date;
        mainmenu[3].data.b.step = 1;
        mainmenu[4].type = set_time;
        mainmenu[4].data.b.step = 1;
        tz_menu_timezone = -1;
    } else {
        int offset, flags;
        cycleitem* dstnam;

        memcpy(mainmenu[3].name, "CHA TZ", 6);
        memcpy(mainmenu[4].name, "CHADST", 6);
        memcpy(tz_cycle[0].altname, "AU7TO ", 6); // AUTO
        mainmenu[3].type = submenu;
        mainmenu[3].data.submenu = tz_submenu;

        mainmenu_update_tz_menu(timezone, tzinfo, dstinfo);
        if( mainmenu_update_dst_menu(timezone, tzinfo, dstinfo) ) {
            mainmenu[4].type = submenu;
            mainmenu[4].data.submenu = dst_submenu;
        } else {
            mainmenu[4].type = updownlist;
            mainmenu[4].data.a.cycleitems = na_cycle;
            mainmenu[4].data.a.selected = 0;
        }
    }
}

__attribute__((mips16)) static void mainmenu_save_tz_changes() {
    signed long new_val;

    if( tz_menu_timezone >= 0 ) {
        new_val = tz_submenu[5].data.b.c.val;
        if( tz_submenu[6].data.b.c.val == 30 )
            new_val ^= DST_DELTA_30;
        if( tz_submenu[6].data.b.c.val && tz_submenu[7].data.a.selected > 0 )
            new_val ^= tz_menu_dstinfo[tz_submenu[7].data.a.selected-1].flag&DST_MASK;

        if( new_val != tz_menu_tzinfo[tz_menu_timezone] ) {
            tz_menu_tzinfo[tz_menu_timezone] = new_val;
            DataEEWrite(new_val, MAX_MENU_STATE_SIZE+1 + tz_menu_timezone);
        }
    }
}

__attribute__((mips16)) static void mainmenu_get_dst_week_day(menuitem* items, signed char* week, unsigned char* dow){
    if( dst_submenu[8].name[0] == 'E' ) {
        *week = WEEK_EQUI;
        *dow = items[1].data.b.c.num.val;
    } else {
        *week = (items[0].data.a.selected == 4 ? WEEK_LAST : items[0].data.a.selected+1);
        *dow = items[1].data.a.selected;
    }
}

__attribute__((mips16)) static void mainmenu_save_dst_changes() {
    daylight_savings_info* info = tz_menu_dstinfo;
    daylight_savings_info* end  = tz_menu_dstinfo + (sizeof(daylight_savings)/sizeof(*daylight_savings));
    while( info < end ) {
        if( tz_menu_tzinfo[tz_map[tz_menu_timezone]] & info->flag ) {
            int mode;

            info->start_month = dst_submenu[0].data.a.selected + 1;//menu_get_cycle_item_index(month_cycle, dst_submenu[0].name) + 1;
            mainmenu_get_dst_week_day(&dst_submenu[1], &info->start_week, &info->start_dow);
            info->start_hour = dst_submenu[3].data.b.c.num.val;

            info->finish_month = dst_submenu[4].data.a.selected + 1;//menu_get_cycle_item_index(month_cycle, dst_submenu[4].name) + 1;
            mainmenu_get_dst_week_day(&dst_submenu[5], &info->finish_week, &info->finish_dow);
            info->finish_hour = dst_submenu[7].data.b.c.num.val;

            mode = dst_submenu[8].data.a.selected;
            info->flag = (info->flag & DST_FLAG_TYPE_MASK) | (mode == 1 ? EXCEPT_CARNAVAL : (mode == 2 ? EXCEPT_RAMADAN : (mode == 3 ? EXCEPT_ROSH_HAS : 0)));
            DataEEWrite( pack_dst_info(info), MAX_MENU_STATE_SIZE+1 + (info - tz_menu_dstinfo) + sizeof(timezones)/sizeof(*timezones) );
            return;
        }

        ++info;
    }
}

__attribute__((mips16)) void change_rtcc_from_UTC_to_local(int timezone) {
    rtccTime time;
    rtccDate date;
    int gtime, gdate;
    time.l = RtccGetTime();
    date.l = RtccGetDate();
    bcd_time_date_to_decimal(&gtime, &gdate, &time, &date);
    apply_timezone(&gtime, &gdate, timezone, tz_menu_tzinfo, tz_menu_dstinfo);
    decimal_time_date_to_bcd(&time, &date, gtime, gdate);
    RtccFixupDOW(&date);
    RtccSetTimeDate(time.l, date.l);
}
__attribute__((mips16)) void change_rtcc_from_local_to_UTC(int timezone) {
    rtccTime time;
    rtccDate date;
    int gtime, gdate;
    time.l = RtccGetTime();
    date.l = RtccGetDate();
    bcd_time_date_to_decimal(&gtime, &gdate, &time, &date);
    unapply_timezone(&gtime, &gdate, timezone, tz_menu_tzinfo, tz_menu_dstinfo);
    decimal_time_date_to_bcd(&time, &date, gtime, gdate);
    RtccFixupDOW(&date);
    RtccSetTimeDate(time.l, date.l);
}

extern int tz_override, gps_detected;
__attribute__((mips16)) void mainmenu_callback(menu_callback_type type, menuitem* item) {
    if( type == menu_exit ) {
        if( item == mainmenu ) {
            mainmenu_save_tz_changes();
        } else if( item == tz_submenu ) {
            mainmenu_update_dst_menu(tz_menu_timezone, tz_menu_tzinfo, tz_menu_dstinfo);
        } else if( item == dst_submenu ) {
            mainmenu_save_dst_changes();
        } else if( item == options_submenu ) {
            if( RtccGetCalibration() != OPTS_XTAL_TRIM )
                RtccSetCalibration(OPTS_XTAL_TRIM);
        }
    } else if( type == menu_cycle_change ) {
        if( item == &dst_submenu[8] ) {
            int which = menu_get_cycle_item_index(item->data.a.cycleitems, item->name);
            if( which == 0 || which == 4 ) {
                const daylight_savings_info* info = tz_menu_dstinfo;
                const daylight_savings_info* end  = tz_menu_dstinfo + (sizeof(daylight_savings)/sizeof(*daylight_savings));
                while( info < end ) {
                    if( tz_menu_tzinfo[tz_map[tz_menu_timezone]] & info->flag ) {
                        mainmenu_update_dst_week_day(&dst_submenu[1], info->start_week, dst_submenu[2].type == numeric ? dst_submenu[2].data.b.c.num.val : dst_submenu[2].data.a.selected);
                        mainmenu_update_dst_week_day(&dst_submenu[5], info->finish_week, dst_submenu[6].type == numeric ? dst_submenu[6].data.b.c.num.val : dst_submenu[6].data.a.selected);
                        break;
                    }
                }
            }
        }
    } else if( type == menu_updownlist_change ) {
        if( item->data.a.cycleitems == tz_cycle ) {
            int new_tz_override = reverse_tz_map((int)item->data.a.selected - 1);
            if( new_tz_override != tz_override ) {
                if( !gps_detected ) {
                    if( new_tz_override == -1 && tz_override != -1 ) {
                        // Changing from having a timezone to not.
                        // Clock is in UTC so will need to apply old timezone to get local time, then set clock to local time.
                        change_rtcc_from_UTC_to_local(tz_override);
                    } else if( tz_override == -1 ) {
                        // Changing from not having a timezone to having one.
                        // Clock is in local time so will need to reverse apply new timezone to get UTC, then set clock to that.
                        change_rtcc_from_local_to_UTC(new_tz_override);
                    } else {
                        // Changing from one timezone to another.
                        // Clock is in UTC so just need to change timezone.
                    }
                }
                tz_override = new_tz_override;
            }
        }
    }
}
