/*
This source code started with Farhan's original source. The license rules are followed as well.
Calibration related functions kept the original source except for the minor ones. 
The part is collected in the last minute of this source.
Ian KD8CEC
 */
 
#include "ubitx.h"
#include "ubitx_eemap.h"

//Current Frequency and mode to active VFO by KD8CEC
void FrequencyToVFO(byte isSaveFreq)
{
  //Save Frequency & Mode Information
  if (vfoActive == VFO_A)
  {
    vfoA = frequency;
    vfoA_mode = modeToByte();

    if (isSaveFreq)
      storeFrequencyAndMode(1);
  }
  else
  {
    vfoB = frequency;
    vfoB_mode = modeToByte();
    
    if (isSaveFreq)
      storeFrequencyAndMode(2);
  }
}

//Commonly called functions when exiting menus by KD8CEC
void menuClearExit(int delayTime)
{
  if (delayTime > 0)
    delay_background(delayTime, 0);
    
  printLine2ClearAndUpdate();
  menuOn = 0;
}

//Ham band or general band movement by KD8CEC
void menuBand(int btn){
  int knob = 0;
  int stepChangeCount = 0;
  byte btnPressCount = 0;

  if (!btn){
   printLineF2(F("Band Select?"));
   return;
  }

  //printLineF2(F("Press to confirm"));
  //wait for the button menu select button to be lifted)
  while (btnDown()) {
     delay_background(50, 0);
    if (btnPressCount++ > 20) {
      btnPressCount = 0;
      if (tuneTXType > 0) { //Just toggle 0 <-> 2, if tuneTXType is 100, 100 -> 0 -> 2
        tuneTXType = 0;
        printLineF2(F("General"));
      }
      else {
        tuneTXType = 2;
        printLineF2(F("Ham band"));
      }
      delay_background(1000, 0);
      printLine2ClearAndUpdate();
    }
  }
  printLineF2(F("Press to confirm"));
  
  char currentBandIndex = -1;
  //Save Band Information
  if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
    //Get Now Band Index
    currentBandIndex = getIndexHambanBbyFreq(frequency);
    
    if (currentBandIndex >= 0) {
      //Save Frequency to Band Frequncy Record
      saveBandFreqByIndex(frequency, modeToByte(), currentBandIndex);
    }
  }
  
  //delay(50);    
  ritDisable();

  while(!btnDown()){
    knob = enc_read();
    if (knob != 0){
      if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
        if (knob < 0) {
          if (stepChangeCount-- < -3) {
            setNextHamBandFreq(frequency, -1);  //Prior Band
            stepChangeCount = 0;
          }
        }
        else if (knob > 0) {
          if (stepChangeCount++ > 3) {
            setNextHamBandFreq(frequency, 1); //Next Band
            stepChangeCount = 0;
          }
        }
      }       //end of only ham band move
      else {  //original source
        if (knob < 0 && frequency > 3000000l)
          setFrequency(frequency - 200000l);
        if (knob > 0 && frequency < 30000000l)
          setFrequency(frequency + 200000l);

        if (frequency > 10000000l)
          isUSB = true;
        else
          isUSB = false;
      }

      updateDisplay();
    }
    
    delay_background(20, 0);
  }

  FrequencyToVFO(1);
  menuClearExit(500);
}

//Convert Mode, Number by KD8CEC
//0: default, 1:not use, 2:LSB, 3:USB, 4:CWL, 5:CWU, 6:FM
byte modeToByte(){
  if (cwMode == 0)
  {
    if (isUSB)
      return 3;
    else
      return 2;
  }
  else if (cwMode == 1)
  {
    return 4;
  }
  else
  {
    return 5;
  }
}

//Convert Number to Mode by KD8CEC
//autoSetModebyFreq : 0
//autoSetModebyFreq : 1, if (modValue is not set, set mode by frequency)
void byteToMode(byte modeValue, byte autoSetModebyFreq){
  if (modeValue == 4)
    cwMode = 1;
  else if (modeValue == 5)
    cwMode = 2;
  else
  {
    cwMode = 0;
    if (modeValue == 3)
      isUSB = 1;
    else if (autoSetModebyFreq == 1 && (modeValue == 0))
      isUSB = (frequency > 10000000l) ? true : false;
    else
      isUSB = 0;
  }
}


//Memory to VFO, VFO to Memory by KD8CEC
void menuCHMemory(int btn, byte isMemoryToVfo){
  int knob = 0;
  int selectChannel = 0;
  byte isDisplayInfo = 1;
  int moveStep = 0;
  unsigned long resultFreq, tmpFreq = 0;
  byte loadMode = 0;
  
  if (!btn){
    if (isMemoryToVfo == 1)
      printLineF2(F("Channel To VFO?"));
   else 
      printLineF2(F("VFO To Channel?"));
  }
  else {
    delay_background(500, 0);

    while(!btnDown()){
      if (isDisplayInfo == 1) {
        //Display Channel info *********************************
        memset(c, 0, sizeof(c));

        if (selectChannel >= 20 || selectChannel <=-1)
        {
          //strcpy(c, "Exit setup?");
          strcpy(c, "Exit?");
        }
        else
        {
          //Read Frequency from eeprom
          EEPROM.get(CHANNEL_FREQ + 4 * selectChannel, resultFreq);
          
          loadMode = (byte)(resultFreq >> 29);
          resultFreq = resultFreq & 0x1FFFFFFF;

          //display channel description
          if (selectChannel < 10 && EEPROM.read(CHANNEL_DESC + 6 * selectChannel) == 0x03) {  //0x03 is display Chnnel Name
            //display Channel Name
            for (int i = 0; i < 5; i++)
              c[i] = EEPROM.read(CHANNEL_DESC + 6 * selectChannel + i + 1);

           c[5] = ':';
          }
          else {
            //Display frequency
            //1 LINE : Channel Information : CH00
            strcpy(c, "CH");
            if (selectChannel < 9)
              c[2] = '0';
            
            ltoa(selectChannel + 1, b, 10);
            strcat(c, b); //append channel Number;
            strcat(c, " :"); //append channel Number;
          }
  
          //display frequency
          tmpFreq = resultFreq;
          for (int i = 15; i >= 6; i--) {
            if (tmpFreq > 0) {
              if (i == 12 || i == 8) c[i] = '.';
              else {
                c[i] = tmpFreq % 10 + 0x30;
                tmpFreq /= 10;
              }
            }
            else
              c[i] = ' ';
          }
        }

        printLine2(c);
        
        isDisplayInfo = 0;
      }

      knob = enc_read();

      if (knob != 0)
      {
        moveStep += (knob > 0 ? 1 : -1);
        if (moveStep < -3) {
          if (selectChannel > -1)
            selectChannel--;

          isDisplayInfo = 1;
          moveStep = 0;
        }
        else if (moveStep > 3) {
          if (selectChannel < 20)
            selectChannel++;
            
          isDisplayInfo = 1;
          moveStep = 0;
        }
      }

      Check_Cat(0);  //To prevent disconnections
    } //end of while (knob)

    if (selectChannel < 20 && selectChannel >= 0)
    {
      if (isMemoryToVfo == 1)
      {
        if (resultFreq > 3000 && resultFreq < 60000000)
        {
          byteToMode(loadMode, 1);
          setFrequency(resultFreq);
        }
      }
      else
      {
        //Save current Frequency to Channel (selectChannel)
        EEPROM.put(CHANNEL_FREQ + 4 * selectChannel, (frequency & 0x1FFFFFFF) | (((unsigned long)modeToByte()) << 29) );
        printLine2("Saved Frequency");
      }
    }
    
    menuClearExit(500);
  }
}

