/* Copyright 2023 Dual Tachyon
 * https://github.com/DualTachyon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 */

#include <string.h>
#include <stdlib.h>

#include "../app/dtmf.h"
#include "../app/menu.h"
#include "../bitmaps.h"
#include "../board.h"
#include "../dcs.h"
#include "../driver/backlight.h"
#include "../driver/bk4819.h"
#include "../driver/eeprom.h"
#include "../driver/st7565.h"
#include "../external/printf/printf.h"
#include "../frequencies.h"
#include "../helper/battery.h"
#include "../misc.h"
#include "../settings.h"
#include "helper.h"
#include "inputbox.h"
#include "menu.h"
#include "ui.h"


const t_menu_item MenuList[] =
        {
//   text,          menu ID
                {"STP", MENU_STEP},
                {"PWR", MENU_TXP}, // was "TXP"
                {"RDCS", MENU_R_DCS}, // was "R_DCS"
                {"RCTCS", MENU_R_CTCS}, // was "R_CTCS"
                {"TDCS", MENU_T_DCS}, // was "T_DCS"
                {"TCTCS", MENU_T_CTCS}, // was "T_CTCS"
                {"TxODir", MENU_SFT_D}, // was "SFT_D"
                {"TxOffs", MENU_OFFSET}, // was "OFFSET"
                {"WID", MENU_W_N},
                {"SCR", MENU_SCR}, // was "SCR"
                {"BusyL", MENU_BCL}, // was "BCL"
                {"COMP", MENU_COMPAND},
                {"Mod", MENU_AM}, // was "AM"
#ifdef ENABLE_FEAT_F4HWN
#endif
                {"ScAdd1", MENU_S_ADD1},
                {"ScAdd2", MENU_S_ADD2},
                {"ScAdd3", MENU_S_ADD3},
                {"ChSave", MENU_MEM_CH}, // was "MEM-CH"
                {"ChDele", MENU_DEL_CH}, // was "DEL-CH"
                {"ChName", MENU_MEM_NAME},

                {"SList", MENU_S_LIST},
                {"SList1", MENU_SLIST1},
                {"SList2", MENU_SLIST2},
                {"SList3", MENU_SLIST3},
                {"ScnRev", MENU_SC_REV},
#ifndef ENABLE_FEAT_F4HWN
#ifdef ENABLE_NOAA
                {"NOAA-S",      MENU_NOAA_S    },
#endif
#endif
                {"F1S", MENU_F1SHRT},
                {"F1L", MENU_F1LONG},
                {"F2S", MENU_F2SHRT},
                {"F2L", MENU_F2LONG},
                {"ML", MENU_MLONG},

                {"KeyLck", MENU_AUTOLK}, // was "AUTOLk"
                {"TXTime", MENU_TOT},
                {"BatSav", MENU_SAVE}, // was "SAVE"
                {"BatTxt", MENU_BAT_TXT},
                {"Mic", MENU_MIC},
                {"MicBar", MENU_MIC_BAR},
                {"ChDisp", MENU_MDF}, // was "MDF"
                {"FSKMod", MENU_FSKMOD},
                {"FSKSRC", MENU_FSKSRC}, // was "AUTOLk"
                {"BLTime", MENU_ABR}, // was "ABR"
                {"BLMin", MENU_ABR_MIN},
                {"BLMax", MENU_ABR_MAX},
                {"BLTxRx", MENU_ABR_ON_TX_RX},
                {"Beep", MENU_BEEP},
#ifdef ENABLE_VOICE
                {"Voice",       MENU_VOICE         },
#endif
                {"Roger", MENU_ROGER},
                {"STE", MENU_STE},
                {"RP STE", MENU_RP_STE},
                {"1 Call", MENU_1_CALL},
#ifdef ENABLE_ALARM
                {"AlarmT", MENU_AL_MOD},
#endif
                {"UPCode", MENU_UPCODE},
                {"DNCode", MENU_DWCODE},
                {"PTT ID", MENU_PTT_ID},
                {"L DTMF", MENU_D_ST},
                {"D Prel", MENU_D_PRE},

                {"D Live", MENU_D_LIVE_DEC}, // live DTMF decoder
#ifndef ENABLE_FEAT_F4HWN
#ifdef ENABLE_AM_FIX
                {"AM Fix",      MENU_AM_FIX        },
#endif
#endif
#ifdef ENABLE_VOX
                {"VOX", MENU_VOX},
#endif
                {"RxMode", MENU_TDR},
                {"Sql", MENU_SQL},
#ifdef ENABLE_FEAT_F4HWN
                {"SPTT", MENU_SET_PTT},
                {"SInv", MENU_SET_INV},
                {"STmr", MENU_SET_TMR},
#ifdef ENABLE_FEAT_F4HWN_SLEEP
                {"SOff", MENU_SET_OFF},
#endif
#ifdef ENABLE_FEAT_F4HWN_VOL
                {"SVol", MENU_SET_VOL},
#endif
#ifdef ENABLE_NOAA
                {"SNWR",      MENU_NOAA_S    },
#endif
#endif
                // hidden menu items from here on
                // enabled if pressing both the PTT and upper side button at power-on
                {"F Lock", MENU_F_LOCK},
#ifdef ENABLE_F_CAL_MENU
                {"FrCali", MENU_F_CALI}, // reference xtal calibration
#endif
                {"BatCal", MENU_BATCAL}, // battery voltage calibration
                {"BatTyp", MENU_BATTYP}, // battery type 1600/2200mAh
                {"Reset", MENU_RESET}, // might be better to move this to the hidden menu items ?

                {"", 0xff}  // end of list - DO NOT delete or move this this
        };

