/* 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 "app/dtmf.h"
#ifdef ENABLE_FMRADIO
	#include "app/fm.h"
	#include "driver/bk1080.h"
#endif
#include "driver/bk4819.h"
#include "driver/eeprom.h"
#if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG)
	#include "driver/uart.h"
#endif
#include "misc.h"
#include "radio.h"
#include "settings.h"
#include "ui/menu.h"

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


static const uint32_t DEFAULT_FREQUENCY_TABLE[] =
{
	14500000,    //
	14550000,    //
	43300000,    //
	43320000,    //
	43350000     //
};

t_eeprom g_eeprom;

void SETTINGS_write_eeprom_config(void)
{	// save the entire EEPROM config contents
	unsigned int index;
	for (index = 0; index < sizeof(g_eeprom.config); index += 8)
		EEPROM_WriteBuffer8(index, ((uint8_t *)&g_eeprom) + index);
}

void SETTINGS_write_eeprom_calib(void)
{	// save the entire EEPROM calibration contents
	unsigned int i;
	unsigned int index = (unsigned int)(((uint8_t *)&g_eeprom.calib) - ((uint8_t *)&g_eeprom));
	index &= ~7u;
	for (i = 0; i < sizeof(g_eeprom.calib); i += 8)
		EEPROM_WriteBuffer8(index + i, ((uint8_t *)&g_eeprom) + index + i);
}

#ifdef ENABLE_FMRADIO
	void SETTINGS_save_fm(void)
	{
		unsigned int i;
		unsigned int index;

		index = (unsigned int)(((uint8_t *)&g_eeprom.config.setting.fm_radio) - ((uint8_t *)&g_eeprom));
		index &= ~7u;
		EEPROM_WriteBuffer8(index, ((uint8_t *)&g_eeprom) + index);

		index = (unsigned int)(((uint8_t *)&g_eeprom.config.setting.fm_channel) - ((uint8_t *)&g_eeprom));
		index &= ~7u;
		for (i = 0; i < sizeof(g_eeprom.config.setting.fm_channel); i += 8)
			EEPROM_WriteBuffer8(index + i, ((uint8_t *)&g_eeprom) + index + i);
	}
#endif

void SETTINGS_save_vfo_indices(void)
{
	uint16_t index = (uint16_t)(((uint8_t *)&g_eeprom.config.setting.indices) - ((uint8_t *)&g_eeprom));
	index &= ~7u;
	EEPROM_WriteBuffer8(index, ((uint8_t *)&g_eeprom) + index);
}

void SETTINGS_save_attributes(void)
{
	unsigned int i;
	unsigned int index = (unsigned int)(((uint8_t *)&g_eeprom.config.channel_attributes) - ((uint8_t *)&g_eeprom));
	index &= ~7u;
	for (i = 0; i < sizeof(g_eeprom.config.channel_attributes); i += 8)
		EEPROM_WriteBuffer8(index + i, ((uint8_t *)&g_eeprom) + index + i);
}

void SETTINGS_save_channel_names(void)
{
	unsigned int i;
	unsigned int index = (unsigned int)(((uint8_t *)&g_eeprom.config.channel_name) - ((uint8_t *)&g_eeprom));
	index &= ~7u;
	for (i = 0; i < sizeof(g_eeprom.config.channel_name); i += 8)
		EEPROM_WriteBuffer8(index + i, ((uint8_t *)&g_eeprom) + index + i);
}

void SETTINGS_read_eeprom(void)
{
	unsigned int index;

	// read the entire EEPROM contents into memory
	for (index = 0; index < sizeof(g_eeprom); index += 128)
		EEPROM_ReadBuffer(index, (uint8_t *)(&g_eeprom) + index, 128);

	#if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG)
		UART_printf("config size %04X %u\r\n"
		            "unused size %04X %u\r\n"
		            "calib  size %04X %u\r\n"
		            "eeprom size %04X %u\r\n",
		             sizeof(g_eeprom.config), sizeof(g_eeprom.config),
					 sizeof(g_eeprom.unused), sizeof(g_eeprom.unused),
					 sizeof(g_eeprom.calib),  sizeof(g_eeprom.calib),
					 sizeof(g_eeprom),        sizeof(g_eeprom));
	#endif

#if 1
	// channel sanity checks ..

	for (index = 0; index < ARRAY_SIZE(g_eeprom.config.channel); index++)
	{
//		if (g_eeprom.config.channel_attributes[index].band <= BAND7_470MHz)
		{	// used channel

			if (g_eeprom.config.channel[index].mod_mode >= MOD_MODE_LEN)
				g_eeprom.config.channel[index].mod_mode = MOD_MODE_FM;

			if (g_eeprom.config.channel[index].tx_power_user == 0)
				g_eeprom.config.channel[index].tx_power_user = 8;

			if (g_eeprom.config.channel[index].squelch_level > 9)
				g_eeprom.config.channel[index].squelch_level = 0;

		}
	}
#endif

#if 0
	// sanity checks ..

	g_eeprom.config.setting.call1            = IS_USER_CHANNEL(g_eeprom.config.setting.call1)  ? g_eeprom.config.setting.call1 : USER_CHANNEL_FIRST;
	g_eeprom.config.setting.squelch_level    = (g_eeprom.config.setting.squelch_level < 10)    ? g_eeprom.config.setting.squelch_level : 1;
	g_eeprom.config.setting.tx_timeout       = (g_eeprom.config.setting.tx_timeout < ARRAY_SIZE(tx_timeout_secs)) ? ARRAY_SIZE(tx_timeout_secs) : 1;
	g_eeprom.config.setting.noaa_auto_scan   = (g_eeprom.config.setting.noaa_auto_scan < 2)    ? g_eeprom.config.setting.noaa_auto_scan : 0;
#ifdef ENABLE_KEYLOCK
	g_eeprom.config.setting.key_lock         = (g_eeprom.config.setting.key_lock < 2)          ? g_eeprom.config.setting.key_lock : 0;
#endif
#ifdef ENABLE_VOX
	g_eeprom.config.setting.vox_enabled      = (g_eeprom.config.setting.vox_enabled < 2)       ? g_eeprom.config.setting.vox_enabled : 0;
	g_eeprom.config.setting.vox_level        = (g_eeprom.config.setting.vox_level < 10)        ? g_eeprom.config.setting.vox_level : 1;
#endif
	g_eeprom.config.setting.mic_sensitivity  = (g_eeprom.config.setting.mic_sensitivity < 5)   ? g_eeprom.config.setting.mic_sensitivity : 4;

	g_eeprom.config.setting.channel_display_mode  = (g_eeprom.config.setting.channel_display_mode < 4)  ? g_eeprom.config.setting.channel_display_mode : MDF_FREQUENCY;    // 4 instead of 3 - extra display mode
	g_eeprom.config.setting.cross_vfo             = (g_eeprom.config.setting.cross_vfo < 3)             ? g_eeprom.config.setting.cross_vfo : CROSS_BAND_OFF;
	g_eeprom.config.setting.battery_save_ratio    = (g_eeprom.config.setting.battery_save_ratio < 5)    ? g_eeprom.config.setting.battery_save_ratio : 4;
	g_eeprom.config.setting.dual_watch            = (g_eeprom.config.setting.dual_watch < 3)            ? g_eeprom.config.setting.dual_watch : DUAL_WATCH_CHAN_A;
	g_eeprom.config.setting.backlight_time        = (g_eeprom.config.setting.backlight_time < ARRAY_SIZE(g_sub_menu_backlight)) ? g_eeprom.config.setting.backlight_time : 3;
	g_eeprom.config.setting.tail_tone_elimination = (g_eeprom.config.setting.tail_tone_elimination < 2) ? g_eeprom.config.setting.tail_tone_elimination : 0;
	g_eeprom.config.setting.vfo_open              = (g_eeprom.config.setting.vfo_open < 2)              ? g_eeprom.config.setting.vfo_open : 1;

	if (g_eeprom.config.setting.vfo_open == 0)
	{
		for (index = 0; index < 2; index++)
			g_eeprom.config.setting.indices.vfo[index].screen = g_eeprom.config.setting.indices.vfo[index].user;
	}

	// 0E80
	for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.indices.vfo); index++)
	{
		g_eeprom.config.setting.indices.vfo[index].screen    = IS_VALID_CHANNEL(g_eeprom.config.setting.indices.vfo[index].screen)   ? g_eeprom.config.setting.indices.vfo[index].screen    : (FREQ_CHANNEL_FIRST + BAND6_400MHz);
		g_eeprom.config.setting.indices.vfo[index].user      = IS_USER_CHANNEL(g_eeprom.config.setting.indices.vfo[index].user)      ? g_eeprom.config.setting.indices.vfo[index].user      :  USER_CHANNEL_FIRST;
		g_eeprom.config.setting.indices.vfo[index].frequency = IS_FREQ_CHANNEL(g_eeprom.config.setting.indices.vfo[index].frequency) ? g_eeprom.config.setting.indices.vfo[index].frequency : (FREQ_CHANNEL_FIRST + BAND6_400MHz);
	}
#ifdef ENABLE_NOAA
	for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.indices.noaa_channel); index++)
		g_eeprom.config.setting.indices.noaa_channel[index] = IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.noaa_channel[index]) ? g_eeprom.config.setting.indices.noaa_channel[index] : NOAA_CHANNEL_FIRST;
#endif

#ifdef ENABLE_FMRADIO
	// 0x0E88
	g_eeprom.config.setting.fm_radio.selected_frequency = (g_eeprom.config.setting.fm_radio.selected_frequency >= BK1080_freq_lower && g_eeprom.config.setting.fm_radio.selected_frequency < BK1080_freq_upper) ? g_eeprom.config.setting.fm_radio.selected_frequency : BK1080_freq_lower;
	g_eeprom.config.setting.fm_radio.selected_channel   = (g_eeprom.config.setting.fm_radio.selected_channel < ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) ? g_eeprom.config.setting.fm_radio.selected_channel : 0;
	g_eeprom.config.setting.fm_radio.channel_mode       = (g_eeprom.config.setting.fm_radio.channel_mode < 2) ? !!g_eeprom.config.setting.fm_radio.channel_mode : 0;

	// 0E40..0E67
	FM_configure_channel_state();
#endif

	// 0E90..0E97
	g_eeprom.config.setting.beep_control = (g_eeprom.config.setting.beep_control < 2) ? g_eeprom.config.setting.beep_control : 0;
	g_eeprom.config.setting.key1_short   = (g_eeprom.config.setting.key1_short < ACTION_OPT_LEN) ? g_eeprom.config.setting.key1_short : ACTION_OPT_MONITOR;
	g_eeprom.config.setting.key1_long    = (g_eeprom.config.setting.key1_long  < ACTION_OPT_LEN) ? g_eeprom.config.setting.key1_long  : ACTION_OPT_FLASHLIGHT;
	g_eeprom.config.setting.key2_short   = (g_eeprom.config.setting.key2_short < ACTION_OPT_LEN) ? g_eeprom.config.setting.key2_short : ACTION_OPT_SCAN;
	g_eeprom.config.setting.key2_long    = (g_eeprom.config.setting.key2_long  < ACTION_OPT_LEN) ? g_eeprom.config.setting.key2_long  : ACTION_OPT_NONE;
	g_eeprom.config.setting.carrier_search_mode = (g_eeprom.config.setting.carrier_search_mode < 3) ? g_eeprom.config.setting.carrier_search_mode : SCAN_RESUME_CARRIER;
	g_eeprom.config.setting.auto_key_lock       = (g_eeprom.config.setting.auto_key_lock < 2)       ? g_eeprom.config.setting.auto_key_lock : 0;
	g_eeprom.config.setting.power_on_display_mode = (g_eeprom.config.setting.power_on_display_mode < 4) ? g_eeprom.config.setting.power_on_display_mode : PWR_ON_DISPLAY_MODE_VOLTAGE;

	// 0EA0..0EA7
	#ifdef ENABLE_VOICE
		g_eeprom.config.setting.voice_prompt = (g_eeprom.config.setting.voice_prompt < 3) ? g_eeprom.config.setting.voice_prompt : VOICE_PROMPT_ENGLISH;
	#endif

	// 0EA8..0EAF
	#ifdef ENABLE_ALARM
		g_eeprom.config.setting.alarm_mode = (g_eeprom.config.setting.alarm_mode < 2) ? g_eeprom.config.setting.alarm_mode : 1;
	#endif
	g_eeprom.config.setting.roger_mode = (g_eeprom.config.setting.roger_mode < 3) ? g_eeprom.config.setting.roger_mode : ROGER_MODE_OFF;
	g_eeprom.config.setting.repeater_tail_tone_elimination = (g_eeprom.config.setting.repeater_tail_tone_elimination < 11) ? g_eeprom.config.setting.repeater_tail_tone_elimination : 0;
	g_eeprom.config.setting.tx_vfo_num = (g_eeprom.config.setting.tx_vfo_num < 2) ? g_eeprom.config.setting.tx_vfo_num : 0;
	#if defined(ENABLE_AIRCOPY) && defined(ENABLE_AIRCOPY_REMEMBER_FREQ)
		if (g_eeprom.config.setting.air_copy_freq > 0 && g_eeprom.config.setting.air_copy_freq < 0xffffffff)
		{
			for (index = 0; index < ARRAY_SIZE(FREQ_BAND_TABLE); index++)
				if (g_eeprom.config.setting.air_copy_freq >= FREQ_BAND_TABLE[index].lower && g_eeprom.config.setting.air_copy_freq < FREQ_BAND_TABLE[index].upper)
					break;
			g_aircopy_freq = (index < ARRAY_SIZE(FREQ_BAND_TABLE)) ? g_eeprom.config.setting.air_copy_freq : 0xffffffff;
		}
	#endif

	// 0ED0..0ED7
	g_eeprom.config.setting.dtmf.side_tone               = (g_eeprom.config.setting.dtmf.side_tone < 2) ? g_eeprom.config.setting.dtmf.side_tone : 1;
	g_eeprom.config.setting.dtmf.separate_code           = DTMF_ValidateCodes((char *)(&g_eeprom.config.setting.dtmf.separate_code),   1) ? g_eeprom.config.setting.dtmf.separate_code   : '*';
	g_eeprom.config.setting.dtmf.group_call_code         = DTMF_ValidateCodes((char *)(&g_eeprom.config.setting.dtmf.group_call_code), 1) ? g_eeprom.config.setting.dtmf.group_call_code : '#';
	g_eeprom.config.setting.dtmf.decode_response         = (g_eeprom.config.setting.dtmf.decode_response < 4) ? g_eeprom.config.setting.dtmf.decode_response : DTMF_DEC_RESPONSE_RING;
	g_eeprom.config.setting.dtmf.auto_reset_time         = (g_eeprom.config.setting.dtmf.auto_reset_time <= DTMF_HOLD_MAX) ? g_eeprom.config.setting.dtmf.auto_reset_time : (g_eeprom.config.setting.dtmf.auto_reset_time >= DTMF_HOLD_MIN) ? g_eeprom.config.setting.dtmf.auto_reset_time : DTMF_HOLD_MAX;
#endif
	g_eeprom.config.setting.dtmf.preload_time            = (g_eeprom.config.setting.dtmf.preload_time            < 3 || g_eeprom.config.setting.dtmf.preload_time            > 99) ? 30 : g_eeprom.config.setting.dtmf.preload_time;
	g_eeprom.config.setting.dtmf.first_code_persist_time = (g_eeprom.config.setting.dtmf.first_code_persist_time < 5 || g_eeprom.config.setting.dtmf.first_code_persist_time > 20) ? 10 : g_eeprom.config.setting.dtmf.first_code_persist_time;
	g_eeprom.config.setting.dtmf.hash_code_persist_time  = (g_eeprom.config.setting.dtmf.hash_code_persist_time  < 5 || g_eeprom.config.setting.dtmf.hash_code_persist_time  > 20) ? 10 : g_eeprom.config.setting.dtmf.hash_code_persist_time;
	g_eeprom.config.setting.dtmf.code_persist_time       = (g_eeprom.config.setting.dtmf.code_persist_time       < 5 || g_eeprom.config.setting.dtmf.code_persist_time       > 20) ? 10 : g_eeprom.config.setting.dtmf.code_persist_time;
	g_eeprom.config.setting.dtmf.code_interval_time      = (g_eeprom.config.setting.dtmf.code_interval_time      < 5 || g_eeprom.config.setting.dtmf.code_interval_time      > 20) ? 10 : g_eeprom.config.setting.dtmf.code_interval_time;
#if 0
	#ifdef ENABLE_DTMF_KILL_REVIVE
		g_eeprom.config.setting.dtmf.permit_remote_kill  = (g_eeprom.config.setting.dtmf.permit_remote_kill <   2) ? g_eeprom.config.setting.dtmf.permit_remote_kill : 0;
	#else
		g_eeprom.config.setting.dtmf.permit_remote_kill  = 0;
	#endif

	// 0EE0..0EE7
	if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.ani_id, sizeof(g_eeprom.config.setting.dtmf.ani_id)))
	{
		memset(g_eeprom.config.setting.dtmf.ani_id, 0, sizeof(g_eeprom.config.setting.dtmf.ani_id));
		strcpy(g_eeprom.config.setting.dtmf.ani_id, "123");
	}

	#ifdef ENABLE_DTMF_KILL_REVIVE
		// 0EE8..0EEF
		if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.kill_code, sizeof(g_eeprom.config.setting.dtmf.kill_code)))
		{
			memset(g_eeprom.config.setting.dtmf.kill_code, 0, sizeof(g_eeprom.config.setting.dtmf.kill_code));
			strcpy(g_eeprom.config.setting.dtmf.kill_code, "ABCD9");
		}

		// 0EF0..0EF7
		if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.revive_code, sizeof(g_eeprom.config.setting.dtmf.revive_code)))
		{
			memset(g_eeprom.config.setting.dtmf.revive_code, 0, sizeof(g_eeprom.config.setting.dtmf.revive_code));
			strcpy(g_eeprom.config.setting.dtmf.revive_code, "9DCBA");
		}
	#else
		memset(g_eeprom.config.setting.dtmf.kill_code,   0, sizeof(g_eeprom.config.setting.dtmf.kill_code));
		memset(g_eeprom.config.setting.dtmf.revive_code, 0, sizeof(g_eeprom.config.setting.dtmf.revive_code));
	#endif

	// 0EF8..0F07
	if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.key_up_code, sizeof(g_eeprom.config.setting.dtmf.key_up_code)))
	{
		memset(g_eeprom.config.setting.dtmf.key_up_code, 0, sizeof(g_eeprom.config.setting.dtmf.key_up_code));
		strcpy(g_eeprom.config.setting.dtmf.key_up_code, "12345");
	}

	// 0F08..0F17
	if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.key_down_code, sizeof(g_eeprom.config.setting.dtmf.key_down_code)))
	{
		memset(g_eeprom.config.setting.dtmf.key_down_code, 0, sizeof(g_eeprom.config.setting.dtmf.key_down_code));
		strcpy(g_eeprom.config.setting.dtmf.key_down_code, "54321");
	}

	// 0F18..0F1F
	g_eeprom.config.setting.scan_list_default = (g_eeprom.config.setting.scan_list_default < 3) ? g_eeprom.config.setting.scan_list_default : 0;  // we now have 'all' channel scan option
	for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.priority_scan_list); index++)
	{
		unsigned int k;
		g_eeprom.config.setting.priority_scan_list[index].enabled = (g_eeprom.config.setting.priority_scan_list[index].enabled < 2) ? g_eeprom.config.setting.priority_scan_list[index].enabled : 0;
		for (k = 0; k < ARRAY_SIZE(g_eeprom.config.setting.priority_scan_list[index].channel); k++)
			if (!IS_USER_CHANNEL(g_eeprom.config.setting.priority_scan_list[index].channel[k]))
				g_eeprom.config.setting.priority_scan_list[index].channel[k] = 0xff;
	}
	g_eeprom.config.setting.unused10 = 0xff;

	// 0F30..0F3F .. AES key
	g_has_aes_key = false;
	#if ENABLE_RESET_AES_KEY
		// wipe that darned AES key
		memset(&g_eeprom.config.setting.aes_key, 0xff, sizeof(g_eeprom.config.setting.aes_key));
	#else
		for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.aes_key) && !g_has_aes_key; index++)
			if (g_eeprom.config.setting.aes_key[index] != 0xffffffff)
				g_has_aes_key = true;
	#endif

	// 0F40..0F47
	g_eeprom.config.setting.freq_lock = (g_eeprom.config.setting.freq_lock < FREQ_LOCK_LAST) ? g_eeprom.config.setting.freq_lock : FREQ_LOCK_NORMAL;
//	g_eeprom.config.setting.enable_tx_350       = (g_eeprom.config.setting.enable_tx_350 < 2) ? g_eeprom.config.setting.enable_tx_350 : false;
	#ifdef ENABLE_DTMF_KILL_REVIVE
		g_eeprom.config.setting.radio_disabled  = (g_eeprom.config.setting.radio_disabled < 2) ? g_eeprom.config.setting.radio_disabled : 0;
	#else
		g_eeprom.config.setting.radio_disabled  = 0;
	#endif
//	g_eeprom.config.setting.enable_tx_200       = (g_eeprom.config.setting.enable_tx_200 < 2) ? g_eeprom.config.setting.enable_tx_200 : 0;
//	g_eeprom.config.setting.enable_tx_470       = (g_eeprom.config.setting.enable_tx_470 < 2) ? g_eeprom.config.setting.enable_tx_470 : 0;
//	g_eeprom.config.setting.enable_350          = (g_eeprom.config.setting.enable_350 < 2)    ? g_eeprom.config.setting.enable_350 : 1;
//	g_eeprom.config.setting.enable_scrambler    = (g_eeprom.config.setting.enable_scrambler & (1u << 0)) ? 1 : 0;
//	g_eeprom.config.setting.enable_rssi_bar     = (Data[6] & (1u << 1)) ? true : false;

//	g_eeprom.config.setting.tx_enable          = (Data[7] & (1u << 0)) ? true : false;
//	g_eeprom.config.setting.dtmf_live_decoder  = (Data[7] & (1u << 1)) ? true : false;
	g_eeprom.config.setting.battery_text       = (g_eeprom.config.setting.battery_text < 3) ? g_eeprom.config.setting.battery_text : 2;
	#ifdef ENABLE_TX_AUDIO_BAR
//		g_eeprom.config.setting.mic_bar        = (Data[7] & (1u << 4)) ? true : false;
	#endif
	#ifdef ENABLE_AM_FIX
//		g_eeprom.config.setting.am_fix         = (Data[7] & (1u << 5)) ? true : false;
	#endif
//	g_eeprom.config.setting.backlight_on_tx_rx = (Data[7] >> 6) & 3u;

#else

	#ifndef ENABLE_DTMF_KILL_REVIVE
		memset(g_eeprom.config.setting.dtmf.kill_code,   0, sizeof(g_eeprom.config.setting.dtmf.kill_code));
		memset(g_eeprom.config.setting.dtmf.revive_code, 0, sizeof(g_eeprom.config.setting.dtmf.revive_code));

		g_eeprom.config.setting.dtmf.permit_remote_kill  = 0;
	#endif

	#if ENABLE_RESET_AES_KEY
		// wipe that darned AES key
		memset(&g_eeprom.config.setting.aes_key, 0xff, sizeof(g_eeprom.config.setting.aes_key));
	#endif

	#ifndef ENABLE_DTMF_KILL_REVIVE
		g_eeprom.config.setting.radio_disabled  = 0;
	#endif

#endif

	#ifdef ENABLE_CONTRAST
		g_eeprom.config.setting.lcd_contrast = (g_eeprom.config.setting.lcd_contrast > 45) ? 31 : (g_eeprom.config.setting.lcd_contrast < 26) ? 31 : g_eeprom.config.setting.lcd_contrast;
	#endif

	// 0F48..0F4F
	g_eeprom.config.setting.scan_hold_time = (g_eeprom.config.setting.scan_hold_time > 40) ? 6 : (g_eeprom.config.setting.scan_hold_time < 2) ? 6 : g_eeprom.config.setting.scan_hold_time;

	// ****************************************
	// EEPROM cleaning

#if 1
	memset(&g_eeprom.config.unused13, 0xff, sizeof(g_eeprom.config.unused13));

	memset(&g_eeprom.unused, 0xff, sizeof(g_eeprom.unused));

	// clear out unused channels
	for (index = 0; index < 200; index++)
	{
		if (g_eeprom.config.channel_attributes[index].band > BAND7_470MHz)
		{	// unused channel
			g_eeprom.config.channel_attributes[index].attributes = 0xff;
			memset(&g_eeprom.config.user_channel[index], 0xff, sizeof(g_eeprom.config.user_channel[index]));
			memset(&g_eeprom.config.channel_name[index], 0xff, sizeof(g_eeprom.config.channel_name[index]));
		}
		else
		{	// used channel
			g_eeprom.config.channel_attributes[index].unused = 0x00;
			memset(g_eeprom.config.channel_name[index].unused, 0x00, sizeof(g_eeprom.config.channel_name[index].unused));

			// ensure the channel band attribute is correct
			if (g_eeprom.config.channel[index].frequency > 0 && g_eeprom.config.channel[index].frequency < 0xffffffff)
				g_eeprom.config.channel_attributes[index].band = FREQUENCY_GetBand(g_eeprom.config.channel[index].frequency);
		}
	}

	// force default VFO attributes
	for (index = 200; index < 207; index++)
		g_eeprom.config.channel_attributes[index].attributes = 0xC0 | (index - 200);
	g_eeprom.config.channel_attributes[207].attributes = 0x00;

	SETTINGS_save_attributes();
#endif

	// ****************************************
	// eeprom calibration data

//	memset(&g_eeprom.calib.unused3, 0xff, sizeof(g_eeprom.calib.unused3));

//	#ifdef ENABLE_FM_DEV_CAL_MENU
		if (g_eeprom.calib.deviation < FM_DEV_LIMIT_LOWER || g_eeprom.calib.deviation > FM_DEV_LIMIT_UPPER)
			g_eeprom.calib.deviation = FM_DEV_LIMIT_DEFAULT;
//	#endif

	if (g_eeprom.calib.battery[0] >= 5000)
	{
		g_eeprom.calib.battery[0] = 1900;
		g_eeprom.calib.battery[1] = 2000;
	}
	g_eeprom.calib.battery[5] = 2300;

	//EEPROM_ReadBuffer(0x1F80 + g_eeprom.config.setting.mic_sensitivity, &Mic, 1);
	//g_mic_sensitivity_tuning = (Mic < 32) ? Mic : 15;
	g_mic_sensitivity_tuning = g_mic_gain_dB_2[g_eeprom.config.setting.mic_sensitivity];

	g_eeprom.calib.bk4819_xtal_freq_low = (g_eeprom.calib.bk4819_xtal_freq_low >= -1000 && g_eeprom.calib.bk4819_xtal_freq_low <= 1000) ? g_eeprom.calib.bk4819_xtal_freq_low : 0;

	g_eeprom.calib.volume_gain = (g_eeprom.calib.volume_gain < 64) ? g_eeprom.calib.volume_gain : 58;
	g_eeprom.calib.dac_gain    = (g_eeprom.calib.dac_gain    < 16) ? g_eeprom.calib.dac_gain    : 8;

	BK4819_write_reg(0x3B, 22656 + g_eeprom.calib.bk4819_xtal_freq_low);
//	BK4819_write_reg(0x3C, g_eeprom.calib.BK4819_XTAL_FREQ_HIGH);

	// ****************************************
}

void SETTINGS_save(void)
{
	uint32_t index;

	#ifndef ENABLE_KEYLOCK
		g_eeprom.config.setting.key_lock = 0;
	#endif

	#ifndef ENABLE_VOX
//		g_eeprom.config.setting.vox_enabled = 0;
//		g_eeprom.config.setting.vox_level  = 0;
	#endif

	#ifndef ENABLE_CONTRAST
//		g_eeprom.config.setting.unused4 = 0xff;
	#endif

//	memset(&g_eeprom.config.setting.unused6, 0xff, sizeof(g_eeprom.config.setting.unused6));

	#ifndef ENABLE_PWRON_PASSWORD
		memset(&g_eeprom.config.setting.power_on_password, 0xff, sizeof(g_eeprom.config.setting.power_on_password));
	#endif

	#if !defined(ENABLE_ALARM) && (!defined(ENABLE_TX_TONE_HZ) || (ENABLE_TX_TONE_HZ == 0))
		g_eeprom.config.setting.alarm_mode = 0;
	#endif

	#if defined(ENABLE_AIRCOPY) && defined(ENABLE_AIRCOPY_REMEMBER_FREQ)
		// remember the AIRCOPY frequency
		g_eeprom.config.setting.air_copy_freq = g_aircopy_freq;
	#else
		memset(&g_eeprom.config.setting.unused8, 0xff, sizeof(g_eeprom.config.setting.unused8));
	#endif

	#ifndef ENABLE_DTMF_KILL_REVIVE
		g_eeprom.config.setting.radio_disabled = 0;
	#endif

	for (index = 0; index < sizeof(g_eeprom.config.setting); index += 8)
	{
		const uint16_t offset = (uint16_t)(((uint8_t *)&g_eeprom.config.setting) - ((uint8_t *)&g_eeprom));
		EEPROM_WriteBuffer8(offset + index, ((uint8_t *)&g_eeprom) + offset + index);
	}
}

void SETTINGS_save_channel(const unsigned int channel, const unsigned int vfo, vfo_info_t *p_vfo, const unsigned int mode)
{
	if (!IS_USER_CHANNEL(channel) && !IS_FREQ_CHANNEL(channel))
		return;

	if (p_vfo != NULL)
	{
		p_vfo->channel.frequency           = p_vfo->freq_config_rx.frequency;
		p_vfo->channel.rx_ctcss_cdcss_code = p_vfo->freq_config_rx.code;
		p_vfo->channel.tx_ctcss_cdcss_code = p_vfo->freq_config_tx.code;
		p_vfo->channel.rx_ctcss_cdcss_type = p_vfo->freq_config_rx.code_type;
		p_vfo->channel.tx_ctcss_cdcss_type = p_vfo->freq_config_tx.code_type;

		#if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG)
//			UART_printf("save chan 1  %u %u %u %uHz %uHz\r\n", channel, vfo, mode, p_vfo->channel.frequency * 10, p_vfo->channel.tx_offset * 10);
		#endif
	}

	if (mode <= 1 && channel <= USER_CHANNEL_LAST)
		return;

	{	// save the channel to EEPROM

		const unsigned int chan = CHANNEL_NUM(channel, vfo);
		const unsigned int addr = sizeof(t_channel) * chan;
		t_channel          m_channel;

		if (p_vfo != NULL)
			memcpy(&m_channel, &p_vfo->channel, sizeof(t_channel));
		else
		if (channel <= USER_CHANNEL_LAST)
			memset(&m_channel, 0xff, sizeof(t_channel));

		#if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG)
//			UART_printf("save chan 2 %04X  %3u %3u %u %u %uHz %uHz\r\n", addr, chan, channel, vfo, mode, m_channel.frequency * 10, m_channel.tx_offset * 10);
		#endif

		memcpy(&g_eeprom.config.channel[chan], &m_channel, sizeof(t_channel));

		g_eeprom.config.channel_attributes[channel] = p_vfo->channel_attributes;

		memset(&g_eeprom.config.channel_name[channel], 0, sizeof(g_eeprom.config.channel_name[channel]));
		memcpy(g_eeprom.config.channel_name[channel].name, p_vfo->channel_name.name, sizeof(g_eeprom.config.channel_name[channel].name));

		EEPROM_WriteBuffer8(addr + 0, ((uint8_t *)&m_channel) + 0);
		EEPROM_WriteBuffer8(addr + 8, ((uint8_t *)&m_channel) + 8);
	}

//	SETTINGS_save_vfo_indices();

	SETTINGS_save_chan_attribs_name(channel, p_vfo);
/*
	if (channel > USER_CHANNEL_LAST)
		return;

	// user channel, it has a channel name

//	memset(&g_eeprom.config.channel_name[channel], (p_vfo != NULL) ? 0x00 : 0xff, sizeof(g_eeprom.config.channel_name[channel]));
//	SETTINGS_save_chan_name(channel);

	if (p_vfo != NULL)
		memcpy(g_eeprom.config.channel_name[channel].name, p_vfo->channel_name.name, sizeof(g_eeprom.config.channel_name[channel].name));
	else
		memset(&g_eeprom.config.channel_name[channel], 0xff, sizeof(g_eeprom.config.channel_name[channel]));

	// save the channel name
	if (mode >= 3 || p_vfo == NULL)
		SETTINGS_save_chan_name(channel);
*/
}