//Analog pin monitoring with CW Key and function keys connected.
//by KD8CEC
#ifdef ENABLE_ADCMONITOR        
void menuADCMonitor(int btn){
  int adcPinA0 = 0;  //A0(BLACK, EncoderA)
  int adcPinA1 = 0;  //A1(BROWN, EncoderB)
  int adcPinA2 = 0;  //A2(RED, Function Key)
  int adcPinA3 = 0;  //A3(ORANGE, CW Key)
  int adcPinA6 = 0;  //A6(BLUE, Ptt)
  int adcPinA7 = 0;  //A7(VIOLET, Spare)
  unsigned long pressKeyTime = 0;
  
  if (!btn){
        printLineF2(F("ADC Line Monitor"));
        return;
  }
  
  printLineF2(F("Exit:Long PTT"));
  delay_background(2000, 0);
  printLineF1(F("A0   A1   A2"));
  printLineF2(F("A3   A6   A7"));
  delay_background(3000, 0);
  
  while (true) {
    adcPinA0 = analogRead(A0);  //A0(BLACK, EncoderA)
    adcPinA1 = analogRead(A1);  //A1(BROWN, EncoderB)
    adcPinA2 = analogRead(A2);  //A2(RED, Function Key)
    adcPinA3 = analogRead(A3);  //A3(PTT)
    adcPinA6 = analogRead(A6);  //A6(KEYER)
    adcPinA7 = analogRead(A7);  //A7(VIOLET, Spare)

    if (adcPinA3 < 50) {
      if (pressKeyTime == 0)
        pressKeyTime = millis();
      else if (pressKeyTime < (millis() - 3000))
          break;
    }
    else
      pressKeyTime = 0;
    
    ltoa(adcPinA0, c, 10);
    //strcat(b, c);
    strcpy(b, c);
    strcat(b, ", ");
    
    ltoa(adcPinA1, c, 10);
    strcat(b, c);
    strcat(b, ", ");
    
    ltoa(adcPinA2, c, 10);
    strcat(b, c);

    printLine1(b);

    //strcpy(b, " ");
    ltoa(adcPinA3, c, 10);
    strcpy(b, c);
    strcat(b, ", ");

    ltoa(adcPinA6, c, 10);
    strcat(b, c);
    strcat(b, ", ");
    
    ltoa(adcPinA7, c, 10);
    strcat(b, c);
    printLine2(b);
    
    delay_background(200, 0);
  } //end of while
      
  menuClearExit(0);
}
#endif

//VFO Toggle and save VFO Information, modified by KD8CEC
void menuVfoToggle(int btn)
{
  if (!btn){
    if (vfoActive == VFO_A)
      printLineF2(F("Select VFO B?"));
    else
      printLineF2(F("Select VFO A?"));    
  }
  else {
      FrequencyToVFO(1);
    
      if (vfoActive == VFO_B){
        vfoActive = VFO_A;
        frequency = vfoA;
        saveCheckFreq = frequency;
        byteToMode(vfoA_mode, 0);
      }
      else {
        vfoActive = VFO_B;
        frequency = vfoB;
        saveCheckFreq = frequency;
        byteToMode(vfoB_mode, 0);
      }

      ritDisable();
      setFrequency(frequency);

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(0);
#endif    
      
  }
}


//Split communication using VFOA and VFOB by KD8CEC
void menuSplitOnOff(int btn){
  if (!btn){
    if (splitOn == 0)
      printLineF2(F("Split On?"));
    else
      printLineF2(F("Split Off?"));
  }
  else {
      if (splitOn == 1){
        splitOn = 0;
        printLineF2(F("SPT Off"));
      }
      else {
        splitOn = 1;
        if (ritOn == 1)
          ritOn = 0;
        printLineF2(F("SPT On"));
      }

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
//Only Clear And Delay for Character LCD
    menuClearExit(500);
#endif    
  }
}

//Function to disbled transmission
//by KD8CEC
void menuTxOnOff(int btn, byte optionType){
  if (!btn){
    if ((isTxType & optionType) == 0)
      printLineF2(F("TX OFF?"));
    else
      printLineF2(F("TX ON?"));
  }
  else {
      if ((isTxType & optionType) == 0){
        isTxType |= optionType;
        printLineF2(F("TX OFF"));
      }
      else {
        isTxType &= ~(optionType);
        printLineF2(F("TX ON"));
      }

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(500);
#endif
  }
}

