/*
 * Copyright (C) 2019      Kai Ludwig, DG4KLU
 * Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
 *                         Daniel Caujolle-Bert, F1RMB
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
 *    incorporation in a commercial product or incorporation into a product or project which allows commercial use.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#ifndef _OPENGD77_CODEPLUG_H_
#define _OPENGD77_CODEPLUG_H_

#include <stdint.h>
#include <stdbool.h>

extern const int CODEPLUG_ADDR_CHANNEL_HEADER_EEPROM;

extern const int CODEPLUG_MAX_VARIABLE_SQUELCH;
extern const int CODEPLUG_MIN_VARIABLE_SQUELCH;
extern const int CODEPLUG_MIN_PER_CHANNEL_POWER;

extern const int VFO_FREQ_STEP_TABLE[8];
extern const int VFO_SWEEP_SCAN_RANGE_SAMPLE_STEP_TABLE[7];

#define CODEPLUG_ADDR_QUICKKEYS                                0x7524 // LSB,HSB
#define CODEPLUG_QUICKKEYS_SIZE                                    10
extern const int CODEPLUG_ADDR_VFO_A_CHANNEL;
extern const int CPS_CODEPLUG_SECTION_2_START;

// APRS Config
#define CODEPLUG_APRS_CONFIG_DATA_STRUCT_SIZE                      64U

// Zone
#define CODEPLUG_ZONE_DATA_ORIGINAL_STRUCT_SIZE                    48 // 16 channels per zone
#define CODEPLUG_ZONE_DATA_OPENGD77_STRUCT_SIZE                   176 // 80 channels per zone
#define ZONE_DATA_STRUCT_SIZE                                     184
// (NOTE: **DO NOT REMOVE THE UNSIGNED SUFFIX** of the following two constants
#define CODEPLUG_ZONES_MAX                                         68U // Max number of zones
#define CODEPLUG_ALL_ZONES_MAX                                     (1U + CODEPLUG_ZONES_MAX) // All channels + every zones

// Channel
#define CODEPLUG_CHANNEL_DATA_STRUCT_SIZE                          56
#define CHANNEL_DATA_STRUCT_SIZE                                   57
#define CODEPLUG_CHANNEL_FLAG4_OFFSET                              51

// RXGroup
#define CODEPLUG_RXGROUP_DATA_STRUCT_SIZE                          80

// Contact
#define CODEPLUG_CONTACT_DATA_SIZE                                 24
#define CONTACT_DATA_STRUCT_SIZE                                   28

// DTMF Contact
#define CODEPLUG_DTMF_CONTACT_DATA_STRUCT_SIZE                     32

// General Settings
#define CODEPLUG_GENERAL_SETTINGS_DATA_STRUCT_SIZE                 40

// DTMF Signalling
#define CODEPLUG_SIGNALLING_DTMF_DATA_STRUCT_SIZE                 120
#define SIGNALLING_DTMF_DURATIONS_DATA_STRUCT_SIZE                  5

// Last Used Channel in Zone
#define CODEPLUG_ADDR_LUCZ                                     0x6000


#define CODEPLUG_CONTACTS_MIN                                       1
#define CODEPLUG_CONTACTS_MAX                                    1024

#define CODEPLUG_CONTACT_FLAG_NO_TS_OVERRIDE                      0x01
#define CODEPLUG_CONTACT_FLAG_TS_OVERRIDE_TIMESLOT_MASK           0x02
#define CODEPLUG_CONTACT_FLAG_TS_OVERRIDE_MASK                    0x03

#define CODEPLUG_RX_GROUPLIST_MAX                                  76

#define CODEPLUG_CHANNELS_MIN                                       1
#define CODEPLUG_CHANNELS_MAX                                    1024
#define CODEPLUG_CHANNELS_BANKS_MAX                                 8
#define CODEPLUG_CHANNELS_PER_BANK                                128

#define CODEPLUG_DTMF_CONTACTS_MIN                                  1
#define CODEPLUG_DTMF_CONTACTS_MAX                                 63

#define CODEPLUG_CSS_TONE_NONE                                 0xFFFF
typedef enum
{
	CSS_TYPE_NONE          = (1 << 0),
	CSS_TYPE_CTCSS         = (1 << 1),
	CSS_TYPE_DCS           = (1 << 15),
	CSS_TYPE_DCS_INVERTED  = (1 << 14),
	CSS_TYPE_DCS_MASK      = (CSS_TYPE_DCS | CSS_TYPE_DCS_INVERTED)
} CodeplugCSSTypes_t;

typedef enum
{
	// LibreDMR_flag1
	CHANNEL_FLAG_OPTIONAL_DMRID = 0,
	CHANNEL_FLAG_NO_BEEP,
	CHANNEL_FLAG_NO_ECO,
	CHANNEL_FLAG_OUT_OF_BAND, // MD-9600 Only
	CHANNEL_FLAG_USE_LOCATION,
	CHANNEL_FLAG_FORCE_DMO,
	CHANNEL_FLAG_ROAMING,
	// flag2
	CHANNEL_FLAG_TIMESLOT_TWO,
	// flag3
	CHANNEL_FLAG_STE,
	CHANNEL_FLAG_NON_STE,
	// flag4
	CHANNEL_FLAG_POWER,
	CHANNEL_FLAG_VOX,
	CHANNEL_FLAG_ZONE_SKIP,
	CHANNEL_FLAG_ALL_SKIP,
	CHANNEL_FLAG_RX_ONLY,
	CHANNEL_FLAG_BW_25K,
	CHANNEL_FLAG_SQUELCH,
} ChannelFlag_t;

// LibreDMR_flag1
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_OPTIONAL_DMRID           0x80
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_NO_BEEP                  0x40
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_NO_ECO                   0x20
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_OUT_OF_BAND              0x10 // MD-9600 Only
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_USE_LOCATION             0x08
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_FORCE_DMO                0x04
#define CODEPLUG_CHANNEL_LIBREDMR_FLAG1_ROAMING                  0x01
// flag2
#define CODEPLUG_CHANNEL_FLAG2_TIMESLOT_TWO                      0x40
// flag3
#define CODEPLUG_CHANNEL_FLAG3_STE                               0xC0
#define CODEPLUG_CHANNEL_FLAG3_NON_STE                           0x20
// flag4
#define CODEPLUG_CHANNEL_FLAG4_SQUELCH                           0x01
#define CODEPLUG_CHANNEL_FLAG4_BW_25K                            0x02
#define CODEPLUG_CHANNEL_FLAG4_RX_ONLY                           0x04
#define CODEPLUG_CHANNEL_FLAG4_ALL_SKIP                          0x10
#define CODEPLUG_CHANNEL_FLAG4_ZONE_SKIP                         0x20
#define CODEPLUG_CHANNEL_FLAG4_VOX                               0x40
#define CODEPLUG_CHANNEL_FLAG4_POWER                             0x80

#define CODEPLUG_APRS_CONFIG_FLAG_NONE                           0x00
#define CODEPLUG_APRS_CONFIG_FLAG_300_BAUDS                      0x01
#define CODEPLUG_APRS_CONFIG_FLAG_USE_POSITION                   0x02
#define CODEPLUG_APRS_CONFIG_FLAG_TRANSMIT_QSY_INFO              0x04
#define CODEPLUG_APRS_CONFIG_FLAG_BEACONING_SILENTLY             0x08
#define CODEPLUG_APRS_CONFIG_FLAG_AMBIGUITY_MASK                 0xE0

extern int codeplugChannelsPerZone;

#if defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
#define FLASH_ADDRESS_OFFSET  (128 * 1024)
#else
#define FLASH_ADDRESS_OFFSET  (0)
#endif

typedef enum
{
	CONTACT_CALLTYPE_TG = 0,
	CONTACT_CALLTYPE_PC,
	CONTACT_CALLTYPE_ALL
} ContactCalltype_t;

typedef enum
{
     CHANNEL_VFO_A = 0,
     CHANNEL_VFO_B,
     CHANNEL_CHANNEL
} Channel_t;

// AllChannels zone indexNumber is -1
#define CODEPLUG_ZONE_IS_ALLCHANNELS(z) ((z).NOT_IN_CODEPLUGDATA_indexNumber < 0)


typedef struct
{
	char		name[16];
	uint16_t	channels[80];// 16 for the original codeplug, but set this to  80 to allow for the new codeplug zones format
	int			NOT_IN_CODEPLUGDATA_numChannelsInZone;// This property is not part of the codeplug data, its initialised by the code
	int			NOT_IN_CODEPLUGDATA_highestIndex; // Highest index in the array of channels
	int			NOT_IN_CODEPLUGDATA_indexNumber;// This property is not part of the codeplug data, its initialised by the code. Index >= 0 are real zone. -1 indicates the virtual "All channels" zone
} CodeplugZone_t;

typedef struct
{
	char name[16];
	uint32_t rxFreq;
	uint32_t txFreq;
	uint8_t chMode;
	uint8_t libreDMR_Power;
	uint8_t locationLat0;// Latitude LS byte
	uint8_t tot;
	uint8_t locationLat1;// Latitude
	uint8_t locationLat2;// Latitude MS byte
	uint8_t locationLon0;// Longitude LS byte
	uint8_t locationLon1;
	uint16_t rxTone;
	uint16_t txTone;
	uint8_t locationLon2;// Latitude MS byte
	uint8_t _UNUSED_1;
	uint8_t LibreDMR_flag1; // was unmuteRule. 0x80: Optional DMRID sets, 0x40: no beep, 0x20: no Eco, 0x10: OutOfBand(MD9600 only, never saved in codeplug)
	uint8_t rxSignaling;    // +--
	uint8_t artsInterval;   // | These 3 bytes were repurposed for optional DMRID
	uint8_t encrypt;        // +--
	uint8_t _UNUSED_2;
	uint8_t rxGroupList; // a.k.a TG List
	uint8_t txColor;
	uint8_t aprsConfigIndex;
	uint16_t contact;
	uint8_t flag1;// lower 4 bits TA Tx control
	uint8_t flag2; // bits... 0x40 = TS
	uint8_t flag3;// bits... 0x20 = NonSTE, 0xC0 = STE
	uint8_t flag4;// bits... 0x80 = Power, 0x40 = Vox, 0x20 = ZoneSkip (AutoScan), 0x10 = AllSkip (LoneWoker), 0x08 = AllowTalkaround(UNUSED), 0x04 = OnlyRx, 0x02 = Channel width, 0x01 = Squelch
	uint16_t VFOoffsetFreq;// NOT USED ?
	uint8_t VFOflag5;// upper 4 bits are the step frequency 2.5,5,6.25,10,12.5,25,30,50kHz
	uint8_t sql;// Does not seem to be used in the official firmware and seems to be always set to 0
	uint8_t NOT_IN_CODEPLUG_flag; // bit 0x01 = vfo channel
	int	NOT_IN_CODEPLUG_CALCULATED_DISTANCE_X10;// -1 = no distance available
} CodeplugChannel_t;

typedef enum
{
	TA_TX_OFF = 0,
	TA_TX_APRS,
	TA_TX_TEXT,
	TA_TX_BOTH
} CodeplugTATxDestination_t;

typedef struct
{
	char name[16];
	uint16_t contacts[32];
	int	NOT_IN_CODEPLUG_numTGsInGroup;// NOT IN THE
	uint32_t NOT_IN_CODEPLUG_contactsTG[32];
} CodeplugRxGroup_t;

typedef struct
{
	char 		name[16];
	uint32_t 	tgNumber;
	uint8_t		callType;
	uint8_t		callRxTone;
	uint8_t		ringStyle;
	uint8_t		reserve1; // TS override: bit0 1 = no TS override, bit1 + 1: timeslot override value
	int         NOT_IN_CODEPLUGDATA_indexNumber;
} CodeplugContact_t;

typedef struct
{
	char       name[16];
	uint8_t    code[16];
} CodeplugDTMFContact_t;

typedef struct
{
	char 		radioName[8];// callsign
	uint32_t	radioId;// DMD Id
	uint8_t		libreDMRCodeplugVersion;
	uint8_t		reserve[3];// was 4 bytes of reserved data but we use the first byte as the codeplug version
	uint8_t		arsInitDly;
	uint8_t		txPreambleDur;
	uint8_t		monitorType;
	uint8_t		voxSense;
	uint8_t		rxLowBatt;
	uint8_t		callAlertDur;
	uint8_t		respTmr;
	uint8_t		reminderTmr;
	uint8_t		grpHang;
	uint8_t		privateHang;
	uint8_t		flag1;
	uint8_t		flag2;
	uint8_t		flag3;
	uint8_t		flag4;
	uint8_t		reserver2[2];
	char 		prgPwd[8];
} CodeplugGeneralSettings_t;

typedef struct
{
	 char		selfId[8];
	 char		killCode[16];
	 char		wakeCode[16];
	 uint8_t	delimiter;
	 uint8_t	groupCode;
	 uint8_t	decodeResp;
	 uint8_t	autoResetTimer;
	 uint8_t	flag1;
	 uint8_t	reserve1[3];
	 char		pttidUpCode[30];
	 uint16_t	reserve2;
	 char		pttidDownCode[30];
	 uint16_t	reserve3;
	 uint8_t	respHoldTime;
	 uint8_t	decTime;
	 uint8_t	fstDigitDly;
	 uint8_t	fstDur;
	 uint8_t	otherDur;
	 uint8_t	rate;
	 uint8_t	flag2;
	 uint8_t	reserve4;
} CodeplugSignallingDTMF_t;

typedef struct
{
	uint8_t 	fstDigitDly;	// First Digit Delay (unit: hundreds of ms)
	uint8_t 	fstDur;			// First Digit Duration (unit: hundreds of ms)
	uint8_t 	otherDur;		// * and # Duration (unit: hundreds of ms)
	uint8_t 	rate;			// DTMF Rate: 1 .. 10 digits / second
	uint8_t		libreDMR_Tail;	// TX Tail duration (unit: hundreds of ms)
} CodeplugSignallingDTMFDurations_t;

typedef struct
{
	uint16_t	minUHFFreq;
	uint16_t	maxUHFFreq;
	uint16_t	minVHFFreq;
	uint16_t	maxVHFFreq;
	uint8_t		lastPrgTime[6];
	uint16_t	reserve2;
	uint8_t		model[8];
	uint8_t		sn[16];
	uint8_t		cpsSwVer[8];
	uint8_t		hardwareVer[8];
	uint8_t		firmwareVer[8];
	uint8_t		dspFwVer[24];
	uint8_t		reserve3[8];
} CodeplugDeviceInfo_t;


typedef enum
{
	CODEPLUG_CUSTOM_DATA_TYPE_EMPTY = 0xFFFFFFFF,
	CODEPLUG_CUSTOM_DATA_TYPE_IMAGE = 1,
	CODEPLUG_CUSTOM_DATA_TYPE_BEEP,
	CODEPLUG_CUSTOM_DATA_TYPE_SATELLITE_TLE,
	CODEPLUG_CUSTOM_DATA_TYPE_THEME_DAY,
	CODEPLUG_CUSTOM_DATA_TYPE_THEME_NIGHT,
	CODEPLUG_CUSTOM_DATA_TYPE_THEME_PRESET, // FreeTRX: 1 byte, selected theme preset (themePreset_t)
} CodeplugCustomDataType_t;


typedef struct
{
	char 		name[6];
	uint8_t		SSID;
} APRS_Paths_t;

// We need to use packed attribute here, as the structure is messy
// and introduces some padding...
typedef struct __attribute__((packed))
{
	char 			name[8];
	uint8_t 		senderSSID;
	char			latitude[3];
	char			longitude[3];
	APRS_Paths_t	paths[2];
	uint8_t			iconTable;
	uint8_t			iconIndex;
	char 			comment[24];
	uint32_t		txFrequency;
	uint8_t			reserved[2];
	uint8_t			flags;
	uint16_t		magicVer;
} CodeplugAPRSConfig_t;

/*
 * deprecated. Use our own non volatile storage instead
 *
void codeplugZoneGetSelected(int *selectedZone,int *selectedChannel);
void codeplugZoneSetSelected(int selectedZone,int selectedChannel);
 */
