/*
 *  Hamlib Barrett 950 backend - main file
 *  Copyright (c) 2017-2020 by Michael Black W9MDB
 *
 *
 *   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
 *
 */

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

#include "hamlib/rig.h"

#include "barrett.h"

#define MAXCMDLEN 32

#define BARRETT950_VFOS (RIG_VFO_A|RIG_VFO_MEM)

#define BARRETT950_MODES (RIG_MODE_AM | RIG_MODE_CW | RIG_MODE_RTTY | RIG_MODE_SSB)

#define BARRETT950_LEVELS (RIG_LEVEL_NONE)


int barrett950_set_freq(RIG *rig, vfo_t vfo, freq_t freq);

static int barrett950_get_level(RIG *rig, vfo_t vfo, setting_t level,
                                value_t *val);

static const char *barrett950_get_info(RIG *rig);

// 10 band channel from 441 to 450
#define CHANNEL_BASE 441

struct chan_map_s
{
    float lo, hi;
    int chan_offset;
};

// Our 10 bands
static struct chan_map_s chan_map[] =
{
    { 0, 3.5, 0},
    { 3.5, 5.3, 1},
    { 5.3, 7.0, 2},
    { 7.0, 10.1, 3},
    { 10.1, 14.0, 4},
    { 14.0, 18.068, 5},
    { 18.068, 21.0, 6},
    { 21.0, 24.89, 7},
    { 24.89, 28.0, 8},
    { 28.0, 30.0, 9}
};

struct rig_caps barrett950_caps =
{
    RIG_MODEL(RIG_MODEL_BARRETT_950),
    .model_name =       "950",
    .mfg_name =         "Barrett",
    .version =          BACKEND_VER ".1",
    .copyright =        "LGPL",
    .status =           RIG_STATUS_STABLE,
    .rig_type =         RIG_TYPE_TRANSCEIVER,
    .targetable_vfo =   RIG_TARGETABLE_FREQ | RIG_TARGETABLE_MODE,
    .ptt_type =         RIG_PTT_RIG,
    .dcd_type =         RIG_DCD_NONE,
    .port_type =        RIG_PORT_SERIAL,
    .serial_rate_min =  9600,
    .serial_rate_max =  9600,
    .serial_data_bits = 8,
    .serial_stop_bits = 1,
    .serial_parity =    RIG_PARITY_NONE,
    .serial_handshake = RIG_HANDSHAKE_XONXOFF,
    .write_delay =      0,
    .post_write_delay = 50,
    .timeout =          1000,
    .retry =            3,

    .has_get_func =     RIG_FUNC_NONE,
    .has_set_func =     RIG_FUNC_NONE,
    .has_get_level =    BARRETT950_LEVELS,
    .has_set_level =    RIG_LEVEL_NONE,
    .has_get_parm =     RIG_PARM_NONE,
    .has_set_parm =     RIG_PARM_NONE,
    .transceive =       RIG_TRN_RIG,
    .rx_range_list1 = {{
            .startf = kHz(1600), .endf = MHz(30), .modes = BARRETT950_MODES,
            .low_power = -1, .high_power = -1, BARRETT950_VFOS, RIG_ANT_1
        },
        RIG_FRNG_END,
    },
    .rx_range_list2 = {RIG_FRNG_END,},
    .tx_range_list1 = {RIG_FRNG_END,},
    .tx_range_list2 = {RIG_FRNG_END,},
    .tuning_steps =  { {BARRETT950_MODES, 1}, {BARRETT950_MODES, RIG_TS_ANY}, RIG_TS_END, },
    .filters = {
        {RIG_MODE_SSB | RIG_MODE_CW | RIG_MODE_RTTY, kHz(2.4)},
        {RIG_MODE_CW, Hz(500)},
        {RIG_MODE_AM, kHz(8)},
        {RIG_MODE_AM, kHz(2.4)},
        RIG_FLT_END,
    },
    .priv = NULL,

    .rig_init =     barrett_init,
    .rig_cleanup =  barrett_cleanup,

    .set_freq = barrett950_set_freq,
    .get_freq = barrett_get_freq,
    .set_mode = barrett_set_mode,
    .get_mode = barrett_get_mode,

    .get_level =    barrett950_get_level,

