//
// Copyright 2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see .
//
#ifndef INCLUDED_N230_RESOURCE_MANAGER_HPP
#define INCLUDED_N230_RESOURCE_MANAGER_HPP
#include "radio_ctrl_core_3000.hpp"
#include "spi_core_3000.hpp"
#include "gpio_atr_3000.hpp"
#include "rx_vita_core_3000.hpp"
#include "tx_vita_core_3000.hpp"
#include "time_core_3000.hpp"
#include "rx_dsp_core_3000.hpp"
#include "tx_dsp_core_3000.hpp"
#include "user_settings_core_3000.hpp"
#include "ad9361_ctrl.hpp"
#include "ad936x_manager.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "usrp3_fw_ctrl_iface.hpp"
#include "n230_clk_pps_ctrl.hpp"
#include "n230_cores.hpp"
#include "n230_fpga_defs.h"
#include "n230_frontend_ctrl.hpp"
#include "n230_uart.hpp"
namespace uhd { namespace usrp { namespace n230 {
enum n230_eth_port_t {
    ETH0,
    ETH1
};
enum n230_eth_pref_t {
    PRI_ETH,
    SEC_ETH
};
enum n230_endpoint_t {
    RADIO_TX_DATA,
    RADIO_RX_DATA,
    RADIO_CONTROL,
    CORE,
    GPS_UART
};
enum n230_ver_src_t {
    SOFTWARE,
    FIRMWARE,
    FPGA
};
enum n230_version_t {
    COMPAT_MAJOR,
    COMPAT_MINOR
};
enum n230_data_dir_t {
    RX_DATA, TX_DATA
};
//Radio resources
class radio_resource_t : public boost::noncopyable {
public:
    radio_ctrl_core_3000::sptr      ctrl;
    gpio_atr::gpio_atr_3000::sptr   gpio_atr;
    time_core_3000::sptr            time;
    rx_vita_core_3000::sptr         framer;
    rx_dsp_core_3000::sptr          ddc;
    tx_vita_core_3000::sptr         deframer;
    tx_dsp_core_3000::sptr          duc;
    user_settings_core_3000::sptr   user_settings;
};
class n230_resource_manager : public boost::noncopyable
{
public:     //Methods
    n230_resource_manager(const std::vector ip_addrs, const bool safe_mode);
    virtual ~n230_resource_manager();
    static bool is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl);
    inline bool is_device_claimed() {
        if (_fw_ctrl.get()) {
            return is_device_claimed(_fw_ctrl);
        } else {
            return false;
        }
    }
    inline boost::uint32_t get_version(n230_ver_src_t src, n230_version_t type) {
        switch (src) {
            case FPGA:      return _fpga_version.get(type);
            case FIRMWARE:  return _fw_version.get(type);
            default:        return 0;
        }
    }
    inline const std::string get_version_hash(n230_ver_src_t src) {
        switch (src) {
            case FPGA:      return _fpga_version.get_hash_str();
            case FIRMWARE:  return _fw_version.get_hash_str();
            default:        return "";
        }
    }
    //Firmware control interface
    inline wb_iface& get_fw_ctrl() const {
        return *_fw_ctrl;
    }
    inline wb_iface::sptr get_fw_ctrl_sptr() {
        return _fw_ctrl;
    }
    //Core settings control interface
    inline radio_ctrl_core_3000& get_core_ctrl() const {
        return *_core_ctrl;
    }
    inline radio_ctrl_core_3000::sptr get_core_ctrl_sptr() {
        return _core_ctrl;
    }
    //AD931 control interface
    inline ad9361_ctrl& get_codec_ctrl() const {
        return *_codec_ctrl;
    }
    inline ad9361_ctrl::sptr get_codec_ctrl_sptr() {
        return _codec_ctrl;
    }
    inline uhd::usrp::ad936x_manager& get_codec_mgr() const {
        return *_codec_mgr;
    }
    //Clock PPS controls
    inline n230_ref_pll_ctrl& get_ref_pll_ctrl() const {
        return *_ref_pll_ctrl;
    }
    inline n230_ref_pll_ctrl::sptr get_ref_pll_ctrl_sptr() {
        return _ref_pll_ctrl;
    }
    //Clock PPS controls
    inline n230_clk_pps_ctrl& get_clk_pps_ctrl() const {
        return *_clk_pps_ctrl;
    }
    inline n230_clk_pps_ctrl::sptr get_clk_pps_ctrl_sptr() {
        return _clk_pps_ctrl;
    }
    //Front-end control
    inline n230_frontend_ctrl& get_frontend_ctrl() const {
        return *_frontend_ctrl;
    }
    inline n230_frontend_ctrl::sptr get_frontend_ctrl_sptr() {
        return _frontend_ctrl;
    }
    //MiniSAS GPIO control
    inline gpio_atr::gpio_atr_3000::sptr get_minisas_gpio_ctrl_sptr(size_t idx) {
        return idx == 0 ? _ms0_gpio : _ms1_gpio;
    }
    inline gpio_atr::gpio_atr_3000& get_minisas_gpio_ctrl(size_t idx) {
        return *get_minisas_gpio_ctrl_sptr(idx);
    }
    //GPSDO control
    inline bool is_gpsdo_present() {
        return _gps_ctrl.get() and _gps_ctrl->gps_detected();
    }
    inline uhd::gps_ctrl::sptr get_gps_ctrl(void) {
        return _gps_ctrl;
    }
    inline radio_resource_t& get_radio(size_t instance) {
        return _radios[instance];
    }
    //Transport to stream data
    transport::zero_copy_if::sptr create_transport(
        n230_data_dir_t direction, size_t radio_instance,
        const device_addr_t ¶ms, sid_t& sid,
        transport::udp_zero_copy::buff_params& buff_out_params);
    //Misc
    inline double get_max_link_rate() {
        return fpga::N230_LINK_RATE_BPS * _eth_conns.size();
    }
private:
    struct ver_info_t {
        boost::uint8_t  compat_major;
        boost::uint16_t compat_minor;
        boost::uint32_t version_hash;
        boost::uint32_t get(n230_version_t type) {
            switch (type) {
                case COMPAT_MAJOR: return compat_major;
                case COMPAT_MINOR: return compat_minor;
                default:           return 0;
            }
        }
        const std::string get_hash_str() {
            return (str(boost::format("%07x%s")
                % (version_hash & 0x0FFFFFFF)
                % ((version_hash & 0xF0000000) ? "(modified)" : "")));
        }
    };
    struct n230_eth_conn_t {
        std::string ip_addr;
        n230_eth_port_t type;
    };
    //-- Functions --
    void _claimer_loop();
    void _initialize_radio(size_t instance);
    std::string _get_fpga_upgrade_msg();
    void _check_fw_compat();
    void _check_fpga_compat();
    const sid_t _generate_sid(
        const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance = 0);
    transport::zero_copy_if::sptr _create_transport(
        const n230_eth_conn_t& eth_conn,
        const sid_t& sid, const device_addr_t &buff_params,
        transport::udp_zero_copy::buff_params& buff_params_out);
    void _program_dispatcher(
        transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid);
    void _reset_codec_digital_interface();
    bool _radio_register_loopback_self_test(wb_iface::sptr iface);
    bool _radio_data_loopback_self_test(wb_iface::sptr iface);
    inline const n230_eth_conn_t& _get_conn(const n230_eth_pref_t pref) {
        if (_eth_conns.size() == 1)
            return _eth_conns[0];
        else
            return _eth_conns[(pref==PRI_ETH)?0:1];
    }
    //-- Members --
    std::vector    _eth_conns;
    const bool                      _safe_mode;
    ver_info_t                      _fw_version;
    ver_info_t                      _fpga_version;
    //Firmware register interface
    uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr   _fw_ctrl;
    uhd::task::sptr                 _claimer_task;
    static boost::mutex             _claimer_mutex;  //All claims and checks in this process are serialized
    //Transport
    boost::uint8_t                  _last_host_enpoint;
    //Radio settings interface
    radio_ctrl_core_3000::sptr      _core_ctrl;
    n230_core_spi_core::sptr        _core_spi_ctrl;
    ad9361_ctrl::sptr               _codec_ctrl;
    uhd::usrp::ad936x_manager::sptr _codec_mgr;
    //Core Registers
    fpga::core_radio_ctrl_reg_t     _core_radio_ctrl_reg;
    fpga::core_misc_reg_t           _core_misc_reg;
    fpga::core_pps_sel_reg_t        _core_pps_sel_reg;
    fpga::core_status_reg_t         _core_status_reg;
    //Radio peripherals
    radio_resource_t                _radios[fpga::NUM_RADIOS];
    //Misc IO peripherals
    n230_ref_pll_ctrl::sptr         _ref_pll_ctrl;
    n230_clk_pps_ctrl::sptr         _clk_pps_ctrl;
    n230_frontend_ctrl::sptr        _frontend_ctrl;
    //miniSAS GPIO
    gpio_atr::gpio_atr_3000::sptr   _ms0_gpio;
    gpio_atr::gpio_atr_3000::sptr   _ms1_gpio;
    //GPSDO
    n230_uart::sptr                 _gps_uart;
    uhd::gps_ctrl::sptr             _gps_ctrl;
};
}}} //namespace
#endif //INCLUDED_N230_RESOURCE_MANAGER_HPP