void codeplugZonesInitCache(void);
int codeplugZonesGetCount(void);
bool codeplugZoneGetDataForNumber(int indexNum, CodeplugZone_t *returnBuf);
uint32_t codeplugChannelGetOptionalDMRID(CodeplugChannel_t *channelBuf);
void codeplugChannelSetOptionalDMRID(CodeplugChannel_t *channelBuf, uint32_t dmrID);
uint8_t codeplugChannelGetFlag(CodeplugChannel_t *channelBuf, ChannelFlag_t flag);
uint8_t codeplugChannelSetFlag(CodeplugChannel_t *channelBuf, ChannelFlag_t flag, uint8_t value);
void codeplugChannelGetDataWithOffsetAndLengthForIndex(int index, CodeplugChannel_t *channelBuf, uint8_t offset, int length);
void codeplugChannelGetDataForIndex(int index, CodeplugChannel_t *channelBuf);
void codeplugUtilConvertBufToString(char *codeplugBuf, char *outBuf, int len);
void codeplugUtilConvertStringToBuf(char *inBuf, char *outBuf, int len);
uint32_t bcd2int(uint32_t i);
uint32_t int2bcd(uint32_t i);
uint16_t codeplugCSSToInt(uint16_t css);
uint16_t codeplugIntToCSS(uint16_t i);

