/*
 *  Hamlib Elecraft backend--support Elecraft extensions to Kenwood commands
 *  Copyright (C) 2010,2011 by Nate Bargmann, n0nb@n0nb.us
 *  Copyright (C) 2011 by Alexander Sack, Alexander Sack, pisymbol@gmail.com
 *
 *
 *   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
 *
 *  See the file 'COPYING.LIB' in the main Hamlib distribution directory for
 *  the complete text of the GNU Lesser Public License version 2.1.
 *
 */
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <string.h>
#include <stdio.h>

#include "iofunc.h"
#include "elecraft.h"
#include "kenwood.h"
#include "misc.h"


static const struct elec_ext_id_str elec_ext_id_str_lst[] =
{
    { K20, "K20" },
    { K21, "K21" },
    { K22, "K22" },
    { K23, "K23" },
    { K30, "K30" },
    { K31, "K31" },
    { EXT_LEVEL_NONE, NULL },   /* end marker */
};


/* K3 firmware revision level, will be assigned to the fw_rev pointer in
 * kenwood_priv_data structure at runtime in electraft_open().  The array is
 * declared here so that the sizeof operator can be used in the call to
 * elecraft_get_firmware_revision_level() to calculate the exact size of the
 * array for the call to strncpy().
 */
static char k3_fw_rev[KENWOOD_MAX_BUF_LEN];


/* Private Elecraft extra levels definitions
 *
 * Token definitions for .cfgparams in rig_caps
 * See enum rig_conf_e and struct confparams in rig.h
 */
const struct confparams elecraft_ext_levels[] =
{
    {
        TOK_IF_FREQ, "ifctr", "IF freq", "IF center frequency",
        NULL, RIG_CONF_NUMERIC, { .n = { 0, 9990, 10 } }
    },
    {
        TOK_TX_STAT, "txst", "TX status", "TX status",
        NULL, RIG_CONF_CHECKBUTTON, { { } },
    },
    {
        TOK_RIT_CLR, "ritclr", "RIT clear", "RIT clear",
        NULL, RIG_CONF_BUTTON, { { } },
    },
    { RIG_CONF_END, NULL, }
};


/* Private function declarations */
static int verify_kenwood_id(RIG *rig, char *id);
static int elecraft_get_extension_level(RIG *rig, const char *cmd, int *ext_level);
static int elecraft_get_firmware_revision_level(RIG *rig, const char *cmd,
        char *fw_rev, size_t fw_rev_sz);

/* Shared backend function definitions */

/* elecraft_open()
 *
 * First checks for ID of '017' then tests for an Elecraft radio/backend using
 * the K2; command.  Here we also test for a K3 and if that fails, assume a K2.
 * Finally, save the value for later reading.
 *
 */

int elecraft_open(RIG *rig)
{
    int err;
    char buf[KENWOOD_MAX_BUF_LEN];
    char *model = "Unknown";
    struct rig_state *rs = STATE(rig);
    struct hamlib_port *rp = RIGPORT(rig);
    struct kenwood_priv_data *priv = rs->priv;

    rig_debug(RIG_DEBUG_VERBOSE, "%s called, rig version=%s\n", __func__,
              rig->caps->version);

    if (rs->auto_power_on && priv->poweron == 0)
    {
        rig_set_powerstat(rig, 1);
        priv->poweron = 1;
    }


    /* Actual read extension levels from radio.
     *
     * The value stored in the k?_ext_lvl variables map to
     * elec_ext_id_str_lst.level and is only written to by the
     * elecraft_get_extension_level() private function during elecraft_open()
     * and thereafter shall be treated as READ ONLY!
     */
    /* As k3_fw_rev is declared static, it is persistent so the structure
     * can point to it.  This way was chosen to allow the compiler to
     * calculate the size of the array to resolve a bug found by gcc 4.8.x
     */
    priv->fw_rev = k3_fw_rev;

    /* Use check for "ID017;" to verify rig is reachable */
    rig_debug(RIG_DEBUG_TRACE, "%s: rig_model=%u,%d\n", __func__,
              rig->caps->rig_model, RIG_MODEL_XG3);

    if (rig->caps->rig_model == RIG_MODEL_XG3)   // XG3 doesn't have ID
    {
        char *cmd = "V;";
        char data[32];

        strcpy(data, "EMPTY");
        // Not going to get carried away with retries and such
        err = write_block(rp, (unsigned char *) cmd, strlen(cmd));

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_TRACE, "%s: XG3 cannot request identification\n", __func__);
            return err;
        }

        err = read_string(rp, (unsigned char *) buf, sizeof(buf),
                          ";", 1, 0, 1);

        if (err < 0)
        {
            rig_debug(RIG_DEBUG_TRACE, "%s: XG3 cannot get identification\n", __func__);
            return err;
        }

        rig_debug(RIG_DEBUG_VERBOSE, "%s: id=%s\n", __func__, buf);