const char gSubMenu_TXP[][5] =
        {
                "<.02",
                ".125",
                ".25",
                ".5",
                "1",
                "2",
                "5"
        };

const char gSubMenu_SFT_D[][4] =
        {
                "OFF",
                "+",
                "-"
        };

const char gSubMenu_W_N[][5] =
        {
                "WIDE",
                "NARR",
                "NAR+",
                "FSKQ"
        };

const char gSubMenu_OFF_ON[][4] =
        {
                "OFF",
                "ON"
        };

const char gSubMenu_NA[4] =
        {
                "N/A"
        };

const char *const gSubMenu_RXMode[] =
        {
                "MAIN",       // TX and RX on main only
                "RESP", // Watch both and respond
                "XBAND",      // TX on main, RX on secondary
                "DMON"  // always TX on main, but RX on both
        };

#ifdef ENABLE_VOICE
const char gSubMenu_VOICE[][4] =
{
    "OFF",
    "CHI",
    "ENG"
};
#endif

const char *const gSubMenu_MDF[] =
        {
                "FREQ",
                "CHNUM",
                "NAME",
                "NAME\n+\nFREQ"
        };

#ifdef ENABLE_ALARM
const char gSubMenu_AL_MOD[][5] =
        {
                "SITE",
                "TONE"
        };
#endif

const char *const gSubMenu_PTT_ID[] =
        {
                "OFF",
                "UP CODE",
                "DN CODE",
                "UP+DN\nCODE",
                "APOLLO"
        };

const char gSubMenu_FSKMod[][6] =
        {
                "450",
                "700",
                "1200",
                "2400",
                "A1200",
                "A2400",
                "NOAA"
        };

const char gSubMenu_ROGER[][6] =
        {
                "OFF",
                "RGR",
                "MDC"
        };

const char gSubMenu_RESET[][4] =
        {
                "VFO",
                "ALL"
        };

const char *const gSubMenu_F_LOCK[] =
        {
                "137-174\n400-470",
#ifdef ENABLE_FEAT_F4HWN_CA
                "FCC HAM\n144-148\n420-450",
                "CA HAM\n144-148\n430-450",
                "CE HAM\n144-146\n430-440",
                "GB HAM\n144-148\n430-440",
                "137-174\n400-430",
                "137-174\n400-438",
#endif
#ifdef ENABLE_FEAT_F4HWN_PMR
                "PMR 446",
#endif
#ifdef ENABLE_FEAT_F4HWN_GMRS_FRS_MURS
                "GMRS\nFRS\nMURS",
#endif
                "DIS\nALL",
                "UNL\nALL",
        };

const char gSubMenu_RX_TX[][6] =
        {
                "OFF",
                "TX",
                "RX",
                "TX/RX"
        };

const char gSubMenu_BAT_TXT[][5] =
        {
                "NONE",
                "VLT",
                "PRC"
        };

const char gSubMenu_BATTYP[][3] =
        {
                "16",
                "22",
                "35"
        };

#ifdef ENABLE_FEAT_F4HWN

const char gSubMenu_SET_PTT[][4] =
        {
                "HOL",
                "TOG",
        };
#endif