void SETTINGS_save_chan_name(const unsigned int channel)
{
	const unsigned int    eeprom_offset = (unsigned int)(((uint8_t *)&g_eeprom.config.channel_name) - ((uint8_t *)&g_eeprom));
	const unsigned int    eeprom_addr   = eeprom_offset + (channel * 16);
	const t_channel_name *chan_name     = &g_eeprom.config.channel_name[channel];

	if (!IS_USER_CHANNEL(channel))
		return;

	EEPROM_WriteBuffer8(eeprom_addr + 0, ((uint8_t *)chan_name) + 0);
	EEPROM_WriteBuffer8(eeprom_addr + 8, ((uint8_t *)chan_name) + 8);
}

void SETTINGS_save_chan_attribs_name(const unsigned int channel, const vfo_info_t *p_vfo)
{
	const unsigned int eeprom_offset = (unsigned int)(((uint8_t *)&g_eeprom.config.channel_attributes) - ((uint8_t *)&g_eeprom));
	const unsigned int index         = channel & ~7u;     // eeprom writes are always 8 bytes in length

	if (!IS_USER_CHANNEL(channel) && !IS_FREQ_CHANNEL(channel))
		return;

	if (p_vfo != NULL)
	{	// channel attributes
		g_eeprom.config.channel_attributes[channel] = p_vfo->channel_attributes;
		EEPROM_WriteBuffer8(eeprom_offset + index, &g_eeprom.config.channel_attributes[index]);
	}
	else
	if (channel <= USER_CHANNEL_LAST)
	{	// user channel
		g_eeprom.config.channel_attributes[channel].attributes = 0xff;
		EEPROM_WriteBuffer8(eeprom_offset + index, &g_eeprom.config.channel_attributes[index]);
	}

	if (channel <= USER_CHANNEL_LAST)
	{	// user channel
		if (p_vfo != NULL)
		{
			memset(&g_eeprom.config.channel_name[channel], 0, sizeof(g_eeprom.config.channel_name[channel]));
			memcpy(g_eeprom.config.channel_name[channel].name, p_vfo->channel_name.name, sizeof(g_eeprom.config.channel_name[channel].name));
		}
		else
		{
			memset(&g_eeprom.config.channel_name[channel], 0xff, sizeof(g_eeprom.config.channel_name[channel]));
		}
		SETTINGS_save_chan_name(channel);
	}
}

