// ---------------------------------------------------------------------------
//    ADAT
// ---------------------------------------------------------------------------
//
//  adat.h
//
//  Created by Frank Goenninger DG1SBG.
//  Copyright © 2011, 2012, 2023 Frank Goenninger.
//
//   This library is free software; you can redistribute it and/or
//   modify it under the terms of the GNU Lesser General Public
//   License as published by the Free Software Foundation; either
//   version 2.1 of the License, or (at your option) any later version.
//
//   This library is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//   Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public
//   License along with this library; if not, write to the Free Software
//   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


#if !defined( __ADAT_INCLUDED__ )
#define __ADAT_INCLUDED__

// ---------------------------------------------------------------------------
// SYSTEM INCLUDES
// ---------------------------------------------------------------------------

#include <stddef.h>

// ---------------------------------------------------------------------------
//    HAMLIB INCLUDES
// ---------------------------------------------------------------------------

#include "hamlib/rig.h"
#include "token.h"

// ---------------------------------------------------------------------------
//    GLOBAL DEFINITIONS
// ---------------------------------------------------------------------------

#define BACKEND_VER "20230927"

#define ADAT_BUFSZ                 255
#define ADAT_RESPSZ                255

#define ADAT_CR                    "\x0d"
#define ADAT_EOL                   "\x0a"
#define ADAT_BOM                   "$"       // Begin of message
#define ADAT_EOM                   ADAT_CR   // End of message

#define ADAT_ON                    1
#define ADAT_OFF                   0

#define ADAT_TOGGLE_ON             ADAT_ON
#define ADAT_TOGGLE_OFF            ADAT_OFF

#define ADAT_FREQ_UNIT_HZ          "Hz"
#define ADAT_FREQ_UNIT_HZ_LEN      2

#define ADAT_FREQ_UNIT_KHZ         "kHz"
#define ADAT_FREQ_UNIT_KHZ_LEN     3

#define ADAT_FREQ_UNIT_MHZ         "MHz"
#define ADAT_FREQ_UNIT_MHZ_LEN     3

#define ADAT_FREQ_UNIT_GHZ         "GHz"
#define ADAT_FREQ_UNIT_GHZ_LEN     3

#define TOKEN_ADAT_PRODUCT_NAME    TOKEN_BACKEND(1)

#define ADAT_SLEEP_MICROSECONDS_BETWEEN_CMDS (11*1000) // = 11 ms
#define ADAT_SLEEP_AFTER_RIG_CLOSE  2 // unit: seconds
#define ADAT_SLEEP_AFTER_RIG_OPEN   2 // unit: seconds

// ADAT VFO SET/GET DEFINITIONS

#define ADAT_NR_VFOS               3

// Each mode is defined by three values:
// ADAT_VFO_STR_... -> The string as given back by TRX when asked by
//                     $VFO?
// ADAT_VFO_RNR_... -> The Hamlib number of the mode: RIG_VFO_...
// ADAT_VFO_ANR_... -> The ADAT Nr representing the VFO when setting it

#define ADAT_VFO_STR_A             "A"
#define ADAT_VFO_RNR_A             RIG_VFO_A
#define ADAT_VFO_ANR_A             1

#define ADAT_VFO_STR_B             "B"
#define ADAT_VFO_RNR_B             RIG_VFO_B
#define ADAT_VFO_ANR_B             2

#define ADAT_VFO_STR_C             "C"
#define ADAT_VFO_RNR_C             RIG_VFO_C
#define ADAT_VFO_ANR_C             3

// ADAT MODE DEFINITIONS

#define ADAT_MODE_LENGTH           15
#define ADAT_NR_MODES              8

// Each mode is defined by three values:
// ADAT_MODE_STR_... -> The string as given back by TRX when asked by
//                      $MOD?
// ADAT_MODE_RNR_... -> The Hamlib number of the mode: RIG_MODE_...
// ADAT_MODE_ANR_... -> The ADAT Nr representing the mode when setting it