const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
        {
                {"NONE", ACTION_OPT_NONE},
#ifdef ENABLE_FLASHLIGHT
                {"FLASH", ACTION_OPT_FLASHLIGHT},
#endif
                {"PWR", ACTION_OPT_POWER},
                {"MON", ACTION_OPT_MONITOR},
                {"SCAN", ACTION_OPT_SCAN},
#ifdef ENABLE_VOX
                {"VOX", ACTION_OPT_VOX},
#endif
#ifdef ENABLE_ALARM
                {"ALARM", ACTION_OPT_ALARM},
#endif
#ifdef ENABLE_FMRADIO
                {"WFM", ACTION_OPT_FM},
#endif
#ifdef ENABLE_TX1750
                {"1750", ACTION_OPT_1750},
#endif
#ifdef ENABLE_REGA
                {"REGA\nALARM", ACTION_OPT_REGA_ALARM},
                {"REGA\nTEST", ACTION_OPT_REGA_TEST},
#endif
                {"LOCK\nKEY", ACTION_OPT_KEYLOCK},
                {"VFO A\nVFO B", ACTION_OPT_A_B},
                {"VFO\nMEM", ACTION_OPT_VFO_MR},
                {"MODE", ACTION_OPT_SWITCH_DEMODUL},
#ifdef ENABLE_BLMIN_TMP_OFF
                {"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF},      //BackLight Minimum Temporay OFF
#endif
#ifdef ENABLE_FEAT_F4HWN
                {"RX MOD", ACTION_OPT_RXMODE},
                {"PTT", ACTION_OPT_PTT},
                {"WIDE\nNAR", ACTION_OPT_WN},
#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
                {"MUTE", ACTION_OPT_MUTE},
#endif
#endif
        };

const uint8_t gSubMenu_SIDEFUNCTIONS_size = ARRAY_SIZE(gSubMenu_SIDEFUNCTIONS);

bool gIsInSubMenu;
uint8_t gMenuCursor;

int UI_MENU_GetCurrentMenuId() {
    if (gMenuCursor < ARRAY_SIZE(MenuList))
        return MenuList[gMenuCursor].menu_id;

    return MenuList[ARRAY_SIZE(MenuList) - 1].menu_id;
}

uint8_t UI_MENU_GetMenuIdx(uint8_t id) {
    for (uint8_t i = 0; i < ARRAY_SIZE(MenuList); i++)
        if (MenuList[i].menu_id == id)
            return i;
    return 0;
}

int32_t gSubMenuSelection;

// edit box
char edit_original[17]; // a copy of the text before editing so that we can easily test for changes/difference
char edit[17];
int edit_index;

void UI_DisplayMenu(void) {
    const unsigned int menu_list_width = 6; // max no. of characters on the menu list (left side)
    const unsigned int menu_item_x1 = (8 * menu_list_width) + 2;
    const unsigned int menu_item_x2 = LCD_WIDTH - 1;
    unsigned int i;
    char String[64];  // bigger cuz we can now do multi-line in one string (use '\n' char)


    UI_DisplayClear();

#ifdef ENABLE_FEAT_F4HWN
    UI_DrawLineBuffer(gFrameBuffer, 48, 0, 48, 55, 1); // Be ware, status zone = 8 lines, the rest = 56 ->total 64
    //UI_DrawLineDottedBuffer(gFrameBuffer, 0, 46, 50, 46, 1);

    for (uint8_t i = 0; i < 48; i += 2) {
        gFrameBuffer[5][i] = 0x40;
    }
#endif

#ifndef ENABLE_CUSTOM_MENU_LAYOUT
    // original menu layout
for (i = 0; i < 3; i++)
    if (gMenuCursor > 0 || i > 0)
        if ((gMenuListCount - 1) != gMenuCursor || i != 2)
            UI_PrintString(MenuList[gMenuCursor + i - 1].name, 0, 0, i * 2 /*, 8 */);

// invert the current menu list item pixels
for (i = 0; i < (8 * menu_list_width); i++)
{
    gFrameBuffer[2][i] ^= 0xFF;
    gFrameBuffer[3][i] ^= 0xFF;
}

// draw vertical separating dotted line
for (i = 0; i < 7; i++)
    gFrameBuffer[i][(8 * menu_list_width) + 1] = 0xAA;

// draw the little sub-menu triangle marker
if (gIsInSubMenu)
    memcpy(gFrameBuffer[0] + (8 * menu_list_width) + 1, BITMAP_CurrentIndicator, sizeof(BITMAP_CurrentIndicator));

// draw the menu index number/count
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);

UI_PrintStringSmallNormal(String, 2, 0, 6);

#else
    {   // new menu layout .. experimental & unfinished
        const int menu_index = gMenuCursor;  // current selected menu item
        i = 0;

        if (!gIsInSubMenu) {
            while (i < 2) {   // leading menu items - small text
                const int k = menu_index + i - 2;
                if (k < 0)
                    UI_PrintStringSmallNormal(MenuList[(gMenuListCount + k) % gMenuListCount].name, 0, 0,
                                              i);  // wrap-around
                else if (k >= (int) gMenuListCount)
                    UI_PrintStringSmallNormal(MenuList[k % gMenuListCount].name, 0, 0, i);  // wrap-around
                else
                    UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
                i++;
            }

            // current menu item - keep big n fat
            if (menu_index >= 0 && menu_index < (int) gMenuListCount) {
                //UI_DrawRectangleBuffer(gFrameBuffer, 0, 37, 16, 45, true);
                UI_PrintStringSmallBold(MenuList[menu_index].name, 0, 0, 2);
            }
            i++;

            while (i < 5) {   // trailing menu item - small text
                const int k = menu_index + i - 2;
                if (k < 0)
                    UI_PrintStringSmallNormal(MenuList[(gMenuListCount + k) % gMenuListCount].name, 0, 0,
                                              i);  // wrap-around
                else if (k >= (int) gMenuListCount)
                    UI_PrintStringSmallNormal(MenuList[k % gMenuListCount].name, 0, 0, i);  // wrap-around
                else
                    UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
                i++;
            }

            // draw the menu index number/count
#ifndef ENABLE_FEAT_F4HWN
            sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
            UI_PrintStringSmallNormal(String, 2, 0, 6);
#endif
        } else if (menu_index >= 0 && menu_index < (int) gMenuListCount) {   // current menu item
//          strcat(String, ":");
            UI_PrintString(MenuList[menu_index].name, 0, 0, 0 /*, 8 */);
//          UI_PrintStringSmallNormal(String, 0, 0, 0);
        }

#ifdef ENABLE_FEAT_F4HWN
        sprintf(String, "%02u/%u", 1 + gMenuCursor, gMenuListCount);
        UI_PrintStringSmallNormal(String, 6, 0, 6);
#endif
    }
#endif

    // **************

    memset(String, 0, sizeof(String));

    bool already_printed = false;

    /* Brightness is set to max in some entries of this menu. Return it to the configured brightness
       level the "next" time we enter here.I.e., when we move from one menu to another.
       It also has to be set back to max when pressing the Exit key. */

    BACKLIGHT_TurnOn();

    switch (UI_MENU_GetCurrentMenuId()) {
        case MENU_SQL:
            sprintf(String, "%ld", gSubMenuSelection);
            break;

        case MENU_MIC: {   // display the mic gain in actual dB rather than just an index number
            const uint8_t mic = gMicGain_dB2[gSubMenuSelection];
            sprintf(String, "+%u.%01udB", mic / 2, mic % 2);
        }
            break;

        case MENU_MIC_BAR:
#ifdef ENABLE_AUDIO_BAR
            strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
#else
            strcpy(String, gSubMenu_NA);
#endif
            break;

        case MENU_STEP: {
            uint16_t step = gStepFrequencyTable[FREQUENCY_GetStepIdxFromSortedIdx(gSubMenuSelection)];
            sprintf(String, "%d.%02ukHz", step / 100, step % 100);
            break;
        }

        case MENU_TXP:
            sprintf(String, "%sW", gSubMenu_TXP[gSubMenuSelection]);
            break;

        case MENU_R_DCS:
        case MENU_T_DCS:
            if (gSubMenuSelection == 0)
                strcpy(String, gSubMenu_OFF_ON[0]);
            else if (gSubMenuSelection < 105)
                sprintf(String, "D%03oN", DCS_Options[gSubMenuSelection - 1]);
            else
                sprintf(String, "D%03oI", DCS_Options[gSubMenuSelection - 105]);
            break;

        case MENU_R_CTCS:
        case MENU_T_CTCS: {
            if (gSubMenuSelection == 0)
                strcpy(String, gSubMenu_OFF_ON[0]);
            else
                sprintf(String, "%u.%uHz", CTCSS_Options[gSubMenuSelection - 1] / 10,
                        CTCSS_Options[gSubMenuSelection - 1] % 10);
            break;
        }

        case MENU_SFT_D:
            strcpy(String, gSubMenu_SFT_D[gSubMenuSelection]);
            break;

        case MENU_OFFSET:
            if (!gIsInSubMenu || gInputBoxIndex == 0) {
                sprintf(String, "%3ld.%05u", gSubMenuSelection / 100000, abs(gSubMenuSelection) % 100000);
                UI_PrintString(String, menu_item_x1, menu_item_x2, 1 /*, 8 */);
            } else {
                const char *ascii = INPUTBOX_GetAscii();
                sprintf(String, "%.3s.%.3s  ", ascii, ascii + 3);
                UI_PrintString(String, menu_item_x1, menu_item_x2, 1 /*, 8 */);
            }

            UI_PrintString("MHz", menu_item_x1, menu_item_x2, 3 /*, 8 */);

            already_printed = true;
            break;

        case MENU_W_N:
            strcpy(String, gSubMenu_W_N[gSubMenuSelection]);
            break;

        case MENU_SCR:
            if (gSubMenuSelection > 0) {
                sprintf(String, "%huHz\n%ld", inverse_scale_freq(gSubMenuSelection), gSubMenuSelection);
            } else {
                strcpy(String, "OFF");
            }
                BK4819_SetScramble(gSubMenuSelection);
            break;

        case MENU_VOX:
#ifdef ENABLE_VOX
            sprintf(String, gSubMenuSelection == 0 ? gSubMenu_OFF_ON[0] : "%lu", gSubMenuSelection);
#else
            strcpy(String, gSubMenu_NA);
#endif
            break;

        case MENU_ABR:
            if (gSubMenuSelection == 0) {
                strcpy(String, gSubMenu_OFF_ON[0]);
            } else if (gSubMenuSelection < 61) {
                sprintf(String, "%02ldm:%02lds", (((gSubMenuSelection) * 5) / 60), (((gSubMenuSelection) * 5) % 60));
            } else {
                strcpy(String, "ON");
            }

            // Obsolete ???
            //if(BACKLIGHT_GetBrightness() < 4)
            //    BACKLIGHT_SetBrightness(4);
            break;

        case MENU_ABR_MIN:
        case MENU_ABR_MAX:
            sprintf(String, "%ld", gSubMenuSelection);
            if (gIsInSubMenu)
                BACKLIGHT_SetBrightness(gSubMenuSelection);
            // Obsolete ???
            //else if(BACKLIGHT_GetBrightness() < 4)
            //    BACKLIGHT_SetBrightness(4);
            break;

        case MENU_AM:
            strcpy(String, gModulationStr[gSubMenuSelection]);
            break;

        case MENU_TOT:
            if (gSubMenuSelection == 0) {
                strcpy(String, "OFF");
            } else {
                sprintf(String, "%02ldm:%02lds", (((gSubMenuSelection + 1) * 5) / 60), (((gSubMenuSelection + 1) * 5) % 60));
            }
            break;

        case MENU_AUTOLK:
            if (gSubMenuSelection == 0)
                strcpy(String, gSubMenu_OFF_ON[0]);
            else {
                sprintf(String, "%02ldm:%02lds", ((gSubMenuSelection * 15) / 60), ((gSubMenuSelection * 15) % 60));
            }
            break;

        case MENU_FSKSRC:
            sprintf(String, "%ld", gSubMenuSelection);
            break;

        case MENU_COMPAND:
        case MENU_ABR_ON_TX_RX:
            strcpy(String, gSubMenu_RX_TX[gSubMenuSelection]);
            break;

#ifndef ENABLE_FEAT_F4HWN
#ifdef ENABLE_AM_FIX
            case MENU_AM_FIX:
#endif
#endif
        case MENU_BCL:
        case MENU_BEEP:
        case MENU_S_ADD1:
        case MENU_S_ADD2:
        case MENU_S_ADD3:
        case MENU_STE:
        case MENU_D_ST:
        case MENU_D_LIVE_DEC:
#ifdef ENABLE_NOAA
            case MENU_NOAA_S:
#endif
#ifdef ENABLE_FEAT_F4HWN
        case MENU_SET_TMR:
#endif
            strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
            break;

        case MENU_MEM_CH:
        case MENU_1_CALL:
        case MENU_DEL_CH: {
            const bool valid = RADIO_CheckValidChannel(gSubMenuSelection, false, 0);

            UI_GenerateChannelStringEx(String, valid, gSubMenuSelection);
            UI_PrintString(String, menu_item_x1, menu_item_x2, 0 /*, 8 */);

            if (valid && !gAskForConfirmation) {   // show the frequency so that the user knows the channels frequency
                const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
                sprintf(String, "%lu.%05lu", frequency / 100000, frequency % 100000);
                UI_PrintString(String, menu_item_x1, menu_item_x2, 4 /*, 8 */);
            }

            SETTINGS_FetchChannelName(String, gSubMenuSelection);
            UI_PrintString(String[0] ? String : "--", menu_item_x1, menu_item_x2, 2 /*, 8 */);
            already_printed = true;
            break;
        }

        case MENU_MEM_NAME: {
            const bool valid = RADIO_CheckValidChannel(gSubMenuSelection, false, 0);

            UI_GenerateChannelStringEx(String, valid, gSubMenuSelection);
            UI_PrintString(String, menu_item_x1, menu_item_x2, 0 /*, 8 */);

            if (valid) {
                const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);

                //if (!gIsInSubMenu || edit_index < 0)
                if (!gIsInSubMenu)
                    edit_index = -1;
                if (edit_index < 0) {   // show the channel name
                    SETTINGS_FetchChannelName(String, gSubMenuSelection);
                    char *pPrintStr = String[0] ? String : "--";
                    UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 2 /*, 8 */);
                } else {   // show the channel name being edited
                    //UI_PrintString(edit, menu_item_x1, 0, 2 /*, 8 */);
                    UI_PrintString(edit, menu_item_x1, menu_item_x2, 2 /*, 8 */);
                    if (edit_index < 10)
                        //UI_PrintString("^", menu_item_x1 + (8 * edit_index), 0, 4 /*, 8 */);  // show the cursor
                        UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index), 0, 4 /*, 8 */); // show the cursor
                }

                if (!gAskForConfirmation) {   // show the frequency so that the user knows the channels frequency
                    sprintf(String, "%lu.%05lu", frequency / 100000, frequency % 100000);
                    UI_PrintString(String, menu_item_x1, menu_item_x2, 4 + (gIsInSubMenu && edit_index >= 0) /*, 8 */);
                }
            }

            already_printed = true;
            break;
        }

        case MENU_SAVE:
            sprintf(String, gSubMenuSelection == 0 ? gSubMenu_OFF_ON[0] : "1:%ld", gSubMenuSelection);
            break;

        case MENU_TDR:
            strcpy(String, gSubMenu_RXMode[gSubMenuSelection]);
            break;