//Toggle SDR MODE
void menuSDROnOff(int btn)
{
  if (!btn){
    if (sdrModeOn == 0)
      printLineF2(F("SDR Mode On?"));
    else
      printLineF2(F("SDR Mode Off?"));
  }
  else {
      if (sdrModeOn == 1){
        sdrModeOn = 0;
        printLineF2(F("SPK MODE"));
      }
      else {
        sdrModeOn = 1;
        
        if (ritOn == 1)
          ritOn = 0;

        if (splitOn == 1)
          splitOn = 0;
          
        printLineF2(F("SDR MODE"));
      }

    EEPROM.put(ENABLE_SDR, sdrModeOn);
    setFrequency(frequency);
    SetCarrierFreq();

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(500);
#endif
  }
}

void displayEmptyData(void){
  printLineF2(F("Empty data"));
  delay_background(2000, 0);
}

//Builtin CW Keyer Logic by KD8CEC
void menuCWAutoKey(int btn){
    if (!btn){
      printLineF2(F("Memory Keyer"));
      return;
    }
    
    //Check CW_AUTO_MAGIC_KEY and CW Text Count
    EEPROM.get(CW_AUTO_COUNT, cwAutoTextCount);
    if (EEPROM.read(CW_AUTO_MAGIC_KEY) != 0x73 || cwAutoTextCount < 1)
    {
      displayEmptyData();
     return;
    }

    printLineF1(F("PTT to Send"));
    delay_background(500, 0);
    beforeCWTextIndex = 255;  //255 value is for start check
    isCWAutoMode = 1;
    updateDisplay();
    menuOn = 0;
}

//Standalone WSPR Beacone
void menuWSPRSend(int btn){
  if (!btn){
     printLineF2(F("WSPR Beacon"));
     return;
  }

  WsprMSGCount = EEPROM.read(WSPR_COUNT);

  if (WsprMSGCount < 1)
  {
    displayEmptyData();
    return;
  }

  SendWSPRManage();
  menuClearExit(1000);
}

//Append by KD8CEC
void menuSetupCWCarrier(int btn){
  int knob = 0;
  unsigned long prevCarrier;
   
  if (!btn){
      printLineF2(F("Set CW RX BFO"));
    return;
  }

  prevCarrier = cwmCarrier;
  printLineF1(F("PTT to confirm. "));
  delay_background(1000, 0);

  si5351bx_setfreq(0, cwmCarrier);
  printCarrierFreq(cwmCarrier);

  //disable all clock 1 and clock 2 
  while (digitalRead(PTT) == HIGH && !btnDown())
  {
    knob = enc_read();

    if (knob > 0)
      cwmCarrier -= 5;
    else if (knob < 0)
      cwmCarrier += 5;
    else
      continue; //don't update the frequency or the display
      
    si5351bx_setfreq(0, cwmCarrier);
    printCarrierFreq(cwmCarrier);

    delay_background(100, 0);
  }

  //save the setting
  if (digitalRead(PTT) == LOW){
    printLineF2(F("Carrier set!"));
    EEPROM.put(CW_CAL, cwmCarrier);
    delay_background(1000, 0);
  }
  else 
    cwmCarrier = prevCarrier;

  if (cwMode == 0)
    si5351bx_setfreq(0, usbCarrier);  //set back the carrier oscillator anyway, cw tx switches it off
  else
    si5351bx_setfreq(0, cwmCarrier);  //set back the carrier oscillator anyway, cw tx switches it off
  
  setFrequency(frequency);
  menuClearExit(0);
}

//=======================================================
//BEGIN OF STANDARD TUNE SETUP for reduce Program Memory
// by KD8CEC
//=======================================================
//valueType 0 : Normal
//          1 : CW Change -> Generate Tone
//          2 : IF Shift Setup -> SetFrequency, Set SideTone
//          5 : ATT
//          11 : Select Mode (different display type)
//knobSensitivity : 1 ~ 
int getValueByKnob(int valueType, int targetValue, int minKnobValue, int maxKnobValue, int incStep, const char* displayTitle, int knobSensitivity)
{
    int knob;
    int moveDetectStep = 0;
    char isInitDisplay = 1;
    delay_background(300, 0); //Default Delay

    if (valueType < 10)
    {
      strcpy(b, "Press, set ");
      strcat(b, displayTitle);
      printLine1(b);
    }
    
    while(!btnDown())
    {
      knob = enc_read();
      if (knob != 0 || isInitDisplay == 1)
      {
        isInitDisplay = 0;
        
        /*
        //Program Size : 29424 (95%)
        if (targetValue > minKnobValue && knob < 0)
          targetValue -= incStep;
        if (targetValue < maxKnobValue && knob > 0)
          targetValue += incStep;
        */

        //Program Size : 29560 (increase 135 byte from avobe codes), but a lot of usable functions
        moveDetectStep += (knob > 0 ? 1 : -1);
        if (moveDetectStep < (knobSensitivity * -1)) {
          if (targetValue > minKnobValue)
            targetValue -= incStep;
            
          moveDetectStep = 0;
        }
        else if (moveDetectStep > knobSensitivity) {
          if (targetValue < maxKnobValue)
            targetValue += incStep;
            
          moveDetectStep = 0;
        }

        strcpy(b, displayTitle);

        if (valueType == 11)   //Mode Select
        {
          b[targetValue * 4] = '>';
        }
        /*
        else if (valueType == 4) //CW Key Type  Select
        {
          if (targetValue == 0)
            strcat(b, "Straight");
          else if (targetValue == 1)
            strcat(b, "IAMBICA");
          else if (targetValue == 2)
            strcat(b, "IAMBICB");
        }
        */
        else
        {
          strcat(b, ":");
          itoa(targetValue,c, 10);
          strcat(b, c);
        }
        
        printLine2(b);

        if (valueType == 1) //Generate Side Tone
        {
          tone(CW_TONE, targetValue);          
        }
        else if (valueType == 2 || valueType == 5 ) // 2:IFS, 5:ATT
        {
          if (valueType == 2)
            ifShiftValue = targetValue;
          else 
            attLevel = targetValue;

#ifdef USE_SW_SERIAL
  menuOn=2;
  updateDisplay();
#endif
          setFrequency(frequency);
          SetCarrierFreq();
        }
      }

      Check_Cat(0);  //To prevent disconnections
    }

    return targetValue;
}