bool codeplugRxGroupGetDataForIndex(int index, CodeplugRxGroup_t *rxGroupBuf);

bool codeplugContactGetDataForIndex(int index, CodeplugContact_t *contact);
bool codeplugDTMFContactGetDataForIndex(int index, CodeplugDTMFContact_t *contact);

uint32_t codeplugGetUserDMRID(void);
void codeplugSetUserDMRID(uint32_t dmrId);
void codeplugGetRadioName(char *buf);
void codeplugGetBootScreenData(char *line1, char *line2, uint8_t *displayType);
void codeplugGetVFO_ChannelData(CodeplugChannel_t *vfoBuf, Channel_t VFONumber);
void codeplugSetVFO_ChannelData(CodeplugChannel_t *vfoBuf, Channel_t VFONumber);
bool codeplugAllChannelsIndexIsInUse(int index);
void codeplugAllChannelsIndexSetUsed(int index);
void codeplugAllChannelsIndexSetUnused(int index);
bool codeplugChannelSaveDataForIndex(int index, CodeplugChannel_t *channelBuf);
CodeplugCSSTypes_t codeplugGetCSSType(uint16_t tone);
void codeplugConvertChannelInternalToCodeplug(CodeplugChannel_t *codeplugChannel, CodeplugChannel_t *internalChannel);