#define ADAT_MODE_STR_CW_R         "CW-R"
#define ADAT_MODE_RNR_CW_R         RIG_MODE_CWR
#define ADAT_MODE_ANR_CW_R         0

#define ADAT_MODE_STR_CW           "CW"
#define ADAT_MODE_RNR_CW           RIG_MODE_CW
#define ADAT_MODE_ANR_CW           1

#define ADAT_MODE_STR_LSB          "LSB"
#define ADAT_MODE_RNR_LSB          RIG_MODE_LSB
#define ADAT_MODE_ANR_LSB          2

#define ADAT_MODE_STR_USB          "USB"
#define ADAT_MODE_RNR_USB          RIG_MODE_USB
#define ADAT_MODE_ANR_USB          3

#define ADAT_MODE_STR_AM           "AM"
#define ADAT_MODE_RNR_AM           RIG_MODE_AM
#define ADAT_MODE_ANR_AM           5

#define ADAT_MODE_STR_AM_SL        "AM-SL"
#define ADAT_MODE_RNR_AM_SL        RIG_MODE_SAL
#define ADAT_MODE_ANR_AM_SL        6

#define ADAT_MODE_STR_AM_SU        "AM-SU"
#define ADAT_MODE_RNR_AM_SU        RIG_MODE_SAH
#define ADAT_MODE_ANR_AM_SU        7

#define ADAT_MODE_STR_FM           "FM"
#define ADAT_MODE_RNR_FM           RIG_MODE_FM
#define ADAT_MODE_ANR_FM           8

// ADAT PTT DEFINITIONS

#define ADAT_PTT_STATUS_ANR_ON     ADAT_ON
#define ADAT_PTT_STATUS_RNR_ON     RIG_PTT_ON

#define ADAT_PTT_STATUS_ANR_OFF    ADAT_OFF
#define ADAT_PTT_STATUS_RNR_OFF    RIG_PTT_OFF

// ADAT POWER LEVEL DEFINITIONS

#define ADAT_PWR_LVL_ANR_00              0
#define ADAT_PWR_LVL_RNR_00            100 // 100 mW

#define ADAT_PWR_LVL_ANR_01              1
#define ADAT_PWR_LVL_RNR_01            300 // 300 mW

#define ADAT_PWR_LVL_ANR_02              2
#define ADAT_PWR_LVL_RNR_02           1000 // ...

#define ADAT_PWR_LVL_ANR_03              3
#define ADAT_PWR_LVL_RNR_03           2000

#define ADAT_PWR_LVL_ANR_04              4
#define ADAT_PWR_LVL_RNR_04           3000

#define ADAT_PWR_LVL_ANR_05              5
#define ADAT_PWR_LVL_RNR_05           5000

#define ADAT_PWR_LVL_ANR_06              6
#define ADAT_PWR_LVL_RNR_06           7000

#define ADAT_PWR_LVL_ANR_07              7
#define ADAT_PWR_LVL_RNR_07          10000

#define ADAT_PWR_LVL_ANR_08              8
#define ADAT_PWR_LVL_RNR_08          15000

#define ADAT_PWR_LVL_ANR_09              9
#define ADAT_PWR_LVL_RNR_09          20000

#define ADAT_PWR_LVL_ANR_10             10
#define ADAT_PWR_LVL_RNR_10          25000

#define ADAT_PWR_LVL_ANR_11             11
#define ADAT_PWR_LVL_RNR_11          30000

#define ADAT_PWR_LVL_ANR_12             12
#define ADAT_PWR_LVL_RNR_12          35000

#define ADAT_PWR_LVL_ANR_13             13
#define ADAT_PWR_LVL_RNR_13          40000

#define ADAT_PWR_LVL_ANR_14             14 // Default value after reset
#define ADAT_PWR_LVL_RNR_14          45000