void menuCWSpeed(int btn){
    int wpm;

    wpm = 1200/cwSpeed;
     
    if (!btn){
     strcpy(b, "CW:");
     itoa(wpm,c, 10);
     strcat(b, c);
     strcat(b, "WPM Change?");
     printLine2(b);
     return;
    }

    //printLineF1(F("Press to set WPM"));
    //strcpy(b, "WPM:");
    //itoa(wpm,c, 10);
    //strcat(b, c);
    //printLine2(b);
    //delay_background(300, 0);
    
    wpm = getValueByKnob(0, wpm, 3, 50, 1, "WPM", 3);
    
    /*
    while(!btnDown()){

      knob = enc_read();
      if (knob != 0){
        if (wpm > 3 && knob < 0)
          wpm--;
        if (wpm < 50 && knob > 0)
          wpm++;

        strcpy(b, "WPM:");
        itoa(wpm,c, 10);
        strcat(b, c);
        printLine2(b);
      }

      Check_Cat(0);  //To prevent disconnections
    }
    */

    
  //save the setting
  //printLineF2(F("CW Speed set!"));
  cwSpeed = 1200 / wpm;
  EEPROM.put(CW_SPEED, cwSpeed);
  //menuClearExit(1000);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(1000);
#endif
  
}

//Modified by KD8CEC
void menuSetupCwTone(int btn){
    //int prev_sideTone;
     
    if (!btn){
      printLineF2(F("Change CW Tone"));
      return;
    }

    //prev_sideTone = sideTone;
    //printLineF1(F("Tune CW tone"));
    //printLineF2(F("PTT to confirm."));
    //printLineF1(F("Press to set WPM"));
    //delay_background(1000, 0);
    //tone(CW_TONE, sideTone);

    sideTone = getValueByKnob(1, sideTone, 100, 2000, 10, "Tone", 2); //1 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize

    noTone(CW_TONE);
    
    printLineF2(F("Sidetone set!"));
    EEPROM.put(CW_SIDETONE, sideTone);

    //delay_background(2000, 0);
    //menuClearExit(0);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    delay_background(2000, 0);
    menuClearExit(0);
#endif
 }

