//
// Copyright 2017 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include "ad937x_gain_ctrl_config.hpp"

using namespace mpm::ad937x::gpio;
using namespace mpm::ad937x::device;
using namespace uhd;

const uint8_t ad937x_gain_ctrl_channel_t::DEFAULT_GAIN_STEP = 1;
const bool ad937x_gain_ctrl_channel_t::DEFAULT_ENABLE       = 0;

// rx uses gain, tx uses attenuation
enum class pin_direction_t {
    INCREASE,
    DECREASE,
};

mykonosGpioSelect_t _convert_gain_pin(gain_pin_t pin)
{
    switch (pin) {
        case gain_pin_t::PIN0:
            return MYKGPIO0;
        case gain_pin_t::PIN1:
            return MYKGPIO1;
        case gain_pin_t::PIN2:
            return MYKGPIO2;
        case gain_pin_t::PIN3:
            return MYKGPIO3;
        case gain_pin_t::PIN4:
            return MYKGPIO4;
        case gain_pin_t::PIN5:
            return MYKGPIO5;
        case gain_pin_t::PIN6:
            return MYKGPIO6;
        case gain_pin_t::PIN7:
            return MYKGPIO7;
        case gain_pin_t::PIN8:
            return MYKGPIO8;
        case gain_pin_t::PIN9:
            return MYKGPIO9;
        case gain_pin_t::PIN10:
            return MYKGPIO10;
        case gain_pin_t::PIN11:
            return MYKGPIO11;
        case gain_pin_t::PIN12:
            return MYKGPIO12;
        case gain_pin_t::PIN13:
            return MYKGPIO13;
        case gain_pin_t::PIN14:
            return MYKGPIO14;
        case gain_pin_t::PIN15:
            return MYKGPIO15;
        case gain_pin_t::PIN16:
            return MYKGPIO16;
        case gain_pin_t::PIN17:
            return MYKGPIO17;
        case gain_pin_t::PIN18:
            return MYKGPIO18;
        default:
            return MYKGPIONAN;
    }
}

ad937x_gain_ctrl_channel_t::ad937x_gain_ctrl_channel_t(
    mykonosGpioSelect_t inc_pin, mykonosGpioSelect_t dec_pin)
    : enable(DEFAULT_ENABLE)
    , inc_step(DEFAULT_GAIN_STEP)
    , dec_step(DEFAULT_GAIN_STEP)
    , inc_pin(inc_pin)
    , dec_pin(dec_pin)
{
}

mykonosGpioSelect_t _get_gain_pin(direction_t direction,
    chain_t chain,
    pin_direction_t pin_direction,
    const gain_pins_t& gain_pins)
{
    switch (direction) {
        case RX_DIRECTION:
            switch (chain) {
                case chain_t::ONE:
                    switch (pin_direction) {
                        case pin_direction_t::INCREASE:
                            return _convert_gain_pin(gain_pins.rx1_inc_gain_pin);
                        case pin_direction_t::DECREASE:
                            return _convert_gain_pin(gain_pins.rx1_dec_gain_pin);
                    }
                case chain_t::TWO:
                    switch (pin_direction) {
                        case pin_direction_t::INCREASE:
                            return _convert_gain_pin(gain_pins.rx2_inc_gain_pin);
                        case pin_direction_t::DECREASE:
                            return _convert_gain_pin(gain_pins.rx2_dec_gain_pin);
                    }
            }

            // !!! TX is attenuation direction, so the pins are flipped !!!
        case TX_DIRECTION:
            switch (chain) {
                case chain_t::ONE:
                    switch (pin_direction) {
                        case pin_direction_t::INCREASE:
                            return _convert_gain_pin(gain_pins.tx1_dec_gain_pin);
                        case pin_direction_t::DECREASE:
                            return _convert_gain_pin(gain_pins.tx1_inc_gain_pin);
                    }
                case chain_t::TWO:
                    switch (pin_direction) {
                        case pin_direction_t::INCREASE:
                            return _convert_gain_pin(gain_pins.tx2_dec_gain_pin);
                        case pin_direction_t::DECREASE:
                            return _convert_gain_pin(gain_pins.tx2_inc_gain_pin);
                    }
            }

        default:
            return MYKGPIONAN;
    }
}

ad937x_gain_ctrl_config_t::ad937x_gain_ctrl_config_t(gain_pins_t gain_pins)
{
    config.emplace(std::piecewise_construct,
        std::forward_as_tuple(RX_DIRECTION),
        std::forward_as_tuple());
    config.emplace(std::piecewise_construct,
        std::forward_as_tuple(TX_DIRECTION),
        std::forward_as_tuple());

    config.at(RX_DIRECTION)
        .emplace(std::piecewise_construct,
            std::forward_as_tuple(chain_t::ONE),
            std::forward_as_tuple(
                _get_gain_pin(
                    RX_DIRECTION, chain_t::ONE, pin_direction_t::INCREASE, gain_pins),
                _get_gain_pin(
                    RX_DIRECTION, chain_t::ONE, pin_direction_t::DECREASE, gain_pins)));
    config.at(RX_DIRECTION)
        .emplace(std::piecewise_construct,
            std::forward_as_tuple(chain_t::TWO),
            std::forward_as_tuple(
                _get_gain_pin(
                    RX_DIRECTION, chain_t::TWO, pin_direction_t::INCREASE, gain_pins),
                _get_gain_pin(
                    RX_DIRECTION, chain_t::TWO, pin_direction_t::DECREASE, gain_pins)));

    config.at(TX_DIRECTION)
        .emplace(std::piecewise_construct,
            std::forward_as_tuple(chain_t::ONE),
            std::forward_as_tuple(
                _get_gain_pin(
                    TX_DIRECTION, chain_t::ONE, pin_direction_t::INCREASE, gain_pins),
                _get_gain_pin(
                    TX_DIRECTION, chain_t::ONE, pin_direction_t::DECREASE, gain_pins)));
    config.at(TX_DIRECTION)
        .emplace(std::piecewise_construct,
            std::forward_as_tuple(chain_t::TWO),
            std::forward_as_tuple(
                _get_gain_pin(
                    TX_DIRECTION, chain_t::TWO, pin_direction_t::INCREASE, gain_pins),
                _get_gain_pin(
                    TX_DIRECTION, chain_t::TWO, pin_direction_t::DECREASE, gain_pins)));
}