#define ADAT_PWR_LVL_ANR_15             15
#define ADAT_PWR_LVL_RNR_15          50000 // 50 W

#define ADAT_MAX_POWER_IN_mW         ADAT_PWR_LVL_RNR_15

// ADAT CHANNEL CAPS

#define ADAT_MEM_CAPS \
    { \
        .vfo          = 1, \
        .ant          = 1, \
        .freq         = 1, \
        .mode         = 1, \
        .width        = 1, \
        .tx_freq      = 1, \
        .tx_mode      = 1, \
        .tx_width     = 1, \
        .tx_vfo       = 1, \
        .rit          = 1, \
        .xit          = 1, \
        .tuning_step  = 1, \
        .channel_desc = 1  \
    }

#define ADAT_MEM_DESC_SIZE         64

// ADAT OPCODES - Kind of an internal command within ADAT Hamlib Backend

#define ADAT_OPCODE_BASE_PTT        110000

#define ADAT_OPCODE_PTT_SWITCH_ON   (ADAT_OPCODE_BASE_PTT + 1)
#define ADAT_OPCODE_PTT_SWITCH_OFF  (ADAT_OPCODE_BASE_PTT + 2)

// ---------------------------------------------------------------------------
//    Individual ADAT CAT commands
// ---------------------------------------------------------------------------

#define ADAT_CMD_DEF_ADAT_SPECIAL (1<<30)

// -- NIL --  (Marks the end of a cmd list)

#define ADAT_CMD_DEF_NIL 0

// -- ADAT SPECIAL: DISPLAY OFF --

#define ADAT_CMD_DEF_STRING_DISPLAY_OFF             "$VRU>"ADAT_CR

// -- ADAT SPECIAL: DISPLAY ON --

#define ADAT_CMD_DEF_STRING_DISPLAY_ON              "$VRU<"ADAT_CR

// -- ADAT SPECIAL: GET SERIAL NR --

#define ADAT_CMD_DEF_STRING_GET_SERIAL_NR           "$CIS?"ADAT_CR

// -- ADAT SPECIAL: GET FIRMWARE VERSION --

#define ADAT_CMD_DEF_STRING_GET_FW_VERSION          "$CIF?"ADAT_CR

// -- ADAT SPECIAL: GET HARDWARE VERSION --

#define ADAT_CMD_DEF_STRING_GET_HW_VERSION          "$CIH?"ADAT_CR

// -- ADAT SPECIAL: GET FIRMWARE VERSION --

#define ADAT_CMD_DEF_STRING_GET_ID_CODE             "$CID?"ADAT_CR

// -- ADAT SPECIAL: GET GUI FIRMWARE VERSION --

#define ADAT_CMD_DEF_STRING_GET_GUI_FW_VERSION      "$CIG?"ADAT_CR

// -- ADAT SPECIAL: GET OPTIONS --

#define ADAT_CMD_DEF_STRING_GET_OPTIONS             "$CIO?"ADAT_CR

// -- ADAT SPECIAL: GET CALLSIGN --

#define ADAT_CMD_DEF_STRING_GET_CALLSIGN            "$CAL?"ADAT_CR

// -- ADAT SPECIAL: SET CALLSIGN --

#define ADAT_CMD_DEF_STRING_SET_CALLSIGN            "$CAL:"

// -- HAMLIB DEFINED COMMANDS --

// -- GET FREQ --

#define ADAT_CMD_DEF_GET_FREQ                             (1<<0)
#define ADAT_CMD_DEF_STRING_GET_FREQ                      "$FRA?"ADAT_CR

// -- SET FREQ --

#define ADAT_CMD_DEF_SET_FREQ                             (1<<1)
#define ADAT_CMD_DEF_STRING_SET_FREQ                      "$FR1:"

// -- GET VFO --

// -- GET MODE --

#define ADAT_CMD_DEF_GET_MODE                             (1<<2)
#define ADAT_CMD_DEF_STRING_GET_MODE                      "$MOD?"ADAT_CR