unsigned int SETTINGS_find_channel(const uint32_t frequency)
{
	unsigned int chan;

	if (frequency == 0 || frequency == 0xffffffff)
		return 0xffffffff;

	for (chan = 0; chan <= USER_CHANNEL_LAST; chan++)
	{
		const uint32_t freq = g_eeprom.config.channel[chan].frequency;
		if (g_eeprom.config.channel_attributes[chan].band > BAND7_470MHz || freq == 0 || freq == 0xffffffff)
			continue;
		if (freq == frequency)
			return chan;    // found it
//		if (abs((int32_t)freq - (int32_t)frequency) < 300)
//			return chan;    // found a close match
	}

	return 0xffffffff;
}

uint32_t SETTINGS_fetch_channel_frequency(const int channel)
{
	uint32_t freq;

	if (channel < 0 || channel > (int)USER_CHANNEL_LAST)
		return 0;

	freq = g_eeprom.config.channel[channel].frequency;

	if (g_eeprom.config.channel_attributes[channel].band > BAND7_470MHz || freq == 0 || freq == 0xffffffff)
		return 0;

	return freq;
}

unsigned int SETTINGS_fetch_channel_step_setting(const int channel)
{
	unsigned int step_setting;

	if (channel < 0)
		return 0;

	if (channel <= USER_CHANNEL_LAST)
		step_setting = g_eeprom.config.channel[channel].step_setting;
	else
	if (channel <= FREQ_CHANNEL_LAST)
		step_setting = g_eeprom.config.vfo_channel[(channel - FREQ_CHANNEL_FIRST) * 2].step_setting;

//	step_size = STEP_FREQ_TABLE[step_setting];

	return (step_setting >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : step_setting;
}

unsigned int SETTINGS_fetch_frequency_step_setting(const int channel, const int vfo)
{
	unsigned int step_setting;

	if (channel < 0 || channel > (FREQ_CHANNEL_LAST - FREQ_CHANNEL_FIRST) || vfo < 0 || vfo >= 2)
		return 0;

	step_setting = g_eeprom.config.vfo_channel[(channel * 2) + vfo].step_setting;

//	step_size = STEP_FREQ_TABLE[step_setting];

	return (step_setting >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : step_setting;
}

void SETTINGS_fetch_channel_name(char *s, const int channel)
{
	int i;

	if (s == NULL)
		return;

	memset(s, 0, 11);  // 's' had better be large enough !

	if (channel < 0 || channel > (int)USER_CHANNEL_LAST)
		return;

	if (g_eeprom.config.channel_attributes[channel].band > BAND7_470MHz)
		return;

	memcpy(s, &g_eeprom.config.channel_name[channel], 10);

	for (i = 0; i < 10; i++)
		if (s[i] < 32 || s[i] > 127)
			break;                // invalid char

	s[i--] = 0;                   // null term

	while (i >= 0 && s[i] == 32)  // trim trailing spaces
		s[i--] = 0;               // null term
}

void SETTINGS_factory_reset(bool bIsAll)
{
	uint16_t i;
	uint8_t  Template[8];

	memset(Template, 0xFF, sizeof(Template));

	for (i = 0x0C80; i < 0x1E00; i += 8)
	{
		if (
			!(i >= 0x0EE0 && i < 0x0F18) &&         // ANI ID + DTMF codes
			!(i >= 0x0F30 && i < 0x0F50) &&         // AES KEY + F LOCK + Scramble Enable
			!(i >= 0x1C00 && i < 0x1E00) &&         // DTMF contacts
			!(i >= 0x0EB0 && i < 0x0ED0) &&         // Welcome strings
			!(i >= 0x0EA0 && i < 0x0EA8) &&         // Voice Prompt
			(bIsAll ||
			(
				!(i >= 0x0D60 && i < 0x0E28) &&     // MR Channel Attributes
				!(i >= 0x0F18 && i < 0x0F30) &&     // Scan List
				!(i >= 0x0F50 && i < 0x1C00) &&     // MR Channel Names
				!(i >= 0x0E40 && i < 0x0E70) &&     // FM Channels
				!(i >= 0x0E88 && i < 0x0E90)        // FM settings
				))
			)
		{
			EEPROM_WriteBuffer8(i, Template);
		}
	}

	if (bIsAll)
	{
		RADIO_InitInfo(g_rx_vfo, FREQ_CHANNEL_FIRST + BAND6_400MHz, 43350000);

		// set the first few memory channels
		for (i = 0; i < ARRAY_SIZE(DEFAULT_FREQUENCY_TABLE); i++)
		{
			const uint32_t Frequency           = DEFAULT_FREQUENCY_TABLE[i];
			g_rx_vfo->freq_config_rx.frequency = Frequency;
			g_rx_vfo->freq_config_tx.frequency = Frequency;
			g_rx_vfo->channel_attributes.band  = FREQUENCY_GetBand(Frequency);
			SETTINGS_save_channel(USER_CHANNEL_FIRST + i, 0, g_rx_vfo, 2);
		}
	}
}