#ifdef ENABLE_VOICE
            case MENU_VOICE:
                strcpy(String, gSubMenu_VOICE[gSubMenuSelection]);
                break;
#endif

        case MENU_SC_REV:
            if (gSubMenuSelection == 0) {
                strcpy(String, "STOP");
            } else if (gSubMenuSelection < 81) {
                sprintf(String, "CARRIER\n%02lds:%03ldms", ((gSubMenuSelection * 250) / 1000),
                        ((gSubMenuSelection * 250) % 1000));
#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
                //ST7565_Gauge(5, 1, 80, gSubMenuSelection);
#endif
            } else {
                sprintf(String, "TIMEOUT\n%02ldm:%02lds", (((gSubMenuSelection - 80) * 5) / 60),
                        (((gSubMenuSelection - 80) * 5) % 60));
#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
                //ST7565_Gauge(5, 80, 104, gSubMenuSelection);
#endif
            }
            break;

        case MENU_MDF:
            strcpy(String, gSubMenu_MDF[gSubMenuSelection]);
            break;

        case MENU_RP_STE:
            sprintf(String, gSubMenuSelection == 0 ? gSubMenu_OFF_ON[0] : "%lu*100ms", gSubMenuSelection);
            break;

        case MENU_S_LIST:
            if (gSubMenuSelection == 0)
                strcpy(String, "LIST [0]\nNO LIST");
            else if (gSubMenuSelection < 4)
                sprintf(String, "LIST [%lu]", gSubMenuSelection);
            else if (gSubMenuSelection == 4)
                strcpy(String, "LISTS\n[1, 2, 3]");
            else if (gSubMenuSelection == 5)
                strcpy(String, "ALL");
            break;