#if 0

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_TRACE, "%s: cannot get identification\n", __func__);
            return err;
        }

#endif
    }
    else   // Standard Kenwood
    {
        err = verify_kenwood_id(rig, buf);

        if (err != RIG_OK)
        {
            return err;
        }
    }

    priv->save_k2_ext_lvl = -1; // so we don't restore if not needed

    if (rig->caps->rig_model != RIG_MODEL_XG3)   // XG3 doesn't have extended
    {
        err = elecraft_get_extension_level(rig, "K2", &priv->save_k2_ext_lvl);

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: error getting K2 ext_lvl: %s\n", __func__,
                      rigerror(err));
            return err;
        }

        // turn on k2 extended to get PC values in more resolution
        err = kenwood_transaction(rig, "K22;", NULL, 0);

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: error setting K22='%s'...continuing\n", __func__,
                      rigerror(err));
        }
    }

    switch (rig->caps->rig_model)
    {
    case RIG_MODEL_K2:
        err = elecraft_get_extension_level(rig, "K2", &priv->k2_ext_lvl);

        if (err != RIG_OK)
        {
            return err;
        }

        rig_debug(RIG_DEBUG_VERBOSE, "%s: K2 level is %d, %s\n", __func__,
                  priv->k2_ext_lvl, elec_ext_id_str_lst[priv->k2_ext_lvl].id);

        priv->is_k2 = 1;
        break;

    case RIG_MODEL_K3:
    case RIG_MODEL_K3S:
    case RIG_MODEL_KX2:
    case RIG_MODEL_KX3:
    case RIG_MODEL_K4:
        // we need to know what's hooked up for PC command max levels
        err =  kenwood_safe_transaction(rig, "OM", buf, KENWOOD_MAX_BUF_LEN, 15);

        if (err != RIG_OK) { return err; }

        rig_debug(RIG_DEBUG_TRACE, "%s: OM=%s\n", __func__, buf);
        priv->has_kpa3 = 0;

        if (strstr(buf, "P")) { priv->has_kpa3 = 1; }

        // could also use K4; command
        priv->is_k3 = 1;  // default to K3

        if (rig->caps->rig_model == RIG_MODEL_K4)
        {
            priv->is_k3 = 0;
            priv->is_k4 = 1;
        }
        else if (strstr(buf, "R"))
        {
            priv->is_k3 = 0;
            priv->is_k3s = 1;
        }

        // combination of OM flags determines model
        if (strstr(buf, "S") && strstr(buf, "4") && strstr(buf, "H"))
        {
            // new firmware should recognize k4hd now
            priv->is_k4 = priv->is_k3 = 0;
            priv->is_k4hd = 1;
        }
        else if (strstr(buf, "S") && strstr(buf, "4"))
        {
            priv->is_k4 = priv->is_k3 = 0;
            priv->is_k4d = 1;
        }

        if (buf[13] == '0') // then we have a KX3 or KX2
        {
            char modelnum;
            modelnum = buf[14];

            switch (modelnum)
            {
            case '1':
                priv->is_k2 = 0;
                model = "KX2";
                priv->is_kx2 = 1;
                break;

            case '2':
                model = "KX3";
                priv->is_k3 = 0;
                priv->is_kx3 = 1;
                break;

            default:
                rig_debug(RIG_DEBUG_ERR, "%s: Unknown Elecraft modelnum=%c, expected 1 or 2\n",
                          __func__, modelnum);
                break;
            }

            if (strstr(buf, "P")) { priv->has_kpa100 = 1; }
        }
        else
        {
            model = "K3";

            if (strstr(buf, "R")) { model = "K3S"; }
        }

        rig_debug(RIG_DEBUG_TRACE,
                  "%s: model=%s, is_k2=%d, is_k3=%d, is_k3s=%d, is_kx3=%d, is_kx2=%d, is_k4=%d, is_k4d=%d, is_k4hd=%d, kpa3=%d\n",
                  __func__, model, priv->is_k2, priv->is_k3, priv->is_k3s, priv->is_kx3,
                  priv->is_kx2, priv->is_k4, priv->is_k4d,  priv->is_k4hd, priv->has_kpa3);

        err = elecraft_get_extension_level(rig, "K2", &priv->k2_ext_lvl);

        if (err != RIG_OK)
        {
            return err;
        }

        rig_debug(RIG_DEBUG_VERBOSE, "%s: K2 level is %d, %s\n", __func__,
                  priv->k2_ext_lvl, elec_ext_id_str_lst[priv->k2_ext_lvl].id);

        err = elecraft_get_extension_level(rig, "K3", &priv->k3_ext_lvl);

        if (err != RIG_OK)
        {
            return err;
        }

        rig_debug(RIG_DEBUG_VERBOSE, "%s: K3 level is %d, %s\n", __func__,
                  priv->k3_ext_lvl, elec_ext_id_str_lst[priv->k3_ext_lvl].id);

        err = elecraft_get_firmware_revision_level(rig, "RVM", priv->fw_rev,
                (sizeof(k3_fw_rev) / sizeof(k3_fw_rev[0])));

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: Firmware RVM failed\n", __func__);
            return err;
        }

        err = elecraft_get_firmware_revision_level(rig, "RVD", priv->fw_rev,
                (sizeof(k3_fw_rev) / sizeof(k3_fw_rev[0])));

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: Firmware RVD failed\n", __func__);
        }

        if (priv->is_k3)
        {
            err = elecraft_get_firmware_revision_level(rig, "RVA", priv->fw_rev,
                    (sizeof(k3_fw_rev) / sizeof(k3_fw_rev[0])));

            if (err != RIG_OK)
            {
                rig_debug(RIG_DEBUG_ERR, "%s: Firmware RVA failed\n", __func__);
            }

            err = elecraft_get_firmware_revision_level(rig, "RVR", priv->fw_rev,
                    (sizeof(k3_fw_rev) / sizeof(k3_fw_rev[0])));

            if (err != RIG_OK)
            {
                rig_debug(RIG_DEBUG_ERR, "%s: Firmware RVR failed\n", __func__);
            }

            err = elecraft_get_firmware_revision_level(rig, "RVF", priv->fw_rev,
                    (sizeof(k3_fw_rev) / sizeof(k3_fw_rev[0])));

            if (err != RIG_OK)
            {
                rig_debug(RIG_DEBUG_ERR, "%s: Firmware RVF failed\n", __func__);
            }
        }

        break;

    case RIG_MODEL_XG3:
        rig_debug(RIG_DEBUG_VERBOSE, "%s: XG3 level is %d, %s\n", __func__,
                  priv->k3_ext_lvl, elec_ext_id_str_lst[priv->k3_ext_lvl].id);
        break;

    default:
        rig_debug(RIG_DEBUG_WARN, "%s: unrecognized rig model %u\n",
                  __func__, rig->caps->rig_model);
        return -RIG_EINVAL;
    }

    if (RIG_MODEL_XG3 != rig->caps->rig_model)
    {
        /* get current AI state so it can be restored */
        priv->trn_state = -1;
        kenwood_get_trn(rig, &priv->trn_state);  /* ignore errors */
        /* Currently we cannot cope with AI mode so turn it off in
             case last client left it on */
        kenwood_set_trn(rig,
                        RIG_TRN_OFF); /* ignore status in case it's not supported */
    }

    // For rigs like K3X vfo emulation need to set VFO_A to start
    vfo_t vfo;
    rig_get_vfo(rig, &vfo);

    if (vfo != RIG_VFO_A && vfo != RIG_VFO_B)
    {
        rig_set_vfo(rig, RIG_VFO_A);
    }

    return RIG_OK;
}