int codeplugDTMFContactsGetCount(void);
int codeplugContactsGetCount(uint32_t callType);
int codeplugContactGetDataForNumberInType(int number, uint32_t callType, CodeplugContact_t *contact);
int codeplugDTMFContactGetDataForNumber(int number, CodeplugDTMFContact_t *contact);
int codeplugContactIndexByTGorPCFromNumber(int number, uint32_t tgorpc, ContactCalltype_t callType, CodeplugContact_t *contact, uint8_t optionalTS);
int codeplugContactIndexByTGorPC(uint32_t tgorpc, ContactCalltype_t callType, CodeplugContact_t *contact, uint8_t optionalTS);
int codeplugContactSaveDataForIndex(int index, CodeplugContact_t *contact);
uint32_t codeplugContactGetPackedId(CodeplugContact_t *contact);
int codeplugContactGetFreeIndex(void);
bool codeplugContactGetRXGroup(int index);
void codeplugInitChannelsPerZone(void);
bool codeplugGetOpenGD77CustomData(CodeplugCustomDataType_t dataType, uint8_t *dataBuf);
bool codeplugSetOpenGD77CustomData(CodeplugCustomDataType_t dataType, uint8_t *dataBuf, int len);

void codeplugAllChannelsInitCache(void);
void codeplugInitCaches(void);