#ifdef ENABLE_ALARM
            case MENU_AL_MOD:
                sprintf(String, gSubMenu_AL_MOD[gSubMenuSelection]);
                break;
#endif

        case MENU_UPCODE:
            sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
            break;

        case MENU_DWCODE:
            sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
            break;

        case MENU_D_PRE:
            sprintf(String, "%ld*10ms", gSubMenuSelection);
            break;

        case MENU_PTT_ID:
            strcpy(String, gSubMenu_PTT_ID[gSubMenuSelection]);
            break;

        case MENU_BAT_TXT:
            strcpy(String, gSubMenu_BAT_TXT[gSubMenuSelection]);
            break;


        case MENU_FSKMOD:
            strcpy(String, gSubMenu_FSKMod[gSubMenuSelection]);
            break;

        case MENU_ROGER:
            strcpy(String, gSubMenu_ROGER[gSubMenuSelection]);
            break;

        case MENU_RESET:
            strcpy(String, gSubMenu_RESET[gSubMenuSelection]);
            break;

        case MENU_F_LOCK:
            strcpy(String, gSubMenu_F_LOCK[gSubMenuSelection]);
            break;

#ifdef ENABLE_F_CAL_MENU
            case MENU_F_CALI: {
                const uint32_t value = 22656 + gSubMenuSelection;
                const uint32_t xtal_Hz = (0x4f0000u + value) * 5;

                writeXtalFreqCal(gSubMenuSelection, false);

                sprintf(String, "%ld\n%lu.%06lu\nMHz",
                        gSubMenuSelection,
                        xtal_Hz / 1000000, xtal_Hz % 1000000);
            }
                break;