// -- SET VFO --

#define ADAT_CMD_DEF_SET_VFO                              (1<<3)
#define ADAT_CMD_DEF_STRING_SWITCH_ON_VFO                 "$VO%1d>%s"
#define ADAT_CMD_DEF_STRING_SET_VFO_AS_MAIN_VFO           "$VO%1d%%%s"

// -- SET MODE --

#define ADAT_CMD_DEF_SET_MODE                             (1<<4)
#define ADAT_CMD_DEF_STRING_SET_MODE                      "$MOD:"

// -- SET PTT --

#define ADAT_CMD_DEF_SET_PTT                              (1<<5)
#define ADAT_CMD_DEF_STRING_SET_PTT                       "$MOX%s%s"
#define ADAT_CMD_PTT_STR_OFF                               "<"
#define ADAT_CMD_PTT_STR_ON                                ">"

// -- GET PTT --

#define ADAT_CMD_DEF_GET_PTT                              (1<<6)
#define ADAT_CMD_DEF_STRING_GET_PTT                       "$MTR?"ADAT_CR

// -- GET POWER STATUS --

// -- GET INFO --
// Nothing to define here

// -- OPEN ADAT --
// Nothing to define here

// -- ADAT SPECIAL: RECOVER FROM ERROR --
// Nothing to define here

// ---------------------------------------------------------------------------
//    ADAT PRIVATE DATA
// ---------------------------------------------------------------------------

#define ADAT_PRIV_DATA_PRODUCTNAME_LENGTH    255
#define ADAT_PRIV_DATA_SERIALNR_LENGTH       255
#define ADAT_PRIV_DATA_IDCODE_LENGTH         255
#define ADAT_PRIV_DATA_OPTIONS_LENGTH        255
#define ADAT_PRIV_DATA_FWVERSION_LENGTH      255
#define ADAT_PRIV_DATA_HWVERSION_LENGTH      255
#define ADAT_PRIV_DATA_GUIFWVERSION_LENGTH   255
#define ADAT_PRIV_DATA_CALLSIGN_LENGTH       255
#define ADAT_PRIV_DATA_CMD_LENGTH            255
#define ADAT_PRIV_DATA_RESULT_LENGTH         255

typedef struct _adat_priv_data
{
    int           nOpCode;

    char          acProductName[ ADAT_PRIV_DATA_PRODUCTNAME_LENGTH + 1]; // Future use (USB direct I/O)

    // ADAT device info

    char          acSerialNr[ ADAT_PRIV_DATA_SERIALNR_LENGTH + 1 ];
    char          acIDCode[ ADAT_PRIV_DATA_IDCODE_LENGTH + 1 ];
    char          acOptions[ ADAT_PRIV_DATA_OPTIONS_LENGTH + 1 ];
    char          acFWVersion[ ADAT_PRIV_DATA_FWVERSION_LENGTH + 1 ];
    char          acHWVersion[ ADAT_PRIV_DATA_HWVERSION_LENGTH + 1 ];
    char          acGUIFWVersion[ ADAT_PRIV_DATA_GUIFWVERSION_LENGTH + 1 ];

    char          acCallsign[ ADAT_PRIV_DATA_CALLSIGN_LENGTH + 1 ];

    // ADAT Operational Settings: will change during TRX use

    int           nCurrentVFO;
    vfo_t         nRIGVFONr;

    freq_t        nFreq;
    char          acRXFreq[ ADAT_BUFSZ ];
    char          acTXFreq[ ADAT_BUFSZ ];

    rmode_t       nRIGMode;
    char          acADATMode[ ADAT_MODE_LENGTH + 1 ];
    int           nADATMode;

    pbwidth_t     nWidth;

    int           nADATPTTStatus;
    ptt_t         nRIGPTTStatus;


    value_t       mNB1;
    value_t       mNB2;

    value_t       mAGC;
    value_t       mRFGain;
    value_t       mIFShift;
    value_t       mRawStr;

    // ADAT Command-related Values

    char          acCmd[ ADAT_PRIV_DATA_CMD_LENGTH + 1 ];
    int           nCmdKind;

    char          acResult[ ADAT_PRIV_DATA_RESULT_LENGTH + 1 ];
    int           nRC;

} adat_priv_data_t,
* adat_priv_data_ptr;

