/* 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 "app/dtmf.h"
#include "app/fm.h"
#include "audio.h"
#include "bsp/dp32g030/gpio.h"
#include "dcs.h"
#include "driver/bk4819.h"
#include "driver/eeprom.h"
#include "driver/gpio.h"
#include "driver/system.h"
#include "frequencies.h"
#include "functions.h"
#include "helper/battery.h"
#include "misc.h"
#include "radio.h"
#include "settings.h"

VFO_Info_t    *gTxVfo;
VFO_Info_t    *gRxVfo;
VFO_Info_t    *gCurrentVfo;
DCS_CodeType_t gSelectedCodeType;
DCS_CodeType_t gCurrentCodeType;
uint8_t        gSelectedCode;
STEP_Setting_t gStepSetting;
VfoState_t     VfoState[2];

bool RADIO_CheckValidChannel(uint16_t Channel, bool bCheckScanList, uint8_t VFO)
{
	uint8_t Attributes;
	uint8_t PriorityCh1;
	uint8_t PriorityCh2;

	if (!IS_MR_CHANNEL(Channel))
		return false;

	// Check channel is valid
	Attributes = gMR_ChannelAttributes[Channel];
	if ((Attributes & MR_CH_BAND_MASK) > BAND7_470MHz)
		return false;

	if (bCheckScanList)
	{
		switch (VFO)
		{
			case 0:
				if ((Attributes & MR_CH_SCANLIST1) == 0)
					return false;

				PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH1[0];
				PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH2[0];
				break;

			case 1:
				if ((Attributes & MR_CH_SCANLIST2) == 0)
					return false;

				PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH1[1];
				PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH2[1];
				break;

			default:
				return true;
		}

		if (PriorityCh1 == Channel)
			return false;

		if (PriorityCh2 == Channel)
			return false;
	}

	return true;
}

uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
{
	uint8_t i;

	for (i = 0; i < 200; i++)
	{
		if (Channel == 0xFF)
			Channel = MR_CHANNEL_LAST;
		else
		if (Channel > MR_CHANNEL_LAST)
			Channel = MR_CHANNEL_FIRST;

		if (RADIO_CheckValidChannel(Channel, bCheckScanList, VFO))
			return Channel;

		Channel += Direction;
	}

	return 0xFF;
}

void RADIO_InitInfo(VFO_Info_t *pInfo, uint8_t ChannelSave, uint8_t Band, uint32_t Frequency)
{
	memset(pInfo, 0, sizeof(*pInfo));
	pInfo->Band                    = Band;
	pInfo->SCANLIST1_PARTICIPATION = true;
	pInfo->SCANLIST2_PARTICIPATION = true;
	pInfo->STEP_SETTING            = STEP_25_0kHz;
	pInfo->StepFrequency           = 2500;
	pInfo->CHANNEL_SAVE            = ChannelSave;
	pInfo->FrequencyReverse        = false;
	pInfo->OUTPUT_POWER            = OUTPUT_POWER_HIGH;
	pInfo->ConfigRX.Frequency      = Frequency;
	pInfo->ConfigTX.Frequency      = Frequency;
	pInfo->pRX                     = &pInfo->ConfigRX;
	pInfo->pTX                     = &pInfo->ConfigTX;
	pInfo->FREQUENCY_OF_DEVIATION  = 1000000;

	RADIO_ConfigureSquelchAndOutputPower(pInfo);
}

void RADIO_ConfigureChannel(uint8_t VFO, uint32_t Arg)
{
	uint8_t     Channel;
	uint8_t     Attributes;
	uint8_t     Band;
	bool        bParticipation2;
	uint16_t    Base;
	uint8_t     Data[8];
	uint8_t     Tmp;
	uint32_t    Frequency;
	VFO_Info_t *pRadio = &gEeprom.VfoInfo[VFO];

	if (!gSetting_350EN)
	{
		if (gEeprom.FreqChannel[VFO] == 204)
			gEeprom.FreqChannel[VFO] = 205;

		if (gEeprom.ScreenChannel[VFO] == 204)
			gEeprom.ScreenChannel[VFO] = 205;
	}

	Channel = gEeprom.ScreenChannel[VFO];

	if (IS_VALID_CHANNEL(Channel))
	{
		#ifndef DISABLE_NOAA
			if (Channel >= NOAA_CHANNEL_FIRST)
			{
				RADIO_InitInfo(pRadio, gEeprom.ScreenChannel[VFO], 2, NoaaFrequencyTable[Channel - NOAA_CHANNEL_FIRST]);
	
				if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF)
					return;
	
				gUpdateStatus            = true;
				gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
				return;
			}
		#endif
		
		if (IS_MR_CHANNEL(Channel))
		{
			Channel = RADIO_FindNextChannel(Channel, RADIO_CHANNEL_UP, false, VFO);
			if (Channel == 0xFF)
			{
				Channel                    = gEeprom.FreqChannel[VFO];
				gEeprom.ScreenChannel[VFO] = gEeprom.FreqChannel[VFO];
			}
			else
			{
				gEeprom.ScreenChannel[VFO] = Channel;
				gEeprom.MrChannel[VFO]     = Channel;
			}
		}
	}
	else
		Channel = 205;

	Attributes = gMR_ChannelAttributes[Channel];
	if (Attributes == 0xFF)
	{
		uint8_t Index;

		if (IS_MR_CHANNEL(Channel))
		{
			Channel = gEeprom.FreqChannel[VFO];
			gEeprom.ScreenChannel[VFO] = gEeprom.FreqChannel[VFO];
		}

		Index = Channel - FREQ_CHANNEL_FIRST;
		RADIO_InitInfo(pRadio, Channel, Index, gLowerLimitFrequencyBandTable[Index]);

		return;
	}

	Band = Attributes & MR_CH_BAND_MASK;
	if (Band > BAND7_470MHz)
		Band = BAND6_400MHz;

	if (IS_MR_CHANNEL(Channel))
	{
		gEeprom.VfoInfo[VFO].Band                    = Band;
		gEeprom.VfoInfo[VFO].SCANLIST1_PARTICIPATION = !!(Attributes & MR_CH_SCANLIST1);
		bParticipation2                              = !!(Attributes & MR_CH_SCANLIST2);
	}
	else
	{
		Band                                         = Channel - FREQ_CHANNEL_FIRST;
		gEeprom.VfoInfo[VFO].Band                    = Band;
		bParticipation2                              = true;
		gEeprom.VfoInfo[VFO].SCANLIST1_PARTICIPATION = true;
	}

	gEeprom.VfoInfo[VFO].SCANLIST2_PARTICIPATION = bParticipation2;
	gEeprom.VfoInfo[VFO].CHANNEL_SAVE            = Channel;

	if (IS_MR_CHANNEL(Channel))
		Base = Channel * 16;
	else
		Base = 0x0C80 + ((Channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);

	if (Arg == 2 || Channel >= FREQ_CHANNEL_FIRST)
	{
		EEPROM_ReadBuffer(Base + 8, Data, 8);

		Tmp = Data[3] & 0x0F;
		if (Tmp > 2)
			Tmp = 0;

		gEeprom.VfoInfo[VFO].FREQUENCY_DEVIATION_SETTING = Tmp;
		gEeprom.VfoInfo[VFO].AM_CHANNEL_MODE             = !!(Data[3] & 0x10);

		Tmp = Data[6];
		if (Tmp > STEP_8_33kHz)
			Tmp = STEP_25_0kHz;

		gEeprom.VfoInfo[VFO].STEP_SETTING  = Tmp;
		gEeprom.VfoInfo[VFO].StepFrequency = StepFrequencyTable[Tmp];

		Tmp = Data[7];
		if (Tmp > 10)
			Tmp = 0;

		gEeprom.VfoInfo[VFO].SCRAMBLING_TYPE   = Tmp;
		gEeprom.VfoInfo[VFO].ConfigRX.CodeType = (Data[2] >> 0) & 0x0F;
		gEeprom.VfoInfo[VFO].ConfigTX.CodeType = (Data[2] >> 4) & 0x0F;

		Tmp = Data[0];
		switch (gEeprom.VfoInfo[VFO].ConfigRX.CodeType)
		{
			case CODE_TYPE_CONTINUOUS_TONE:
				if (Tmp >= 50)
					Tmp = 0;
				break;

			case CODE_TYPE_DIGITAL:
			case CODE_TYPE_REVERSE_DIGITAL:
				if (Tmp >= 104)
					Tmp = 0;
				break;

			default:
				gEeprom.VfoInfo[VFO].ConfigRX.CodeType = CODE_TYPE_OFF;
				Tmp = 0;
				break;
		}
		gEeprom.VfoInfo[VFO].ConfigRX.Code = Tmp;

		Tmp = Data[1];
		switch (gEeprom.VfoInfo[VFO].ConfigTX.CodeType)
		{
			case CODE_TYPE_CONTINUOUS_TONE:
				if (Tmp >= 50)
					Tmp = 0;
				break;

			case CODE_TYPE_DIGITAL:
			case CODE_TYPE_REVERSE_DIGITAL:
				if (Tmp >= 104)
					Tmp = 0;
				break;

			default:
				gEeprom.VfoInfo[VFO].ConfigTX.CodeType = CODE_TYPE_OFF;
				Tmp = 0;
				break;
		}
		gEeprom.VfoInfo[VFO].ConfigTX.Code = Tmp;

		if (Data[4] == 0xFF)
		{
			gEeprom.VfoInfo[VFO].FrequencyReverse  = false;
			gEeprom.VfoInfo[VFO].CHANNEL_BANDWIDTH = 0;
			gEeprom.VfoInfo[VFO].OUTPUT_POWER      = 2;
			gEeprom.VfoInfo[VFO].BUSY_CHANNEL_LOCK = false;
		}
		else
		{
			gEeprom.VfoInfo[VFO].FrequencyReverse  = !!(Data[4] & 1u);
			gEeprom.VfoInfo[VFO].CHANNEL_BANDWIDTH = !!(Data[4] & 2u);
			gEeprom.VfoInfo[VFO].OUTPUT_POWER      = (Data[4] >> 2) & 3u;
			gEeprom.VfoInfo[VFO].BUSY_CHANNEL_LOCK = !!(Data[4] & 0x10);
		}

		if (Data[5] == 0xFF)
		{
			gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = false;
			gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE  = 0;
		}
		else
		{
			gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = !!(Data[5] & 1u);
			gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE  = (Data[5] >> 1) & 3u;
		}

		struct
		{
			uint32_t Frequency;
			uint32_t Offset;
		} Info;

		EEPROM_ReadBuffer(Base, &Info, 8);

		pRadio->ConfigRX.Frequency = Info.Frequency;

		if (Info.Offset >= 100000000)
			Info.Offset = 1000000;

		gEeprom.VfoInfo[VFO].FREQUENCY_OF_DEVIATION = Info.Offset;
	}

	Frequency = pRadio->ConfigRX.Frequency;
	if (Frequency < gLowerLimitFrequencyBandTable[Band])
		pRadio->ConfigRX.Frequency = gLowerLimitFrequencyBandTable[Band];
	else
	if (Frequency > gUpperLimitFrequencyBandTable[Band])
		pRadio->ConfigRX.Frequency = gUpperLimitFrequencyBandTable[Band];
	else
	if (Channel >= FREQ_CHANNEL_FIRST)
		pRadio->ConfigRX.Frequency = FREQUENCY_FloorToStep(pRadio->ConfigRX.Frequency, gEeprom.VfoInfo[VFO].StepFrequency, gLowerLimitFrequencyBandTable[Band]);
	pRadio->ConfigRX.Frequency = Frequency;

	if (Frequency >= 10800000 && Frequency < 13600000)
		gEeprom.VfoInfo[VFO].FREQUENCY_DEVIATION_SETTING = FREQUENCY_DEVIATION_OFF;
	else
	if (!IS_MR_CHANNEL(Channel))
	{
		Frequency = FREQUENCY_FloorToStep(gEeprom.VfoInfo[VFO].FREQUENCY_OF_DEVIATION, gEeprom.VfoInfo[VFO].StepFrequency, 0);
		gEeprom.VfoInfo[VFO].FREQUENCY_OF_DEVIATION = Frequency;
	}

	RADIO_ApplyOffset(pRadio);

	memset(gEeprom.VfoInfo[VFO].Name, 0, sizeof(gEeprom.VfoInfo[VFO].Name));

	if (IS_MR_CHANNEL(Channel))
	{	// 16 bytes allocated to the channel name but only 12 used
		EEPROM_ReadBuffer(0x0F50 + (Channel * 16), gEeprom.VfoInfo[VFO].Name + 0, 8);
		EEPROM_ReadBuffer(0x0F58 + (Channel * 16), gEeprom.VfoInfo[VFO].Name + 8, 2);
	}

	if (!gEeprom.VfoInfo[VFO].FrequencyReverse)
	{
		gEeprom.VfoInfo[VFO].pRX = &gEeprom.VfoInfo[VFO].ConfigRX;
		gEeprom.VfoInfo[VFO].pTX = &gEeprom.VfoInfo[VFO].ConfigTX;
	}
	else
	{
		gEeprom.VfoInfo[VFO].pRX = &gEeprom.VfoInfo[VFO].ConfigTX;
		gEeprom.VfoInfo[VFO].pTX = &gEeprom.VfoInfo[VFO].ConfigRX;
	}

	if (!gSetting_350EN)
	{
		FREQ_Config_t *pConfig = gEeprom.VfoInfo[VFO].pRX;
		if (pConfig->Frequency < (35000000 + 4999991))
			pConfig->Frequency = 41001250;
	}

//	if (gEeprom.VfoInfo[VFO].Band == BAND2_108MHz && gEeprom.VfoInfo[VFO].AM_CHANNEL_MODE)
	if (gEeprom.VfoInfo[VFO].AM_CHANNEL_MODE) // allow AM on any frequency/band
	{
		gEeprom.VfoInfo[VFO].IsAM                 = true;
		gEeprom.VfoInfo[VFO].SCRAMBLING_TYPE      = 0;
		gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = false;
		gEeprom.VfoInfo[VFO].ConfigRX.CodeType    = CODE_TYPE_OFF;
		gEeprom.VfoInfo[VFO].ConfigTX.CodeType    = CODE_TYPE_OFF;
	}
	else
		gEeprom.VfoInfo[VFO].IsAM = false;

	RADIO_ConfigureSquelchAndOutputPower(pRadio);
}

void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
{
	uint8_t          Txp[3];
	FREQUENCY_Band_t Band = FREQUENCY_GetBand(pInfo->pRX->Frequency);
	uint16_t         Base = (Band < BAND4_174MHz) ? 0x1E60 : 0x1E00;

	if (gEeprom.SQUELCH_LEVEL == 0)
	{
		pInfo->SquelchOpenRSSIThresh    = 0x00;
		pInfo->SquelchOpenNoiseThresh   = 0x7F;
		pInfo->SquelchCloseGlitchThresh = 0xFF;
		pInfo->SquelchCloseRSSIThresh   = 0x00;
		pInfo->SquelchCloseNoiseThresh  = 0x7F;
		pInfo->SquelchOpenGlitchThresh  = 0xFF;
	}
	else
	{
		Base += gEeprom.SQUELCH_LEVEL;
		EEPROM_ReadBuffer(Base + 0x00, &pInfo->SquelchOpenRSSIThresh,    1);
		EEPROM_ReadBuffer(Base + 0x10, &pInfo->SquelchCloseRSSIThresh,   1);
		EEPROM_ReadBuffer(Base + 0x20, &pInfo->SquelchOpenNoiseThresh,   1);
		EEPROM_ReadBuffer(Base + 0x30, &pInfo->SquelchCloseNoiseThresh,  1);
		EEPROM_ReadBuffer(Base + 0x40, &pInfo->SquelchCloseGlitchThresh, 1);
		EEPROM_ReadBuffer(Base + 0x50, &pInfo->SquelchOpenGlitchThresh,  1);

		if (pInfo->SquelchOpenNoiseThresh >= 0x80)
			pInfo->SquelchOpenNoiseThresh = 0x7F;

		if (pInfo->SquelchCloseNoiseThresh >= 0x80)
			pInfo->SquelchCloseNoiseThresh = 0x7F;
	}

	Band = FREQUENCY_GetBand(pInfo->pTX->Frequency);

	EEPROM_ReadBuffer(0x1ED0 + (Band * 16) + (pInfo->OUTPUT_POWER * 3), Txp, 3);

	pInfo->TXP_CalculatedSetting =
	    FREQUENCY_CalculateOutputPower(
	        Txp[0],
	        Txp[1],
	        Txp[2],
	        LowerLimitFrequencyBandTable[Band],
	        MiddleFrequencyBandTable[Band],
	        UpperLimitFrequencyBandTable[Band],
	        pInfo->pTX->Frequency);
}

void RADIO_ApplyOffset(VFO_Info_t *pInfo)
{
	uint32_t Frequency = pInfo->ConfigRX.Frequency;

	switch (pInfo->FREQUENCY_DEVIATION_SETTING)
	{
		case FREQUENCY_DEVIATION_OFF:
			break;
		case FREQUENCY_DEVIATION_ADD:
			Frequency += pInfo->FREQUENCY_OF_DEVIATION;
			break;
		case FREQUENCY_DEVIATION_SUB:
			Frequency -= pInfo->FREQUENCY_OF_DEVIATION;
			break;
	}

	if (Frequency < 5000000)
		Frequency = 5000000;
	else
	if (Frequency > 60000000)
		Frequency = 60000000;

	pInfo->ConfigTX.Frequency = Frequency;
}

static void RADIO_SelectCurrentVfo(void)
{
	gCurrentVfo = (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF) ? gRxVfo : &gEeprom.VfoInfo[gEeprom.TX_CHANNEL];
}

void RADIO_SelectVfos(void)
{
	if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_CHAN_B)
		gEeprom.TX_CHANNEL = 1;
	else
	if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_CHAN_A)
		gEeprom.TX_CHANNEL = 0;
	else
	if (gEeprom.DUAL_WATCH == DUAL_WATCH_CHAN_B)
		gEeprom.TX_CHANNEL = 1;
	else
	if (gEeprom.DUAL_WATCH == DUAL_WATCH_CHAN_A)
		gEeprom.TX_CHANNEL = 0;

	gTxVfo = &gEeprom.VfoInfo[gEeprom.TX_CHANNEL];

	if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF)
		gEeprom.RX_CHANNEL = gEeprom.TX_CHANNEL;
	else
		gEeprom.RX_CHANNEL = (gEeprom.TX_CHANNEL == 0) ? 1 : 0;

	gRxVfo = &gEeprom.VfoInfo[gEeprom.RX_CHANNEL];

	RADIO_SelectCurrentVfo();
}

void RADIO_SetupRegisters(bool bSwitchToFunction0)
{
	BK4819_FilterBandwidth_t Bandwidth;
	uint16_t                 Status;
	uint16_t                 InterruptMask;
	uint32_t                 Frequency;

	GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
	gEnableSpeaker = false;

	BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_GREEN, false);

	Bandwidth = gRxVfo->CHANNEL_BANDWIDTH;
	if (Bandwidth != BK4819_FILTER_BW_WIDE)
		Bandwidth = BK4819_FILTER_BW_NARROW;

	BK4819_SetFilterBandwidth(Bandwidth);

	BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_RED, false);
	BK4819_SetupPowerAmplifier(0, 0);
	BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1, false);

	while (1)
	{
		Status = BK4819_ReadRegister(BK4819_REG_0C);
		if ((Status & 1U) == 0) // INTERRUPT REQUEST
			break;

		BK4819_WriteRegister(BK4819_REG_02, 0);
		SYSTEM_DelayMs(1);
	}
	BK4819_WriteRegister(BK4819_REG_3F, 0);

	// mic gain 0.5dB/step 0 to 31
	BK4819_WriteRegister(BK4819_REG_7D, 0xE940 | (gEeprom.MIC_SENSITIVITY_TUNING & 0x1f));

	#ifndef DISABLE_NOAA
		if (IS_NOT_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE) || !gIsNoaaMode)
			Frequency = gRxVfo->pRX->Frequency;
		else
			Frequency = NoaaFrequencyTable[gNoaaChannel];
	#else
		Frequency = gRxVfo->pRX->Frequency;
	#endif
	BK4819_SetFrequency(Frequency);

	BK4819_SetupSquelch(
		gRxVfo->SquelchOpenRSSIThresh,
		gRxVfo->SquelchCloseRSSIThresh,
		gRxVfo->SquelchOpenNoiseThresh,
		gRxVfo->SquelchCloseNoiseThresh,
		gRxVfo->SquelchCloseGlitchThresh,
		gRxVfo->SquelchOpenGlitchThresh);
	BK4819_PickRXFilterPathBasedOnFrequency(Frequency);
	BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2, true);
	BK4819_WriteRegister(BK4819_REG_48, 0xB3A8);

	InterruptMask = 0 | BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;

	#ifndef DISABLE_NOAA
		if (IS_NOT_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
	#endif
	{
		if (!gRxVfo->IsAM)
		{
			uint8_t CodeType;
			uint8_t Code;

			CodeType = gSelectedCodeType;
			Code     = gSelectedCode;
			if (gCssScanMode == CSS_SCAN_MODE_OFF)
			{
				CodeType = gRxVfo->pRX->CodeType;
				Code     = gRxVfo->pRX->Code;
			}

			switch (CodeType)
			{
				case CODE_TYPE_DIGITAL:
				case CODE_TYPE_REVERSE_DIGITAL:
					BK4819_SetCDCSSCodeWord(DCS_GetGolayCodeWord(CodeType, Code));
					InterruptMask = 0
						| BK4819_REG_3F_CxCSS_TAIL
						| BK4819_REG_3F_CDCSS_FOUND
						| BK4819_REG_3F_CDCSS_LOST
						| BK4819_REG_3F_SQUELCH_FOUND
						| BK4819_REG_3F_SQUELCH_LOST;
					break;

				case CODE_TYPE_CONTINUOUS_TONE:
					BK4819_SetCTCSSFrequency(CTCSS_Options[Code]);
					BK4819_Set55HzTailDetection();
					InterruptMask = 0
						| BK4819_REG_3F_CxCSS_TAIL
						| BK4819_REG_3F_CTCSS_FOUND
						| BK4819_REG_3F_CTCSS_LOST
						| BK4819_REG_3F_SQUELCH_FOUND
						| BK4819_REG_3F_SQUELCH_LOST;
					break;

				default:
					BK4819_SetCTCSSFrequency(670);
					BK4819_Set55HzTailDetection();
					InterruptMask = 0
						| BK4819_REG_3F_CxCSS_TAIL
						| BK4819_REG_3F_SQUELCH_FOUND
						| BK4819_REG_3F_SQUELCH_LOST;
					break;
			}

			if (gRxVfo->SCRAMBLING_TYPE == 0 || !gSetting_ScrambleEnable)
				BK4819_DisableScramble();
			else
				BK4819_EnableScramble(gRxVfo->SCRAMBLING_TYPE - 1);
		}
	}
	#ifndef DISABLE_NOAA
		else
		{
			BK4819_SetCTCSSFrequency(2625);
			InterruptMask = 0
				| BK4819_REG_3F_CTCSS_FOUND
				| BK4819_REG_3F_CTCSS_LOST
				| BK4819_REG_3F_SQUELCH_FOUND
				| BK4819_REG_3F_SQUELCH_LOST;
		}
	#endif
	
	#ifndef DISABLE_NOAA
		if (gEeprom.VOX_SWITCH && !gFmRadioMode && IS_NOT_NOAA_CHANNEL(gCurrentVfo->CHANNEL_SAVE) && !gCurrentVfo->IsAM)
	#else
		if (gEeprom.VOX_SWITCH && !gFmRadioMode && !gCurrentVfo->IsAM)
	#endif
	{
		BK4819_EnableVox(gEeprom.VOX1_THRESHOLD, gEeprom.VOX0_THRESHOLD);
		InterruptMask |= 0 | BK4819_REG_3F_VOX_FOUND | BK4819_REG_3F_VOX_LOST;
	}
	else
		BK4819_DisableVox();

	if (gRxVfo->IsAM || (!gRxVfo->DTMF_DECODING_ENABLE && !gSetting_KILLED))
	{
		BK4819_DisableDTMF();
	}
	else
	{
		BK4819_EnableDTMF();
		InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
	}
	BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);

	FUNCTION_Init();

	if (bSwitchToFunction0)
		FUNCTION_Select(FUNCTION_FOREGROUND);
}

#ifndef DISABLE_NOAA
	void RADIO_ConfigureNOAA(void)
	{
		uint8_t ChanAB;
	
		gUpdateStatus = true;
	
		if (gEeprom.NOAA_AUTO_SCAN)
		{
			if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
			{
				if (IS_NOT_NOAA_CHANNEL(gEeprom.ScreenChannel[0]))
				{
					if (IS_NOT_NOAA_CHANNEL(gEeprom.ScreenChannel[1]))
					{
						gIsNoaaMode = false;
						return;
					}
					ChanAB = 1;
				}
				else
					ChanAB = 0;
	
				if (!gIsNoaaMode)
					gNoaaChannel = gEeprom.VfoInfo[ChanAB].CHANNEL_SAVE - NOAA_CHANNEL_FIRST;
	
				gIsNoaaMode = true;
				return;
			}
	
			if (gRxVfo->CHANNEL_SAVE >= NOAA_CHANNEL_FIRST)
			{
				gIsNoaaMode     = true;
				gNoaaChannel    = gRxVfo->CHANNEL_SAVE - NOAA_CHANNEL_FIRST;
				gNOAA_Countdown = 50;
				gScheduleNOAA   = false;
			}
			else
				gIsNoaaMode = false;
		}
		else
			gIsNoaaMode = false;
	}
#endif

void RADIO_SetTxParameters(void)
{
	BK4819_FilterBandwidth_t Bandwidth;

	GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);

	gEnableSpeaker = false;

	BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2, false);

	Bandwidth = gCurrentVfo->CHANNEL_BANDWIDTH;
	if (Bandwidth != BK4819_FILTER_BW_WIDE)
		Bandwidth = BK4819_FILTER_BW_NARROW;
	BK4819_SetFilterBandwidth(Bandwidth);

	BK4819_SetFrequency(gCurrentVfo->pTX->Frequency);

	BK4819_PrepareTransmit();

	SYSTEM_DelayMs(10);

	BK4819_PickRXFilterPathBasedOnFrequency(gCurrentVfo->pTX->Frequency);

	BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1, true);

	SYSTEM_DelayMs(5);

	BK4819_SetupPowerAmplifier(gCurrentVfo->TXP_CalculatedSetting, gCurrentVfo->pTX->Frequency);

	SYSTEM_DelayMs(10);

	switch (gCurrentVfo->pTX->CodeType)
	{
		case CODE_TYPE_CONTINUOUS_TONE:
			BK4819_SetCTCSSFrequency(CTCSS_Options[gCurrentVfo->pTX->Code]);
			break;

		case CODE_TYPE_DIGITAL:
		case CODE_TYPE_REVERSE_DIGITAL:
			BK4819_SetCDCSSCodeWord(
					DCS_GetGolayCodeWord(gCurrentVfo->pTX->CodeType, gCurrentVfo->pTX->Code)
				);
			break;

		default:
			BK4819_ExitSubAu();
			break;
	}
}

void RADIO_SetVfoState(VfoState_t State)
{
	if (State == VFO_STATE_NORMAL)
	{
		VfoState[0] = VFO_STATE_NORMAL;
		VfoState[1] = VFO_STATE_NORMAL;

		gFM_ResumeCountdown = 0;
	}
	else
	{
		if (State == VFO_STATE_VOL_HIGH)
		{
			VfoState[0] = VFO_STATE_VOL_HIGH;
			VfoState[1] = VFO_STATE_TX_DISABLE;
		}
		else
		{
			const uint8_t Channel = (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF) ? gEeprom.RX_CHANNEL : gEeprom.TX_CHANNEL;
			VfoState[Channel] = State;
		}

		gFM_ResumeCountdown = 5;
	}

	gUpdateDisplay = true;
}

void RADIO_PrepareTX(void)
{
	if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
	{
		gDualWatchCountdown = 360;
		gScheduleDualWatch  = false;
		if (!gRxVfoIsActive)
		{
			gEeprom.RX_CHANNEL = gEeprom.TX_CHANNEL;
			gRxVfo             = &gEeprom.VfoInfo[gEeprom.TX_CHANNEL];
		}
		gRxVfoIsActive = true;
	}

	RADIO_SelectCurrentVfo();

	#ifndef DISABLE_ALARM
		if (gAlarmState == ALARM_STATE_OFF || gAlarmState == ALARM_STATE_TX1750 || (gAlarmState == ALARM_STATE_ALARM && gEeprom.ALARM_MODE == ALARM_MODE_TONE))
	#endif
	{
		VfoState_t State;

		if (!FREQUENCY_Check(gCurrentVfo))
		{
			if (gCurrentVfo->BUSY_CHANNEL_LOCK && gCurrentFunction == FUNCTION_RECEIVE)
				State = VFO_STATE_BUSY;
			else
			if (gBatteryDisplayLevel == 0)
				State = VFO_STATE_BAT_LOW;
			else
			if (gBatteryDisplayLevel == 6)
				State = VFO_STATE_VOL_HIGH;
			else
				goto Skip;
		}
		else
			State = VFO_STATE_TX_DISABLE;

		RADIO_SetVfoState(State);

		#ifndef DISABLE_ALARM
			gAlarmState = ALARM_STATE_OFF;
		#endif
		
		AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);

		gDTMF_ReplyState = DTMF_REPLY_NONE;

		return;
	}
	
Skip:
	if (gDTMF_ReplyState == DTMF_REPLY_ANI)
	{
		if (gDTMF_CallMode == DTMF_CALL_MODE_DTMF)
		{
			gDTMF_IsTx            = true;
			gDTMF_CallState       = DTMF_CALL_STATE_NONE;
			gDTMF_TxStopCountdown = 6;
		}
		else
		{
			gDTMF_CallState = DTMF_CALL_STATE_CALL_OUT;
			gDTMF_IsTx      = false;
		}
	}

	FUNCTION_Select(FUNCTION_TRANSMIT);

	#ifndef DISABLE_ALARM
		gTxTimerCountdown = (gAlarmState == ALARM_STATE_OFF) ? gEeprom.TX_TIMEOUT_TIMER * 120 : 0;
	#else
		gTxTimerCountdown = gEeprom.TX_TIMEOUT_TIMER * 120;
	#endif
	
	gTxTimeoutReached    = false;
	gFlagEndTransmission = false;
	gRTTECountdown       = 0;
	gDTMF_ReplyState     = DTMF_REPLY_NONE;
}

void RADIO_EnableCxCSS(void)
{
	switch (gCurrentVfo->pTX->CodeType)
	{
		case CODE_TYPE_DIGITAL:
		case CODE_TYPE_REVERSE_DIGITAL:
			BK4819_EnableCDCSS();
			break;

		default:
			BK4819_EnableCTCSS();
			break;
	}

	SYSTEM_DelayMs(200);
}

void RADIO_PrepareCssTX(void)
{
	RADIO_PrepareTX();
	SYSTEM_DelayMs(200);
	RADIO_EnableCxCSS();
	RADIO_SetupRegisters(true);
}

void RADIO_SendEndOfTransmission(void)
{
	if (gEeprom.ROGER == ROGER_MODE_ROGER)
		BK4819_PlayRoger();
	else
	if (gEeprom.ROGER == ROGER_MODE_MDC)
		BK4819_PlayRogerMDC();

	if (gDTMF_CallState == DTMF_CALL_STATE_NONE && (gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_EOT || gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_BOTH))
	{
		if (gEeprom.DTMF_SIDE_TONE)
		{
			GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
			gEnableSpeaker = true;
			SYSTEM_DelayMs(60);
		}

		BK4819_EnterDTMF_TX(gEeprom.DTMF_SIDE_TONE);

		BK4819_PlayDTMFString(
			gEeprom.DTMF_DOWN_CODE,
			0,
			gEeprom.DTMF_FIRST_CODE_PERSIST_TIME,
			gEeprom.DTMF_HASH_CODE_PERSIST_TIME,
			gEeprom.DTMF_CODE_PERSIST_TIME,
			gEeprom.DTMF_CODE_INTERVAL_TIME);

		GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);

		gEnableSpeaker = false;
	}

	BK4819_ExitDTMF_TX(true);
}