#endif

        case MENU_BATCAL: {
            const uint16_t vol = (uint32_t) gBatteryVoltageAverage * gBatteryCalibration[3] / gSubMenuSelection;
            sprintf(String, "%u.%02dV\n%lu", vol / 100, vol % 100, gSubMenuSelection);
            break;
        }

        case MENU_BATTYP:
            strcpy(String, gSubMenu_BATTYP[gSubMenuSelection]);
            break;

        case MENU_F1SHRT:
        case MENU_F1LONG:
        case MENU_F2SHRT:
        case MENU_F2LONG:
        case MENU_MLONG:
            strcpy(String, gSubMenu_SIDEFUNCTIONS[gSubMenuSelection].name);
            break;

#ifdef ENABLE_FEAT_F4HWN_SLEEP
        case MENU_SET_OFF:
            if (gSubMenuSelection == 0) {
                strcpy(String, gSubMenu_OFF_ON[0]);
            } else if (gSubMenuSelection < 121) {
                sprintf(String, "%ldh:%02ldm", (gSubMenuSelection / 60), (gSubMenuSelection % 60));
            }
            break;
#endif

#ifdef ENABLE_FEAT_F4HWN
        case MENU_SET_PTT:
            strcpy(String, gSubMenu_SET_PTT[gSubMenuSelection]);
            break;

        case MENU_SET_INV:
#ifdef ENABLE_FEAT_F4HWN_INV
            strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
            ST7565_ContrastAndInv();
#else
            strcpy(String, gSubMenu_NA);
#endif
            break;

#ifdef ENABLE_FEAT_F4HWN_VOL
            case MENU_SET_VOL:
                if (gSubMenuSelection == 0) {
                    strcpy(String, gSubMenu_OFF_ON[0]);
                } else if (gSubMenuSelection < 64) {
                    sprintf(String, "%02lu", gSubMenuSelection);
#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
#endif
                }
                gEeprom.VOLUME_GAIN = gSubMenuSelection;
                BK4819_WriteRegister(BK4819_REG_48,
                                     (11u << 12) |     // ??? .. 0 ~ 15, doesn't seem to make any difference
                                     (0u << 10) |     // AF Rx Gain-1
                                     (gEeprom.VOLUME_GAIN << 4) |     // AF Rx Gain-2
                                     (gEeprom.DAC_GAIN << 0));     // AF DAC Gain (after Gain-1 and Gain-2)
                break;
#endif
#endif

    }

//#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
//    if (gaugeLine != 0) {
//        ST7565_Gauge(gaugeLine, gaugeMin, gaugeMax, gSubMenuSelection);
//    }
//#endif

    if (!already_printed) {   // we now do multi-line text in a single string

        unsigned int y;
        unsigned int lines = 1;
        unsigned int len = strlen(String);
        bool small = false;

        if (len > 0) {
            // count number of lines
            for (i = 0; i < len; i++) {
                if (String[i] == '\n' && i < (len - 1)) {   // found new line char
                    lines++;
                    String[i] = 0;  // null terminate the line
                }
            }

            if (lines > 3) {   // use small text
                small = true;
                if (lines > 7)
                    lines = 7;
            }

            // center vertically'ish
            /*
            if (small)
                y = 3 - ((lines + 0) / 2);  // untested
            else
                y = 2 - ((lines + 0) / 2);
            */

            y = (small ? 3 : 2) - (lines / 2);

            // draw the text lines
            for (i = 0; i < len && lines > 0; lines--) {
                if (small)
                    UI_PrintStringSmallNormal(String + i, menu_item_x1, menu_item_x2, y);
                else
                    UI_PrintString(String + i, menu_item_x1, menu_item_x2, y /*, 8 */);

                // look for start of next line
                while (i < len && String[i] >= 32)
                    i++;

                // hop over the null term char(s)
                while (i < len && String[i] < 32)
                    i++;

                y += small ? 1 : 2;
            }
        }
    }

    if (UI_MENU_GetCurrentMenuId() == MENU_SLIST1 || UI_MENU_GetCurrentMenuId() == MENU_SLIST2 ||
        UI_MENU_GetCurrentMenuId() == MENU_SLIST3) {
        i = UI_MENU_GetCurrentMenuId() - MENU_SLIST1;

        char *pPrintStr = String;

        if (gSubMenuSelection < 0) {
            pPrintStr = "NULL";
        } else {
            UI_GenerateChannelStringEx(String, true, gSubMenuSelection);
            pPrintStr = String;
        }

        // channel number
        UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 0 /*, 8 */);

        SETTINGS_FetchChannelName(String, gSubMenuSelection);
        pPrintStr = String[0] ? String : "--";

        // channel name and scan-list
        if (gSubMenuSelection < 0 || !gEeprom.SCAN_LIST_ENABLED[i]) {
            UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 2 /*, 8 */);
        } else {
            /*
            UI_PrintStringSmallNormal(pPrintStr, menu_item_x1, menu_item_x2, 2);

            if (IS_MR_CHANNEL(gEeprom.SCANLIST_PRIORITY_CH1[i])) {
                sprintf(String, "PRI%ld:%u", 1, gEeprom.SCANLIST_PRIORITY_CH1[i] + 1);
                UI_PrintString(String, menu_item_x1, menu_item_x2, 3 , 8);
            }

            if (IS_MR_CHANNEL(gEeprom.SCANLIST_PRIORITY_CH2[i])) {
                sprintf(String, "PRI%ld:%u", 2, gEeprom.SCANLIST_PRIORITY_CH2[i] + 1);
                UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
            }
            */

            UI_PrintStringSmallNormal(pPrintStr, menu_item_x1, menu_item_x2, 2);

            for (uint8_t pri = 1; pri <= 2; pri++) {
                uint8_t channel = (pri == 1) ? gEeprom.SCANLIST_PRIORITY_CH1[i] : gEeprom.SCANLIST_PRIORITY_CH2[i];

                if (IS_MR_CHANNEL(channel)) {
                    sprintf(String, "PRI%d:%u", pri, channel + 1);
                    UI_PrintString(String, menu_item_x1, menu_item_x2, pri * 2 + 1 /*, 8 */);
                }
            }

        }
    }

    if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gCssBackgroundScan)
        UI_PrintString("SCAN", menu_item_x1, menu_item_x2, 4 /*, 8 */);


    if (UI_MENU_GetCurrentMenuId() == MENU_R_CTCS ||
        UI_MENU_GetCurrentMenuId() == MENU_T_CTCS ||
        UI_MENU_GetCurrentMenuId() == MENU_R_DCS ||
        UI_MENU_GetCurrentMenuId() == MENU_T_DCS
            ) {
        sprintf(String, "%2ld", gSubMenuSelection);
        UI_PrintStringSmallNormal(String, 105, 0, 0);
    }

    if ((UI_MENU_GetCurrentMenuId() == MENU_RESET ||
         UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
         UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME ||
         UI_MENU_GetCurrentMenuId() == MENU_DEL_CH) && gAskForConfirmation) {   // display confirmation
        char *pPrintStr = (gAskForConfirmation == 1) ? "SURE?" : "WAIT!";
        UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 5 /*, 8 */);
    }

    ST7565_BlitFullScreen();
}