int elecraft_close(RIG *rig)
{
    const struct kenwood_priv_data *priv = STATE(rig)->priv;

    if (priv->save_k2_ext_lvl >= 0)
    {
        int err;
        char cmd[32];
        sprintf(cmd, "K2%d;", priv->save_k2_ext_lvl);
        err = kenwood_transaction(rig, cmd, NULL, 0);

        if (err != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: error restoring %s='%s'...continuing\n", __func__,
                      cmd,
                      rigerror(err));
        }
    }

    return kenwood_close(rig);
}

/* Private helper functions */

/* Tests for Kenwood ID string of "017" */

static int verify_kenwood_id(RIG *rig, char *id)
{
    int err;
    const char *idptr;

    rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);

    if (!id)
    {
        return -RIG_EINVAL;
    }

    /* Check for an Elecraft K2|K3 which returns "017" */
    err = kenwood_get_id(rig, id);

    if (err != RIG_OK)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: cannot get identification\n", __func__);
        return err;
    }

    /* ID is 'ID017;' */
    if (strlen(id) < 5)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: unknown ID type (%s)\n", __func__, id);
        return -RIG_EPROTO;
    }

    /* check for any white space and skip it */
    idptr = &id[2];

    if (*idptr == ' ')
    {
        idptr++;
    }

    if (strcmp("017", idptr) != 0)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: Rig (%.4095s) is not a K2 or K3\n", __func__,
                  id);