//Modified by KD8CEC
void menuSetupCwDelay(int btn){
    //int knob = 0;
    int tmpCWDelay = cwDelayTime * 10;
     
    if (!btn){
      printLineF2(F("CW TX->RX Delay"));
     return;
    }

    tmpCWDelay = getValueByKnob(0, tmpCWDelay, 3, 2500, 10, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize

    //save the setting
    cwDelayTime = tmpCWDelay / 10;
    EEPROM.put(CW_DELAY, cwDelayTime);
   //menuClearExit(1000);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(1000);
#endif
}

//CW Time delay by KD8CEC
void menuSetupTXCWInterval(int btn){
    //char needDisplayInformation = 1;
    //int knob = 0;
    int tmpTXCWInterval = delayBeforeCWStartTime * 2;
     
    if (!btn){
      printLineF2(F("CW Start Delay"));
     return;
    }

    //printLineF1(F("Press, set Delay"));
    //delay_background(300, 0);

    tmpTXCWInterval = getValueByKnob(0, tmpTXCWInterval, 0, 500, 2, "Delay", 2); //0 : Generate Tone, targetValue, minKnobValue, maxKnobValue, stepSize

   delayBeforeCWStartTime = tmpTXCWInterval / 2;
   EEPROM.put(CW_START, delayBeforeCWStartTime);
   //menuClearExit(1000);

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(1000);
#endif
   
}

//IF Shift function, BFO Change like RIT, by KD8CEC
void menuIFSSetup(int btn){
  //int knob = 0;
  //char needApplyChangeValue = 1;
  
  if (!btn){
    if (isIFShift == 1)
      printLineF2(F("IF Shift Change?"));
    else
      printLineF2(F("IF Shift On?"));
  }
  else 
  {
      isIFShift = 1;

      ifShiftValue = getValueByKnob(2, ifShiftValue, -20000, 20000, 50, "IFS", 2); //2 : IF Setup (updateLine2Buffer(1), SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize
      delay_background(500, 0); //for check Long Press function key
      
      if (btnDown() || ifShiftValue == 0)
      {
        isIFShift = 0;
        ifShiftValue = 0;
        setFrequency(frequency);
        SetCarrierFreq();
      }

      //Store IF Shiift
      EEPROM.put(IF_SHIFTVALUE, ifShiftValue);
      //menuClearExit(0);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(0);
#endif
  }
}

//ATT SETUP (IF1(45MHZ) SHIFT), by KD8CEC
void menuATTSetup(int btn){
  //int knob = 0;
  //char needApplyChangeValue = 1;
  
  if (!btn){
    if (attLevel != 0)
      printLineF2(F("ATT Change?"));
    else
      printLineF2(F("ATT On?"));
  }
  else 
  {
      attLevel = getValueByKnob(5, attLevel, 0, 250, 5, "ATT", 2); //2 : (SetFrequency), targetValue, minKnobValue, maxKnobValue, stepSize
      delay_background(500, 0); //for check Long Press function key
      
      if (btnDown() || attLevel == 0)
      {
        attLevel = 0;
        setFrequency(frequency);
        //SetCarrierFreq();
      }
      //menuClearExit(0);

#ifdef USE_SW_SERIAL
      menuOn = 0;
#else
      //Only Clear And Delay for Character LCD
      menuClearExit(0);
#endif
      
  }
}

//Functions for CWL and CWU by KD8CEC
void menuSelectMode(int btn){
  //int knob = 0;
  int selectModeType = 0;
  int beforeMode = 0;
  //int moveStep = 0;
  
  if (!btn){
      printLineF2(F("Select Mode?"));
  }
  else 
  {
    //LSB, USB, CWL, CWU
    if (cwMode == 0 && isUSB == 0)
      selectModeType = 0;
    else if (cwMode == 0 && isUSB == 1)
      selectModeType = 1;
    else if (cwMode == 1)
      selectModeType = 2;
    else
      selectModeType = 3;

    beforeMode = selectModeType;
    selectModeType = getValueByKnob(11, selectModeType, 0, 3, 1, " LSB USB CWL CWU", 4); //3 : Select Mode, targetValue, minKnobValue, maxKnobValue, stepSize

    if (beforeMode != selectModeType) 
    {
      //printLineF1(F("Changed Mode"));
      if (selectModeType == 0) {
        cwMode = 0; isUSB = 0;
      }
      else if (selectModeType == 1) {
        cwMode = 0; isUSB = 1;
      }
      else if (selectModeType == 2) {
        cwMode = 1;
      }
      else if (selectModeType == 3) {
        cwMode = 2;
      }

      FrequencyToVFO(1);
    }

    SetCarrierFreq();
    setFrequency(frequency);
    //menuClearExit(500);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(500);
#endif
  }
}

//Select CW Key Type by KD8CEC
void menuSetupKeyType(int btn){
  //int knob = 0;
  int selectedKeyType = 0;
  //int moveStep = 0;
  if (!btn){
        printLineF2(F("Change Key Type?"));
  }
  else {
    selectedKeyType = cwKeyType;

    //selectedKeyType = getValueByKnob(12, selectedKeyType, 0, 2, 1, " KEY:", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize
    selectedKeyType = getValueByKnob(11, selectedKeyType, 0, 2, 1, " ST  IA  IB", 5); //4 : Select Key Type, targetValue, minKnobValue, maxKnobValue, stepSize

    printLineF2(F("CW Key Type set!"));
    cwKeyType = selectedKeyType;
    EEPROM.put(CW_KEY_TYPE, cwKeyType);

    if (cwKeyType == 0)
      Iambic_Key = false;
    else
    {
      Iambic_Key = true;
      if (cwKeyType == 1)
        keyerControl &= ~IAMBICB;
      else
        keyerControl |= IAMBICB;
    }
    
    //menuClearExit(1000);
#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(1000);
#endif
    
  }
}

//=====================================================
//END OF STANDARD Set by Knob for reduce Program Memory
//=====================================================
 

//Lock Dial move by KD8CEC
void setDialLock(byte tmpLock, byte fromMode) {
  if (tmpLock == 1)
    isDialLock |= (vfoActive == VFO_A ? 0x01 : 0x02);
  else
    isDialLock &= ~(vfoActive == VFO_A ? 0x01 : 0x02);
    
  if (fromMode == 2 || fromMode == 3) return;

  //delay_background(1000, 0);
  printLine2ClearAndUpdate();
}

byte btnDownTimeCount;

#define PRESS_ADJUST_TUNE 20 //1000msec 20 * 50 = 1000milisec
#define PRESS_LOCK_CONTROL 40 //2000msec 40 * 50 = 2000milisec

//Modified by KD8CEC
void doMenu(){
  int select=0, i,btnState;
  char isNeedDisplay = 0;
  
  //for DialLock On/Off function
  btnDownTimeCount = 0;
  
  //wait for the button to be raised up

  //Appened Lines by KD8CEC for Adjust Tune step and Set Dial lock
  while(btnDown()){
    delay_background(50, 0);
    
    if (btnDownTimeCount++ == (PRESS_ADJUST_TUNE)) { //Set Tune Step 
      printLineF2(F("Set Tune Step?"));
    }
    else if (btnDownTimeCount > (PRESS_LOCK_CONTROL)) {  //check long time Down Button -> 2.5 Second => Lock
      if (vfoActive == VFO_A)
        setDialLock((isDialLock & 0x01) == 0x01 ? 0 : 1, 0); //Reverse Dial lock
      else
        setDialLock((isDialLock & 0x02) == 0x02 ? 0 : 1, 0); //Reverse Dial lock
      return;
    }
  }
  delay(50);  //debounce

  //ADJUST TUNE STEP 
  if (btnDownTimeCount > PRESS_ADJUST_TUNE)
  {
    printLineF1(F("Press to set"));
    isNeedDisplay = 1; //check to need display for display current value
    
    while (!btnDown())
    {
      delay_background(50, 0);

      if (isNeedDisplay) {
        strcpy(b, "Tune Step:");
        itoa(arTuneStep[tuneStepIndex -1], c, 10);
        strcat(b, c);
        printLine2(b);
        isNeedDisplay = 0;
      }
        
      i = enc_read();

      if (i != 0) {
        select += (i > 0 ? 1 : -1);

        if (select * select >= 25) {  //Threshold 5 * 5 = 25
          if (select < 0) {
            if (tuneStepIndex > 1)
              tuneStepIndex--;
          }
          else {
            if (tuneStepIndex < 5)
              tuneStepIndex++;
          }
          select = 0;
          isNeedDisplay = 1;
        }
      }
    } //end of while

    EEPROM.put(TUNING_STEP, tuneStepIndex);
    delay_background(500, 0);
    printLine2ClearAndUpdate();
    return;
  }   //set tune step

  //Below codes are origial code with modified by KD8CEC
  menuOn = 2;
  TriggerBySW = 0;  //Nextion LCD and Other MCU

  //*********************************************************************************
  // New type menu for developer by KD8CEC
  // Selectable menu
  // Version : 1.097 ~
  //*********************************************************************************
#ifndef ENABLE_ADCMONITOR
  #define FN_ADCMONITOR  0
#endif

  #define FN_DEFAULT_MENU     2 //Setup Onff / Exit
  #define FN_DEFAULT_SETUP    1 //Exit

  #define FN_BAND_IDX         (FN_BAND -1)        //0 or -1
  #define FN_VFO_TOGGLE_IDX   (FN_BAND_IDX        + FN_VFO_TOGGLE)
  #define FN_MODE_IDX         (FN_VFO_TOGGLE_IDX  + FN_MODE)
  #define FN_RIT_IDX          (FN_MODE_IDX        + FN_RIT)
  #define FN_IFSHIFT_IDX      (FN_RIT_IDX         + FN_IFSHIFT)
  #define FN_ATT_IDX          (FN_IFSHIFT_IDX     + FN_ATT)
  #define FN_CW_SPEED_IDX     (FN_ATT_IDX         + FN_CW_SPEED)
  #define FN_SPLIT_IDX        (FN_CW_SPEED_IDX    + FN_SPLIT)
  #define FN_VFOTOMEM_IDX     (FN_SPLIT_IDX       + FN_VFOTOMEM)
  #define FN_MEMTOVFO_IDX     (FN_VFOTOMEM_IDX    + FN_MEMTOVFO)
  #define FN_MEMORYKEYER_IDX  (FN_MEMTOVFO_IDX    + FN_MEMORYKEYER)
  #define FN_WSPR_IDX         (FN_MEMORYKEYER_IDX + FN_WSPR)
  #define FN_SDRMODE_IDX      (FN_WSPR_IDX        + FN_SDRMODE)
  #define FN_SETUP_IDX        (FN_SDRMODE_IDX     + 1)
  #define FN_EXIT_IDX         (FN_SETUP_IDX       + 1)
  #define FN_CALIBRATION_IDX  (FN_EXIT_IDX        + FN_CALIBRATION)
  #define FN_CARRIER_IDX      (FN_CALIBRATION_IDX + FN_CARRIER)
  #define FN_CWCARRIER_IDX    (FN_CARRIER_IDX     + FN_CWCARRIER)
  #define FN_CWTONE_IDX       (FN_CWCARRIER_IDX   + FN_CWTONE)
  #define FN_CWDELAY_IDX      (FN_CWTONE_IDX      + FN_CWDELAY)
  #define FN_TXCWDELAY_IDX    (FN_CWDELAY_IDX     + FN_TXCWDELAY)
  #define FN_KEYTYPE_IDX      (FN_TXCWDELAY_IDX   + FN_KEYTYPE)
  #define FN_ADCMONITOR_IDX   (FN_KEYTYPE_IDX     + FN_ADCMONITOR)
  #define FN_TXONOFF_IDX      (FN_ADCMONITOR_IDX  + FN_TXONOFF)

  #define FN_MENU_COUNT       (FN_DEFAULT_MENU + FN_BAND + FN_VFO_TOGGLE + FN_MODE + FN_RIT + FN_IFSHIFT + FN_ATT + FN_CW_SPEED + FN_SPLIT + FN_VFOTOMEM + FN_MEMTOVFO + FN_MEMORYKEYER + FN_WSPR + FN_SDRMODE)
  #define FN_SETUP_COUNT      (FN_DEFAULT_SETUP + FN_CALIBRATION + FN_CARRIER + FN_CWCARRIER + FN_CWTONE + FN_CWDELAY + FN_TXCWDELAY + FN_KEYTYPE + FN_ADCMONITOR + FN_TXONOFF)
  #define FN_STEP_COUNT       (FN_MENU_COUNT + FN_SETUP_COUNT)
    
  while (menuOn){
    i = enc_read();
    btnState = btnDown();

    if (i > 0){
      if (modeCalibrate && select + i < FN_STEP_COUNT * 10)
        select += i;
      else if (!modeCalibrate && select + i < FN_MENU_COUNT * 10)
        select += i;
    }
    else if (i < 0 && select - i >= -10)
      select += i;
      
    switch (select / 10)
    {
#if FN_BAND == 1
      case FN_BAND_IDX : 
        menuBand(btnState); 
        break;
#endif
#if FN_VFO_TOGGLE == 1
      case FN_VFO_TOGGLE_IDX : 
        menuVfoToggle(btnState); 
        break;
#endif        
#if FN_MODE == 1
      case FN_MODE_IDX : 
        menuSelectMode(btnState); 
        break;
#endif        
#if FN_RIT == 1
      case FN_RIT_IDX : 
        menuRitToggle(btnState); 
        break;
#endif        
#if FN_IFSHIFT == 1
      case FN_IFSHIFT_IDX : 
        menuIFSSetup(btnState); 
        break;
#endif        
#if FN_ATT == 1
      case FN_ATT_IDX : 
        menuATTSetup(btnState); 
        break;
#endif        
#if FN_CW_SPEED == 1
      case FN_CW_SPEED_IDX : 
        menuCWSpeed(btnState); 
        break;
#endif        
#if FN_SPLIT == 1
      case FN_SPLIT_IDX : 
        menuSplitOnOff(btnState);        //SplitOn / off
        break;
#endif        
#if FN_VFOTOMEM == 1
      case FN_VFOTOMEM_IDX : 
        menuCHMemory(btnState, 0);       //VFO to Memroy
        break;
#endif        
#if FN_MEMTOVFO == 1
      case FN_MEMTOVFO_IDX : 
        menuCHMemory(btnState, 1);       //Memory to VFO
        break;
#endif        
#if FN_MEMORYKEYER == 1
      case FN_MEMORYKEYER_IDX : 
        menuCWAutoKey(btnState);  
        break;
#endif        
#if FN_WSPR == 1
      case FN_WSPR_IDX : 
        menuWSPRSend(btnState);
        break;
#endif        
#if FN_SDRMODE == 1
      case FN_SDRMODE_IDX : 
        menuSDROnOff(btnState);
        break;
#endif        
      case FN_SETUP_IDX : 
        menuSetup(btnState);
        break;
      case FN_EXIT_IDX : 
        menuExit(btnState);
        break;
        
#if FN_CALIBRATION == 1
      case FN_CALIBRATION_IDX : 
        menuSetupCalibration(btnState);  //crystal
        break;
#endif        
#if FN_CARRIER == 1
      case FN_CARRIER_IDX : 
        menuSetupCarrier(btnState);      //ssb
        break;
#endif        
#if FN_CWCARRIER == 1
      case FN_CWCARRIER_IDX : 
        menuSetupCWCarrier(btnState);    //cw
        break;
#endif        
#if FN_CWTONE == 1
      case FN_CWTONE_IDX : 
        menuSetupCwTone(btnState);  
        break;
#endif        
#if FN_CWDELAY == 1
      case FN_CWDELAY_IDX : 
        menuSetupCwDelay(btnState);  
        break;
#endif        
#if FN_TXCWDELAY == 1
      case FN_TXCWDELAY_IDX : 
        menuSetupTXCWInterval(btnState);  
        break;
#endif        
#if FN_KEYTYPE == 1
      case FN_KEYTYPE_IDX :
        menuSetupKeyType(btnState);  
        break;
#endif        
#if FN_ADCMONITOR == 1
      case FN_ADCMONITOR_IDX :
        menuADCMonitor(btnState);  
        break;
#endif        
#if FN_TXONOFF == 1
      case FN_TXONOFF_IDX :
        menuTxOnOff(btnState, 0x01);       //TX OFF / ON
        break;
#endif        
      default :
        menuExit(btnState);  break;
    } //end of switch
    Check_Cat(0);  //To prevent disconnections
  }   //end of while

  //****************************************************************************
  //Before change menu type (Version : ~ 0.95)
  //****************************************************************************
  /*
  while (menuOn){
    i = enc_read();
    btnState = btnDown();

    if (i > 0){
      if (modeCalibrate && select + i < 250)
        select += i;
      else if (!modeCalibrate && select + i < 150)
        select += i;
    }
    else if (i < 0 && select - i >= -10)
      select += i;

    //if -> switch : reduce program memory 200byte
    switch (select / 10)
    {
      case 0 : 
        menuBand(btnState); 
        break;
      case 1 : 
        menuVfoToggle(btnState); 
        break;
      case 2 : 
        menuSelectMode(btnState); 
        break;
      case 3 : 
        menuRitToggle(btnState); 
        break;
      case 4 : 
        menuIFSSetup(btnState); 
        break;
      case 5 : 
        menuATTSetup(btnState); 
        break;
      case 6 : 
        menuCWSpeed(btnState); 
        break;
      case 7 : 
        menuSplitOnOff(btnState);        //SplitOn / off
        break;
      case 8 : 
        menuCHMemory(btnState, 0);       //VFO to Memroy
        break;
      case 9 : 
        menuCHMemory(btnState, 1);       //Memory to VFO
        break;
      case 10 : 
        menuCWAutoKey(btnState);  
        break;
      case 11 : 
        menuWSPRSend(btnState);
        break;
      case 12 : 
        menuSDROnOff(btnState);
        break;
      case 13 : 
        menuSetup(btnState);
        break;
      case 14 : 
        menuExit(btnState);
        break;
      case 15 : 
        menuSetupCalibration(btnState);  //crystal
        break;
      case 16 : 
        menuSetupCarrier(btnState);      //ssb
        break;
      case 17 : 
        menuSetupCWCarrier(btnState);    //cw
        break;
      case 18 : 
        menuSetupCwTone(btnState);  
        break;
      case 19 : 
        menuSetupCwDelay(btnState);  
        break;
      case 20 : 
        menuSetupTXCWInterval(btnState);  
        break;
      case 21 :
        menuSetupKeyType(btnState);  
        break;
#ifdef ENABLE_ADCMONITOR        
      case 22 :
        menuADCMonitor(btnState);  
        break;
      case 23 :
#else      
      case 22 :
#endif      
        menuTxOnOff(btnState, 0x01);       //TX OFF / ON
        break;
      default :
        menuExit(btnState);  break;
    } //end of case    
    Check_Cat(0);  //To prevent disconnections
  } //end of while
  */
}

//*************************************************************************************
//Original Source Part
//The code below is the original source part that I kept unchanged for compatibility.
//By KD8CEC
//*************************************************************************************

/** 
 Original source comment 
 *  Menus
 *  The Radio menus are accessed by tapping on the function button. 
 *  - The main loop() constantly looks for a button press and calls doMenu() when it detects
 *  a function button press. 
 *  - As the encoder is rotated, at every 10th pulse, the next or the previous menu
 *  item is displayed. Each menu item is controlled by it's own function.
 *  - Eache menu function may be called to display itself
 *  - Each of these menu routines is called with a button parameter. 
 *  - The btn flag denotes if the menu itme was clicked on or not.
 *  - If the menu item is clicked on, then it is selected,
 *  - If the menu item is NOT clicked on, then the menu's prompt is to be displayed
 */

/**
 * The calibration routines are not normally shown in the menu as they are rarely used
 * They can be enabled by choosing this menu option
 */
void menuSetup(int btn){
  if (!btn)
  {
    if (!modeCalibrate)
      printLineF2(F("Setup On?"));
    else
      printLineF2(F("Setup Off?"));
  }
  else 
  {
    modeCalibrate = ! modeCalibrate;
    //menuClearExit(1000);

#ifdef USE_SW_SERIAL
    menuOn = 0;
#else
    //Only Clear And Delay for Character LCD
    menuClearExit(1000);
#endif
  }
}

void menuExit(int btn){
  if (!btn){
      printLineF2(F("Exit Menu?"));
  }
  else
   menuClearExit(0);
}

//modified for reduce used flash memory by KD8CEC
void menuRitToggle(int btn){
  if (!btn){
    if (ritOn == 1)
      printLineF2(F("RIT Off?"));
    else
      printLineF2(F("RIT On?"));
  }
  else {
      if (ritOn == 0){
        //printLineF2(F("RIT is ON"));
        //enable RIT so the current frequency is used at transmit
        ritEnable(frequency);
      }
      else{
        //printLineF2(F("RIT is OFF"));
        ritDisable();
      }
      
      //menuClearExit(500);
#ifdef USE_SW_SERIAL
      menuOn = 0;
#else
      //Only Clear And Delay for Character LCD
      menuClearExit(500);
#endif
      
  }
}

/**
 * Take a deep breath, math(ematics) ahead
 * The 25 mhz oscillator is multiplied by 35 to run the vco at 875 mhz
 * This is divided by a number to generate different frequencies.
 * If we divide it by 875, we will get 1 mhz signal
 * So, if the vco is shifted up by 875 hz, the generated frequency of 1 mhz is shifted by 1 hz (875/875)
 * At 12 Mhz, the carrier will needed to be shifted down by 12 hz for every 875 hz of shift up of the vco
 * 
 */

 //this is used by the si5351 routines in the ubitx_5351 file
extern int32_t calibration;
extern uint32_t si5351bx_vcoa;

void factoryCalibration(int btn){
  int knob = 0;

  //keep clear of any previous button press
  while (btnDown())
    delay(100);
  delay(100);
   
  if (!btn){
    printLineF2(F("Set Calibration?"));
    return;
  }

  calibration = 0;

  cwMode = 0;
  isUSB = true;

  //turn off the second local oscillator and the bfo
  si5351_set_calibration(calibration);
  startTx(TX_CW, 1);
  si5351bx_setfreq(2, 10000000l); 
  
  strcpy(b, "#1 10 MHz cal:");
  ltoa(calibration/8750, c, 10);
  strcat(b, c);
  printLine2(b);     

  while (!btnDown())
  {

    if (digitalRead(PTT) == LOW && !keyDown)
      cwKeydown();
    if (digitalRead(PTT)  == HIGH && keyDown)
      cwKeyUp();
      
    knob = enc_read();

    if (knob > 0)
      calibration += 875;
    else if (knob < 0)
      calibration -= 875;
    else 
      continue; //don't update the frequency or the display
      
    si5351_set_calibration(calibration);
    si5351bx_setfreq(2, 10000000l);
    strcpy(b, "#1 10 MHz cal:");
    ltoa(calibration/8750, c, 10);
    strcat(b, c);
    printLine2(b);     
  }

  cwTimeout = 0;
  keyDown = 0;
  stopTx();

  printLineF2(F("Calibration set!"));
  EEPROM.put(MASTER_CAL, calibration);
  initOscillators();
  setFrequency(frequency);
  updateDisplay();

  while(btnDown())
    delay(50);

  menuClearExit(100);
}

void menuSetupCalibration(int btn){
  int knob = 0;
  int32_t prev_calibration;
   
  if (!btn){
    printLineF2(F("Set Calibration?"));
    return;
  }

  printLineF1(F("Set to Zero-beat,"));
  printLineF2(F("press PTT to save"));
  delay_background(1000, 0);
  
  prev_calibration = calibration;
  calibration = 0;
  si5351_set_calibration(calibration);
  setFrequency(frequency);    
  
  strcpy(b, "cal:");
  ltoa(calibration/8750, c, 10);
  strcat(b, c);
  printLine2(b);     

  while (digitalRead(PTT) == HIGH && !btnDown())
  {
    knob = enc_read();

    if (knob > 0){
      calibration += 8750;
      usbCarrier += 120;
    }
    else if (knob < 0){
      calibration -= 8750;
      usbCarrier -= 120;
    }
    else
      continue; //don't update the frequency or the display

    si5351_set_calibration(calibration);
    si5351bx_setfreq(0, usbCarrier);
    setFrequency(frequency);    

    strcpy(b, "cal:");
    ltoa(calibration/8750, c, 10);
    strcat(b, c);
    printLine2(b);     
  }
  
  //save the setting
  if (digitalRead(PTT) == LOW){
    printLineF1(F("Calibration set!"));
    printLineF2(F("Set Carrier now"));
    EEPROM.put(MASTER_CAL, calibration);
    delay_background(2000, 0);
  }
  else
    calibration = prev_calibration;

  initOscillators();
  //si5351_set_calibration(calibration);
  setFrequency(frequency);    
  //printLine2ClearAndUpdate();
  //menuOn = 0;
  menuClearExit(0);
}

void printCarrierFreq(unsigned long freq){

  memset(c, 0, sizeof(c));
  memset(b, 0, sizeof(b));

  ultoa(freq, b, DEC);
  
  strncat(c, b, 2);
  strcat(c, ".");
  strncat(c, &b[2], 3);
  strcat(c, ".");
  strncat(c, &b[5], 3);
  printLine2(c);    
}

//modified by KD8CEC (just 1 line remarked //usbCarrier = ...
void menuSetupCarrier(int btn){
  int knob = 0;
  unsigned long prevCarrier;
   
  if (!btn){
      printLineF2(F("Set the BFO"));
    return;
  }

  prevCarrier = usbCarrier;
  printLineF1(F("Tune to best Signal"));  
  printLineF1(F("PTT to confirm. "));
  delay_background(1000, 0);

  //usbCarrier = 11995000l; //Remarked by KD8CEC, Suggest from many user, if entry routine factoryrest
  /*
  //for uBITX V5.0, but not used by KD8CEC, if you want default value of carrier on Calibration, delete remark symbols
#if UBITX_BOARD_VERSION == 5
  usbCarrier = 11053000l;
#else
  usbCarrier = 11995000l;
#endif
   */

  
  si5351bx_setfreq(0, usbCarrier);
  printCarrierFreq(usbCarrier);

  //disable all clock 1 and clock 2 
  while (digitalRead(PTT) == HIGH && !btnDown())
  {
    knob = enc_read();

    if (knob > 0)
      usbCarrier -= 5;
    else if (knob < 0)
      usbCarrier += 5;
    else
      continue; //don't update the frequency or the display
      
    si5351bx_setfreq(0, usbCarrier);
    printCarrierFreq(usbCarrier);

    Check_Cat(0);  //To prevent disconnections
    delay(100);
  }

  //save the setting
  if (digitalRead(PTT) == LOW){
    printLineF2(F("Carrier set!"));
    EEPROM.put(USB_CAL, usbCarrier);
    delay_background(1000, 0);
  }
  else 
    usbCarrier = prevCarrier;

  //si5351bx_setfreq(0, usbCarrier);          
  if (cwMode == 0)
    si5351bx_setfreq(0, usbCarrier);  //set back the carrier oscillator anyway, cw tx switches it off
  else
    si5351bx_setfreq(0, cwmCarrier);  //set back the carrier oscillator anyway, cw tx switches it off
  
  setFrequency(frequency);    
  //printLine2ClearAndUpdate();
  //menuOn = 0; 
  menuClearExit(0);
}