// ---------------------------------------------------------------------------
//    ADAT CAT COMMAND DATA TYPE DECLARATIONS
// ---------------------------------------------------------------------------

typedef unsigned long long adat_cmd_id_t; // Bit mask for commands. Each command
                                          // is represented by 1 bit.

// adat_cmd_def : ADAT COMMAND DEFINITION.
// Basic idea: Each command can be made of several strings to be sent
// to the ADAT device. Therefore it is possible to build aggregated
// commands which will be executed as a set of individual commands
// executed by adat_transaction(). The last value as returned by the
// commands will be set as overall command result.

typedef enum
{
    ADAT_CMD_KIND_WITH_RESULT    = 0,  // After sending a command to the ADAT,
                                       // a result has to be read.
    ADAT_CMD_KIND_WITHOUT_RESULT = 1

} adat_cmd_kind_t;

typedef struct _adat_cmd_def_t
{
    adat_cmd_id_t    nCmdId;        // Bit indicating this cmd
    adat_cmd_kind_t  nCmdKind;      // Defines if result expected

    int (*pfCmdFn)(RIG *pRig);      // Fn to be called to execute this cmd

    int              nNrCmdStrs;    // Oh my, C as a language ... I'd love to
                                    // switch to Common Lisp ... What a hack here ...
    char            *pacCmdStrs[];  // Commands to be executed if no CmdFn given

} adat_cmd_def_t,
* adat_cmd_def_ptr;

typedef struct _adat_cmd_table_t
{
    int              nNrCmds;
    adat_cmd_def_ptr adat_cmds[];

} adat_cmd_table_t,
* adat_cmd_table_ptr;

typedef struct _adat_cmd_list_t
{
    int              nNrCmds;
    adat_cmd_def_ptr adat_cmds[];

} adat_cmd_list_t,
* adat_cmd_list_ptr;

// ---------------------------------------------------------------------------
//    OTHER ADAT DATA TYPES
// ---------------------------------------------------------------------------

typedef enum
{
    ADAT_FREQ_PARSE_MODE_WITH_VFO     = 0,
    ADAT_FREQ_PARSE_MODE_WITHOUT_VFO  = 1

} adat_freq_parse_mode_t;

// ADAT MODE DEFINITION

typedef struct _adat_mode_def
{
    char     *pcADATModeStr;
    rmode_t   nRIGMode;
    int       nADATMode;

} adat_mode_def_t,
* adat_mode_def_ptr;

typedef struct _adat_mode_list
{
    int nNrModes;

    adat_mode_def_t adat_modes[ ADAT_NR_MODES ];

} adat_mode_list_t,
* adat_mode_list_ptr;

// ADAT VFO DEFINITION

typedef struct _adat_vfo_def
{
    char     *pcADATVFOStr;
    vfo_t     nRIGVFONr;
    int       nADATVFONr;

} adat_vfo_def_t,
* adat_vfo_def_ptr;

typedef struct _adat_vfo_list
{
    int nNrVFOs;

    adat_vfo_def_t adat_vfos[ ADAT_NR_VFOS ];

} adat_vfo_list_t,
* adat_vfo_list_ptr;

// ---------------------------------------------------------------------------
//    ADAT INTERNAL FUNCTION DECLARATIONS
// ---------------------------------------------------------------------------

// Helper functions

size_t trimwhitespace(char *, size_t, const char *);
int adat_print_cmd(adat_cmd_def_ptr);