//        return -RIG_EPROTO;
    }
    else
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: Rig ID is %.4095s\n", __func__, id);
    }

    return RIG_OK;
}


/* Determines K2 and K3 extension level */

static int elecraft_get_extension_level(RIG *rig, const char *cmd, int *ext_level)
{
    int err, i;
    char buf[KENWOOD_MAX_BUF_LEN];
    const char *bufptr;

    rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);

    if (!ext_level)
    {
        return -RIG_EINVAL;
    }

    err = kenwood_safe_transaction(rig, cmd, buf, KENWOOD_MAX_BUF_LEN, 3);

    if (err != RIG_OK)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: Cannot get K2|K3 ID\n", __func__);
        return err;
    }

    /* Get extension level string */
    bufptr = &buf[0];

    for (i = 0; elec_ext_id_str_lst[i].level != EXT_LEVEL_NONE; i++)
    {
        if (strcmp(elec_ext_id_str_lst[i].id, bufptr) == 0)
        {
            *ext_level = elec_ext_id_str_lst[i].level;
            rig_debug(RIG_DEBUG_VERBOSE, "%s: %s extension level is %d, %s\n",
                      __func__, cmd, *ext_level, elec_ext_id_str_lst[i].id);
        }
    }

    return RIG_OK;
}

/* Determine firmware revision level */