bool codeplugContactsContainsPC(uint32_t pc);
bool codeplugGetGeneralSettings(CodeplugGeneralSettings_t *generalSettingsBuffer);
bool codeplugGetSignallingDTMF(CodeplugSignallingDTMF_t *signallingDTMFBuffer);
bool codeplugGetSignallingDTMFDurations(CodeplugSignallingDTMFDurations_t *signallingDTMFDurationsBuffer);
bool codeplugZoneAddChannelToZoneAndSave(int channelIndex, CodeplugZone_t *zoneBuf);
bool codeplugZoneRemoveChannelFromZoneAndSave(int channelIndex, CodeplugZone_t *zoneBuf);
bool codeplugGetDeviceInfo(CodeplugDeviceInfo_t *deviceInfoBuffer);

uint16_t codeplugGetQuickkeyFunctionID(char key);
bool codeplugSetQuickkeyFunctionID(char key, uint16_t functionId);

int codeplugGetRepeaterWakeAttempts(void);
int codeplugGetPasswordPin(int32_t *pinCode);

void codeplugSetTATxForTS(CodeplugChannel_t *channelBuf, uint8_t ts, CodeplugTATxDestination_t taTx);
CodeplugTATxDestination_t codeplugGetTATxForTS(CodeplugChannel_t *channelBuf, uint8_t ts);

void codeplugInitLastUsedChannelInZone(void);
int16_t codeplugGetLastUsedChannelInZone(int zoneNum);
int16_t codeplugGetLastUsedChannelInCurrentZone(void);
int16_t codeplugGetLastUsedChannelNumberInCurrentZone(void);
int16_t codeplugSetLastUsedChannelInZone(int zoneNum, int16_t channelNum);
int16_t codeplugSetLastUsedChannelInCurrentZone(int16_t channelNum);
bool codeplugSaveLastUsedChannelInZone(void);

int codeplugAPRSConfigGetCount(void);
bool codeplugAPRSGetDataForIndex(int index, CodeplugAPRSConfig_t *APRS_Buf);
int codeplugAPRSGetIndexOfName(char *configName);

#endif