int adat_parse_freq(char *, adat_freq_parse_mode_t, int *, freq_t *);

int adat_parse_mode(char *, rmode_t *, char *);
int adat_mode_rnr2anr(rmode_t, int *);
#ifdef XXREMOVEDXX
// this function wasn't referenced anywhere
int adat_mode_anr2rnr(int, rmode_t *);
#endif

#ifdef XXREMOVEDXX
// this function wasn't referenced anywhere
int adat_parse_vfo(char *, vfo_t *, int *);
#endif
int adat_vfo_rnr2anr(vfo_t, int *);
int adat_vfo_anr2rnr(int, vfo_t *);

int adat_parse_ptt(char *, int *);
int adat_ptt_rnr2anr(ptt_t, int *);
int adat_ptt_anr2rnr(int, ptt_t *);

int adat_send(RIG *, char *);
int adat_receive(RIG *, char *);

int adat_priv_set_cmd(RIG *, char *, int);
int adat_priv_set_result(RIG *, char *);
int adat_priv_clear_result(RIG *);

int adat_get_single_cmd_result(RIG *);

int adat_cmd_recover_from_error(RIG *, int);

int adat_transaction(RIG *, adat_cmd_list_ptr);

// Command implementation

int adat_cmd_fn_get_serial_nr(RIG *);
int adat_cmd_fn_get_fw_version(RIG *);
int adat_cmd_fn_get_hw_version(RIG *);
int adat_cmd_fn_get_gui_fw_version(RIG *);
int adat_cmd_fn_get_id_code(RIG *);
int adat_cmd_fn_get_options(RIG *);

int adat_cmd_fn_get_callsign(RIG *);

int adat_cmd_fn_set_freq(RIG *);
int adat_cmd_fn_get_freq(RIG *);

int adat_cmd_fn_get_mode(RIG *);
int adat_cmd_fn_set_mode(RIG *);

int adat_cmd_fn_get_vfo(RIG *);
int adat_cmd_fn_set_vfo(RIG *);

int adat_cmd_fn_get_ptt(RIG *);
int adat_cmd_fn_set_ptt(RIG *);

// ---------------------------------------------------------------------------
//    ADAT FUNCTION DECLARATIONS
// ---------------------------------------------------------------------------

int adat_init(RIG *);
int adat_cleanup(RIG *);
int adat_reset(RIG *, reset_t);
int adat_open(RIG *);
int adat_close(RIG *);

int adat_set_conf(RIG *, hamlib_token_t, const char *val);
int adat_get_conf(RIG *, hamlib_token_t, char *val);

int adat_set_freq(RIG *, vfo_t, freq_t);
int adat_get_freq(RIG *, vfo_t, freq_t *);

int adat_set_vfo(RIG *, vfo_t);
int adat_get_vfo(RIG *, vfo_t *);

int adat_set_ptt(RIG *, vfo_t, ptt_t);
int adat_get_ptt(RIG *, vfo_t, ptt_t *);

int adat_set_mode(RIG *, vfo_t, rmode_t, pbwidth_t);
int adat_get_mode(RIG *, vfo_t, rmode_t *, pbwidth_t *);

int adat_set_func(RIG *, vfo_t, setting_t func, int);
int adat_get_func(RIG *, vfo_t, setting_t func, int *);

int adat_set_level(RIG *, vfo_t, setting_t level, value_t);
int adat_get_level(RIG *, vfo_t, setting_t level, value_t *);

int adat_handle_event(RIG *);

const char *adat_get_info(RIG *);

int adat_mW2power(RIG *, float *, unsigned int, freq_t, rmode_t);
int adat_power2mW(RIG *, unsigned int *, float, freq_t, rmode_t);

int adat_get_powerstat(RIG *, powerstat_t *);

extern struct rig_caps adt_200a_caps;

// ---------------------------------------------------------------------------
//    END OF FILE
// ---------------------------------------------------------------------------

#endif