static int elecraft_get_firmware_revision_level(RIG *rig, const char *cmd,
        char *fw_rev, size_t fw_rev_sz)
{
    int err;
    char *bufptr;
    char buf[KENWOOD_MAX_BUF_LEN];
    char rvp = cmd[2];
    char *rv = "UNK";

    if (rig->caps->rig_model == RIG_MODEL_K4)
    {
        switch (rvp)
        {
        case 'F':
        case 'M': rv = "FPF"; break;

        case 'A':
        case 'D': rv = "DSP"; break;

        case 'R': rv = "DAP"; break;
        }
    }
    else
    {
        switch (rvp)
        {
        case 'M': rv = "MCU"; break;

        case 'D': rv = "DSP"; break;

        case 'A': rv = "AUX"; break;

        case 'R': rv = "DVR"; break;

        case 'F': rv = "FPF"; break;
        }
    }

    rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);

    if (!fw_rev)
    {
        return -RIG_EINVAL;
    }

    /* Get the actual firmware revision number. */
    err = kenwood_transaction(rig, cmd, buf, sizeof(buf));

    if (err != RIG_OK)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: Cannot get firmware revision level\n",
                  __func__);
        return err;
    }

    /* Now buf[] contains the string from the K3 which includes the command
     * and the firmware revision number as:  "RVM04.67".
     */

    bufptr = &buf[0];

    /* Skip the command string */
    bufptr += strlen(cmd);

    /* Skip leading zero(s) as the revision number has the format of: "04.67" */
    while (*bufptr == '0') { bufptr++; }

    /* Copy out */
    strncpy(fw_rev, bufptr, fw_rev_sz - 1);

    rig_debug(RIG_DEBUG_VERBOSE, "%s: Elecraft %s firmware revision is %s\n",
              __func__,
              rv, fw_rev);

    return RIG_OK;
}

//  FR;FT;TQ; is faster than IF;
//  Works on K4
int elecraft_get_vfo_tq(RIG *rig, vfo_t *vfo)
{
    int retval;
    int fr, ft, tq;
    char cmdbuf[10];
    char splitbuf[12];

    ENTERFUNC2;

    memset(splitbuf, 0, sizeof(splitbuf));
    SNPRINTF(cmdbuf, sizeof(cmdbuf), "FR;");
    retval = kenwood_safe_transaction(rig, cmdbuf, splitbuf, 12, 3);

    if (retval != RIG_OK)
    {
        RETURNFUNC2(retval);
    }

    if (sscanf(splitbuf, "FR%1d", &fr) != 1)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: unable to parse FR '%s'\n", __func__, splitbuf);
    }

    SNPRINTF(cmdbuf, sizeof(cmdbuf), "FT;");
    retval = kenwood_safe_transaction(rig, cmdbuf, splitbuf, 12, 3);

    if (retval != RIG_OK)
    {
        RETURNFUNC2(retval);
    }

    if (sscanf(splitbuf, "FT%1d", &ft) != 1)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: unable to parse FT '%s'\n", __func__, splitbuf);
    }

// We can use the TQX; command but we have to check that we have R32 firmware or higher
// Not sure it's much better than TQ; since it still seems to take 350ms
// So we have to sleep for a while after sending it to avoid TCP timeouts which still needs to be fixed
// See

#if 0

    if (rig->caps->rig_model == RIG_MODEL_K4)
    {
        SNPRINTF(cmdbuf, sizeof(cmdbuf), "TQX;");
    }
    else
#endif
    {
        SNPRINTF(cmdbuf, sizeof(cmdbuf), "TQ;");
    }

    retval = kenwood_safe_transaction(rig, cmdbuf, splitbuf, 12, 3);

    if (retval != RIG_OK)
    {
        RETURNFUNC2(retval);
    }

    if (sscanf(splitbuf, "TQ%1d", &tq) != 1)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: unable to parse TQ or TQX response of '%s'\n",
                  __func__, splitbuf);
    }

    *vfo = STATE(rig)->tx_vfo = RIG_VFO_A;

    if (tq && ft == 1) { *vfo = STATE(rig)->tx_vfo = RIG_VFO_B; }
    else if (tq && ft == 0) { *vfo = STATE(rig)->tx_vfo = RIG_VFO_A; }

    if (!tq && fr == 1) { *vfo = STATE(rig)->rx_vfo = STATE(rig)->tx_vfo = RIG_VFO_B; }

    RETURNFUNC2(RIG_OK);
}