    .get_info =     barrett950_get_info,
    .set_ptt =      barrett_set_ptt,
    .get_ptt =      NULL,
    .set_split_freq =   barrett_set_split_freq,
    .set_split_vfo =    barrett_set_split_vfo,
    .get_split_vfo =    barrett_get_split_vfo,
    .hamlib_check_rig_caps = HAMLIB_CHECK_RIG_CAPS
};

/*
 * barrett950_set_freq
 * assumes rig!=NULL, STATE(rig)->priv!=NULL
 */
int barrett950_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
    char cmd_buf[MAXCMDLEN];
    int retval;
    int i;
    int chan = -1;
    freq_t freq_rx, freq_tx;
    freq_t freq_MHz;
    char *response = NULL;
    const struct barrett_priv_data *priv = STATE(rig)->priv;
    //struct barrett_priv_data *priv = STATE(rig)->priv;

    rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s freq=%.0f\n", __func__,
              rig_strvfo(vfo), freq);

    // 950 can only set freq via memory channel
    // So we make a 10-channel memory from 441-450 by band
    // And we don't care about VFO -- we set TX=RX to avoid doing split freq changes
    // Trying to minimize writes to EEPROM

    // What band is being requested?
    freq_MHz = freq / 1e6;

    for (i = 0; i < 10; ++i)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: MHz=%lg, lo=%lg, hi=%lg\n", __func__,
                  freq_MHz, chan_map[i].lo, chan_map[i].hi);

        if (freq_MHz >= chan_map[i].lo && freq_MHz <= chan_map[i].hi)
        {
            int channel_base = priv->channel_base;
            chan = channel_base + chan_map[i].chan_offset;
        }
    }

    rig_debug(RIG_DEBUG_VERBOSE, "%s: using chan %d for freq %.0f \n", __func__,
              chan, freq);

    // Set the channel
    SNPRINTF((char *) cmd_buf, sizeof(cmd_buf), "XC%04d", chan);
    retval = barrett_transaction(rig, cmd_buf, 0, &response);

    if (retval < 0)
    {
        return retval;
    }

    // Read the current channel for the requested freq to see if it needs changing
    SNPRINTF((char *) cmd_buf, sizeof(cmd_buf), "IDC%04d", chan);
    retval = barrett_transaction(rig, cmd_buf, 0, &response);

    if (retval < 0)
    {
        return retval;
    }

    if (strstr(response, "E5"))
    {
        freq_rx = freq_tx = 0;
        rig_debug(RIG_DEBUG_VERBOSE, "%s: new channel being programmed\n", __func__);
    }
    else if (sscanf(response, "%4d%8lf%8lf", &chan, &freq_rx, &freq_tx) != 3)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: unable to parse chan/freq from %s\n", __func__,
                  response);
        return -RIG_EPROTO;
    }

    rig_debug(RIG_DEBUG_VERBOSE, "%s: got chan %d, freq_rx=%.0f, freq_tx=%.0f",
              __func__, chan,
              freq_rx, freq_tx);

    if (freq_rx == freq && freq_tx == freq)
    {
        rig_debug(RIG_DEBUG_VERBOSE, "%s: no freq change needed\n", __func__);
        return RIG_OK;
    }

    // New freq so let's update the channel
    // We do not support split mode -- too many writes to EEPROM to support it
    SNPRINTF((char *) cmd_buf, sizeof(cmd_buf), "PC%04dR%08.0lfT%08.0lf", chan,
             freq, freq);
    retval = barrett_transaction(rig, cmd_buf, 0, &response);

    if (retval != RIG_OK || strncmp(response, "OK", 2) != 0)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: Expected OK, got '%s'\n", __func__, response);
        return -RIG_EPROTO;
    }

    return RIG_OK;
}

/*
 * barrett950_get_level
 */
int barrett950_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
{
    return -RIG_ENIMPL;
}

/*
 * barrett950_get_info
 */
const char *barrett950_get_info(RIG *rig)
{
    char *response = NULL;
    int retval;

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

    retval = barrett_transaction(rig, "IV", 0, &response);

    if (retval == RIG_OK)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: result=%s\n", __func__, response);
    }
    else
    {
        rig_debug(RIG_DEBUG_VERBOSE, "Software Version %s\n", response);
    }

    return response;
}
