#include <shared_mutex>
#include "terminal_api/ControlProtoCInterface.h"
#include "system_client.h"

std::shared_mutex mtx;
TSID sid_counter { 0 };
std::map<TSID, std::unique_ptr<system_client>> clients;

EXTERNC CP_Result CP_SetRollofBaudrate(TSID sid, double rollof, double baudrate)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;
        auto resp = clients[sid]->send_rollof_and_baudrate(rollof, baudrate);
        if (resp == response_type::error)
            return ERROR;
        return OK;
    }

    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetQoSParams(TSID sid, const std::string &type_node, const std::string & node)
{
    std::shared_lock lock(mtx);
    name_classes_qos name_class;
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;
        if(type_node == "realtime1")
        {
            name_class = name_classes_qos::realtime1;
        }
        else if(type_node == "realtime2")
        {
            name_class = name_classes_qos::realtime2;
        }
        else if(type_node == "realtime3")
        {
            name_class = name_classes_qos::realtime3;
        }
        else if(type_node == "critical1")
        {
            name_class = name_classes_qos::critical1;
        }
        else if(type_node == "critical2")
        {
            name_class = name_classes_qos::critical2;
        }
        else if(type_node == "critical3")
        {
            name_class = name_classes_qos::critical3;
        }
        else if(type_node == "cir")
        {
            name_class = name_classes_qos::cir;
        }
        else if(type_node == "pir")
        {
            name_class = name_classes_qos::pir;
        }
        else if(type_node == "wcr1")
        {
            name_class = name_classes_qos::wcr1;
        }
        else if(type_node == "wcr2")
        {
            name_class = name_classes_qos::wcr2;
        }
        else if(type_node == "wcr3")
        {
            name_class = name_classes_qos::wcr3;
        }
        else if(type_node == "enable")
        {
            name_class = name_classes_qos::enable;
        }
        else if(type_node == "ful_node")
        {
            name_class = name_classes_qos::ful_node;
        }

        auto resp = clients[sid]->send_set_qos_params(node, name_class);
        if (resp == response_type::error)
            return ERROR;
        return OK;
    }

    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetQoSParams(TSID sid,  const std::string &type_node, std::string * node)
{
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;

        std::string node_ = std::string();
        name_classes_qos name_class;
        if(type_node == "realtime1")
        {
            name_class = name_classes_qos::realtime1;
        }
        else if(type_node == "realtime2")
        {
            name_class = name_classes_qos::realtime2;
        }
        else if(type_node == "realtime3")
        {
            name_class = name_classes_qos::realtime3;
        }
        else if(type_node == "critical1")
        {
            name_class = name_classes_qos::critical1;
        }
        else if(type_node == "critical2")
        {
            name_class = name_classes_qos::critical2;
        }
        else if(type_node == "critical3")
        {
            name_class = name_classes_qos::critical3;
        }
        else if(type_node == "cir")
        {
            name_class = name_classes_qos::cir;
        }
        else if(type_node == "pir")
        {
            name_class = name_classes_qos::pir;
        }
        else if(type_node == "wcr1")
        {
            name_class = name_classes_qos::wcr1;
        }
        else if(type_node == "wcr2")
        {
            name_class = name_classes_qos::wcr2;
        }
        else if(type_node == "wcr3")
        {
            name_class = name_classes_qos::wcr3;
        }
        else if(type_node == "enable")
        {
            name_class = name_classes_qos::enable;
        }
        else if(type_node == "ful_node")
        {
            name_class = name_classes_qos::ful_node;
        }

        auto resp = clients[sid]->send_get_qos_params(node_, name_class);
        if (resp == response_type::error)
            return ERROR;
        *node = node_;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetDpdiParams(TSID sid, DPDI_parmeters dpdi_params)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;

        dpdi_parameters dpdi_pars;
        dpdi_pars.is_delay_window = dpdi_params.is_delay_window;
        dpdi_pars.max_delay = dpdi_params.max_delay;
        dpdi_pars.min_delay = dpdi_params.min_delay;
        dpdi_pars.freq_offset = dpdi_params.freq_offset;
        dpdi_pars.latitude_station_grad = dpdi_params.latitude_station_grad;
        dpdi_pars.latitude_station_minute = dpdi_params.latitude_station_minute;
        dpdi_pars.longitude_sattelite_grad = dpdi_params.longitude_sattelite_grad;
        dpdi_pars.longitude_sattelite_minute = dpdi_params.longitude_sattelite_minute;
        dpdi_pars.longitude_station_grad = dpdi_params.longitude_station_grad;
        dpdi_pars.longitude_station_minute = dpdi_params.longitude_station_minute;
        auto resp = clients[sid]->send_set_dpdi_params(dpdi_pars);
        if (resp == response_type::error)
            return ERROR;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetNetwork(TSID sid, const char *param_name, const char *val)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        network_value net_val;
        std::string cmd(param_name);
        if(cmd == "mode")
            net_val = network_value::mode_interface;
        else if(cmd == "addr")
            net_val = network_value::network;
        else if(cmd == "mask")
            net_val = network_value::mask;
        else if(cmd == "gateway")
            net_val = network_value::gateway;
        else if(cmd == "dhcp_on")
            net_val = network_value::dhcp_on;
        else if(cmd == "dhcp_range")
            net_val = network_value::dhcp_range;
        else if(cmd == "addr_data")
            net_val = network_value::network_data;
        else if(cmd == "name_serv")
            net_val = network_value::name_serv;
        else if(cmd == "network_debug_send")
            net_val = network_value::network_debug_send;
        else if(cmd == "network_port_metric")
            net_val = network_value::network_port_metric;
        else if(cmd == "network_port_data")
            net_val = network_value::network_port_data;
        else if(cmd == "if_debug_mode")
            net_val = network_value::if_debug_mode;
        else if(cmd == "periodic_send_metrics")
            net_val = network_value::periodic_send_metrics;

        auto resp = clients[sid]->send_network_settings(net_val, val);
        if (resp == response_type::error)
            return ERROR;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetNetwork(TSID sid, const char *param_name, std::string *val)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        network_value net_val;
        std::string cmd(param_name);
        std::string val_from_server;
        if(cmd == "mode")
            net_val = network_value::mode_interface;
        else if(cmd == "addr")
            net_val = network_value::network;
        else if(cmd == "version")
            net_val = network_value::version;
        else if(cmd == "chip_id")
            net_val = network_value::chip_id;
        else if(cmd == "mask")
            net_val = network_value::mask;
        else if(cmd == "gateway")
            net_val = network_value::gateway;
        else if(cmd == "dhcp_on")
            net_val = network_value::dhcp_on;
        else if(cmd == "dhcp_range")
            net_val = network_value::dhcp_range;
        else if(cmd == "addr_data")
            net_val = network_value::network_data;
        else if(cmd == "serial")
            net_val = network_value::serial;
        else if(cmd == "mac_eth0")
            net_val = network_value::mac_eth0;
        else if(cmd == "mac_eth1")
            net_val = network_value::mac_eth1;
        else if(cmd == "name_serv")
            net_val = network_value::name_serv;

        auto resp = clients[sid]->send_get_network_settings(net_val, val_from_server);
        if (resp == response_type::error)
            return ERROR;
        *val = val_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetLBQParams(TSID sid, const uint32_t &tick_ms, const uint32_t &bucket_size)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return ERROR;
        auto resp = clients[sid]->send_set_lbq_params(tick_ms, bucket_size);

        if (resp == response_type::error)
            return ERROR;

        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetDmaDebug(TSID sid, const char *command, std::string val)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;
        cmd_dma_debugg dma_debugg;
        std::string param_name(command);

        if (param_name == "start")
            dma_debugg = cmd_dma_debugg::start;
        else if (param_name == "stop")
            dma_debugg = cmd_dma_debugg::stop;
        else if (param_name == "reset")
            dma_debugg = cmd_dma_debugg::reset;
        else if (param_name == "reinit")
            dma_debugg = cmd_dma_debugg::reinit;
        else if (param_name == "modcod")
            dma_debugg = cmd_dma_debugg::modcod;
        else if (param_name == "log_bool")
            dma_debugg = cmd_dma_debugg::log_bool;
        else if (param_name == "data_mode")
            dma_debugg = cmd_dma_debugg::data_mode;
        else if (param_name == "buc_voltage")
            dma_debugg = cmd_dma_debugg::buc_voltage;
        else if (param_name == "lnb_voltage")
            dma_debugg = cmd_dma_debugg::lnb_voltage;
        else if (param_name == "10MHz_tx")
            dma_debugg = cmd_dma_debugg::_10mhz_tx;
        else if (param_name == "10MHz_rx")
            dma_debugg = cmd_dma_debugg::_10mhz_rx;
        else if (param_name == "10MHz_out")
            dma_debugg = cmd_dma_debugg::_10MHz_out;
        else if (param_name == "powerdown_plata")
            dma_debugg = cmd_dma_debugg::powerdown_plata;
        else if (param_name == "default_params")
            dma_debugg = cmd_dma_debugg::default_params;
        else if (param_name == "current_state_tx")
            dma_debugg = cmd_dma_debugg::current_state_tx;
        else if (param_name == "current_state_oib")
            dma_debugg = cmd_dma_debugg::current_state_oib;
        else if (param_name == "save_config")
            dma_debugg = cmd_dma_debugg::save_config;
        else if (param_name == "begin_save_config")
            dma_debugg = cmd_dma_debugg::begin_save_config;
        else return ERROR;

        auto resp = clients[sid]->send_set_dma_debug(dma_debugg, val);
        if (resp == response_type::error)
            return ERROR;

        return OK;

    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_SetAcmParams(TSID sid, ACM_parameters_serv_ acm_params)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;

        ACM_parameters_serv acm_pars;
        acm_pars.enable = acm_params.enable;
        acm_pars.max_attenuation = acm_params.max_attenuation;
        acm_pars.max_modcod = acm_params.max_modcod;
        acm_pars.min_attenuation = acm_params.min_attenuation;
        acm_pars.min_modcod = acm_params.min_modcod;
        acm_pars.snr_treashold = acm_params.snr_treashold;
        acm_pars.enable_auto_atten = acm_params.enable_auto_atten;
        acm_pars.snr_treashold_acm = acm_params.snr_treashold_acm;
        acm_pars.period_pack = acm_params.period_pack;
        auto resp = clients[sid]->send_set_acm_params(acm_pars);
        if (resp == response_type::error)
            return ERROR;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_Set10gConfig(TSID sid, const char *parameter, std::string val)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
                return ERROR;
        cmd_10g_config _10g_config;
        std::string param_name(parameter);
        if(param_name == "udp_payload_length")
            _10g_config = cmd_10g_config::udp_payload_lenght;
        else if (param_name == "local_ip")
            _10g_config = cmd_10g_config::local_ip;
        else if (param_name == "dest_ip")
            _10g_config = cmd_10g_config::dest_ip;
        else if (param_name == "gateway_ip")
            _10g_config = cmd_10g_config::gateway_ip;
        else if (param_name == "subnet_mask")
            _10g_config = cmd_10g_config::subnet_mask;
        else if (param_name == "udp_source_port")
            _10g_config = cmd_10g_config::udp_source_port;
        else if (param_name == "udp_dest_port")
            _10g_config = cmd_10g_config::udp_dest_port;
        else if (param_name == "local_mac_1")
            _10g_config = cmd_10g_config::local_mac_31_0;
        else if (param_name == "local_mac_2")
            _10g_config = cmd_10g_config::local_mac_32_47;
        else if (param_name == "chan0")
            _10g_config = cmd_10g_config::mtx_chan0;
        else if (param_name == "chan1")
            _10g_config = cmd_10g_config::mtx_chan1;
        else return ERROR;

        auto resp = clients[sid]->send_set_10g_config(_10g_config, val);
        if (resp == response_type::error)
            return ERROR;

        return OK;

    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetDpdiParams(TSID sid, DPDI_parmeters *dpdi_pars_get)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;

        dpdi_parameters dpdi_from_server;
        auto resp = clients[sid]->send_get_dpdi_params(dpdi_from_server);
        if (resp == response_type::error)
            return ERROR;
        DPDI_parmeters dpdi_pars;
        dpdi_pars.is_delay_window = dpdi_from_server.is_delay_window;
        dpdi_pars.max_delay = dpdi_from_server.max_delay;
        dpdi_pars.min_delay = dpdi_from_server.min_delay;
        dpdi_pars.freq_offset = dpdi_from_server.freq_offset;
        dpdi_pars.latitude_station_grad = dpdi_from_server.latitude_station_grad;
        dpdi_pars.latitude_station_minute = dpdi_from_server.latitude_station_minute;
        dpdi_pars.longitude_sattelite_grad = dpdi_from_server.longitude_sattelite_grad;
        dpdi_pars.longitude_sattelite_minute = dpdi_from_server.longitude_sattelite_minute;
        dpdi_pars.longitude_station_grad = dpdi_from_server.longitude_station_grad;
        dpdi_pars.longitude_station_minute = dpdi_from_server.longitude_station_minute;
        *dpdi_pars_get = dpdi_pars;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetAcmParams(TSID sid, ACM_parameters_serv_ *acm_params)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;

        cmd_get_acm_param acm_from_server;
        auto resp = clients[sid]->send_get_acm_params(acm_from_server);
        if (resp == response_type::error)
            return ERROR;
        ACM_parameters_serv_ acm_from_serv_;
        acm_from_serv_.enable = acm_from_server.enable;
        acm_from_serv_.enable_auto_atten = acm_from_server.enable_auto_atten;
        acm_from_serv_.min_attenuation = acm_from_server.min_attenuation;
        acm_from_serv_.max_attenuation = acm_from_server.max_attenuation;
        acm_from_serv_.min_modcod = acm_from_server.min_modcod;
        acm_from_serv_.snr_treashold = acm_from_server.snr_treashold;
        acm_from_serv_.max_modcod = acm_from_server.max_modcod;
        acm_from_serv_.snr_treashold_acm = acm_from_server.snr_treashold_acm;
        acm_from_serv_.period_pack = acm_from_server.period_pack;
        *acm_params = acm_from_serv_;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetDmaDebug(TSID sid, const char *command, std::string *val)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        std::string value_from_server;
        cmd_get_dma_debugg_enum cmd;
        std::string param_name(command);

        if(param_name == "speed_tx")
            cmd = cmd_get_dma_debugg_enum::speed_tx;
        else if (param_name == "speed_rx")
            cmd = cmd_get_dma_debugg_enum::speed_rx;
        if(param_name == "speed_tx_iface")
            cmd = cmd_get_dma_debugg_enum::speed_tx_iface;
        else if (param_name == "speed_rx_iface")
            cmd = cmd_get_dma_debugg_enum::speed_rx_iface;
        else if (param_name == "modcod")
            cmd = cmd_get_dma_debugg_enum::modcod;
        else if (param_name == "drop_bad_rx")
            cmd = cmd_get_dma_debugg_enum::drop_bad_rx;
        else if (param_name == "drop_full_rx")
            cmd = cmd_get_dma_debugg_enum::drop_full_rx;
        else if (param_name == "packet_ok_rx")
            cmd = cmd_get_dma_debugg_enum::packet_ok_rx;
        else if (param_name == "packet_little_tx")
            cmd = cmd_get_dma_debugg_enum::packet_little_tx;
        else if (param_name == "packet_big_tx")
            cmd = cmd_get_dma_debugg_enum::packet_big_tx;
        else if (param_name == "bad_modcod_tx")
            cmd = cmd_get_dma_debugg_enum::bad_modcod_tx;
        else if (param_name == "bad_length_tx")
            cmd = cmd_get_dma_debugg_enum::bad_length_tx;
        else if (param_name == "reset_cnt_rx")
            cmd = cmd_get_dma_debugg_enum::reset_cnt_rx;
        else if (param_name == "buc_voltage")
            cmd = cmd_get_dma_debugg_enum::buc_voltage;
        else if (param_name == "lnb_voltage")
            cmd = cmd_get_dma_debugg_enum::lnb_voltage;
        else if (param_name == "10MHz_tx")
            cmd = cmd_get_dma_debugg_enum::_10mhz_tx;
        else if (param_name == "10MHz_rx")
            cmd = cmd_get_dma_debugg_enum::_10mhz_rx;
        else if (param_name == "10MHz_out")
            cmd = cmd_get_dma_debugg_enum::_10MHz_out;
        else if (param_name == "current_state_tx")
            cmd = cmd_get_dma_debugg_enum::current_state_tx;
        else if (param_name == "data_mode")
            cmd = cmd_get_dma_debugg_enum::data_mode;
        else if (param_name == "delay_dpdi")
            cmd = cmd_get_dma_debugg_enum::real_dpdi_shift;
        else if (param_name == "freq_error_offset")
            cmd = cmd_get_dma_debugg_enum::freq_error_offset;
        else if (param_name == "freq_fine_estimate")
            cmd = cmd_get_dma_debugg_enum::freq_fine_estimate;
        else if (param_name == "powerdown_plata")
            cmd = cmd_get_dma_debugg_enum::powerdown_plata;
        else if (param_name == "current_state_oib")
            cmd = cmd_get_dma_debugg_enum::current_state_oib;
        else if (param_name == "ratio_signal_signal")
            cmd = cmd_get_dma_debugg_enum::ratio_signal_signal;
        else if (param_name == "status_init")
            cmd = cmd_get_dma_debugg_enum::status_init;

        auto resp = clients[sid]->send_get_dma_debug(cmd, value_from_server);
        if (resp == response_type::error)
            return ERROR;
        *val = value_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetLevelDemod(TSID sid, const char * param ,double *value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        double level_from_server;
        cmd_level_dem level_value;
        std::string param_name(param);
        if(param_name == "snr")
            level_value = cmd_level_dem::snr;
        else if(param_name == "rssi")
            level_value = cmd_level_dem::rssi;
        else if(param_name == "pkt_sync")
            level_value = cmd_level_dem::pkt_sync;
        else if(param_name == "afc_lock")
            level_value = cmd_level_dem::afc_lock;
        else if(param_name == "freq_lock")
            level_value = cmd_level_dem::freq_search_lock;
        else if(param_name == "sym_sync_lock")
            level_value = cmd_level_dem::sym_sync_lock;
        else if(param_name == "phase_inv")
            level_value = cmd_level_dem::phase_inv;
        else if (param_name == "afc_err")
            level_value = cmd_level_dem::afc_error;
        else if (param_name == "crs_freq_err")
            level_value = cmd_level_dem::crs_freq_error;
        else if (param_name == "sym_err")
            level_value = cmd_level_dem::sym_error;
        else if (param_name == "fine_freq_err")
            level_value = cmd_level_dem::fine_freq_error;
        else if (param_name == "gain_aru")
            level_value = cmd_level_dem::gc_gain_aru;
        else if (param_name == "rollof")
            level_value = cmd_level_dem::rollof;
        else if (param_name == "carrier_lock")
            level_value = cmd_level_dem::carrier_lock;
        else if (param_name == "filt_adapt_lock")
            level_value = cmd_level_dem::filt_adapt_lock;
        else if (param_name == "snr_acm")
            level_value = cmd_level_dem::snr_acm;
        else if (param_name == "modcod_tx")
            level_value = cmd_level_dem::modcod_tx;
        auto resp = clients[sid]->send_get_level_dem(level_value, level_from_server);
        if (resp == response_type::error)
            return ERROR;
        *value = level_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_ZynqParams(TSID sid, const char *zynq_param, double *value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        zynq_value zyn_value;
        double par_from_server;
        std::string param_name(zynq_param);
        if(param_name == "ps_voltage")
            zyn_value = zynq_value::ps_volt;
        else if(param_name == "ps_temper_celsius")
            zyn_value = zynq_value::ps_temp;
        else if(param_name == "pl_voltage")
            zyn_value = zynq_value::pl_volt;
        else if(param_name == "pl_temper_celsius")
            zyn_value = zynq_value::pl_temp;
        else if(param_name == "adrv-c_temper_celsius")
            zyn_value = zynq_value::adrv_temp;
        else if(param_name == "adrv-d_temper_celsius")
            zyn_value = zynq_value::adrv_temp2;
        else return ERROR;

        auto resp = clients[sid]->send_zynq_cmd(zyn_value, par_from_server);
        if (resp == response_type::error)
            return ERROR;

        *value = par_from_server;
        return OK;

    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetModulatorParams(TSID sid, const char *modulator_param, uint32_t *value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        modulator_value mod_val;
        uint32_t par_from_server;
        std::string param_name(modulator_param);
        if(param_name == "baudrate")
            mod_val = modulator_value::baud_rate;
        else if (param_name == "center_freq")
            mod_val = modulator_value::lo_freaquency;
        else if (param_name == "rollof")
            mod_val = modulator_value::rollof;
        else if (param_name == "mode_trans")
            mod_val = modulator_value::mode_transmitt;
        else if (param_name == "if_overload")
            mod_val = modulator_value::if_overload;
        else if (param_name == "gold_seq")
            mod_val = modulator_value::gold_seq;

        else return ERROR;

        auto resp = clients[sid]->send_get_modulator_param(mod_val, par_from_server);
        if (resp == response_type::error)
            return ERROR;
        *value = par_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetDemodulatorParams(TSID sid, const char *demodulator_param, uint32_t *value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        getdemodulator_value demod_val;
        uint32_t par_from_server;
        std::string param_name(demodulator_param);
        if(param_name == "baudrate")
            demod_val = getdemodulator_value::baud_rate;
        else if (param_name == "center_freq")
            demod_val = getdemodulator_value::lo_freaquency;
        else if (param_name == "dummy_cnt")
            demod_val = getdemodulator_value::dummy_counter;
        else if (param_name == "modcod")
            demod_val = getdemodulator_value::modcod;
        else if (param_name == "mode_gain_control")
            demod_val = getdemodulator_value::mode_gain_control;
        else if (param_name == "sig_min")
            demod_val = getdemodulator_value::sig_min;
        else if (param_name == "sig_max")
            demod_val = getdemodulator_value::sig_max;
        else if (param_name == "rvt")
            demod_val = getdemodulator_value::rvt;
        else if (param_name == "fec_size")
            demod_val = getdemodulator_value::fec_size;
        else if (param_name == "mode_demod")
            demod_val = getdemodulator_value::mode_demod;
        else if (param_name == "cnt_bad_lock_cinc")
            demod_val = getdemodulator_value::cnt_bad_lock_cinc;
        else if (param_name == "gold_seq")
            demod_val = getdemodulator_value::gold_seq;
        else if (param_name == "attitude_signals")
            demod_val = getdemodulator_value::attitude_signals;
        else if (param_name == "cnt_dem_time_sinc")
            demod_val = getdemodulator_value::cnt_dem_time_sinc;
        else if (param_name == "cnt_dem_lock_afc")
            demod_val = getdemodulator_value::cnt_dem_lock_afc;
        else if (param_name == "cnt_dem_sync_pack")
            demod_val = getdemodulator_value::cnt_dem_sync_pack;
        else if (param_name == "cnt_dem_sinc_frec_corse")
            demod_val = getdemodulator_value::cnt_dem_sinc_frec_corse;
        else if (param_name == "freq_lock_estimate")
            demod_val = getdemodulator_value::freq_lock_estimate;
        else if (param_name == "reset_cinc")
            demod_val = getdemodulator_value::is_reset_CinC;
        else if (param_name == "type_pack")
            demod_val = getdemodulator_value::type_pack;
        else if (param_name == "is_pilots")
            demod_val = getdemodulator_value::is_pilots;
        else return ERROR;

        auto resp = clients[sid]->send_get_demodulator_param(demod_val, par_from_server);
        if (resp == response_type::error)
            return ERROR;

        *value = par_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_DemodulatorParams(TSID sid, const char *demodulator_param, uint32_t value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        demodulator_value demod_val;
        std::string param_name(demodulator_param);
        if(param_name == "baudrate")
            demod_val = demodulator_value::baud_rate;
        else if (param_name == "center_freq")
            demod_val = demodulator_value::lo_freaquency;
        else if (param_name == "demod_reset")
            demod_val = demodulator_value::demod_reset;
        else if (param_name == "fec_size")
            demod_val = demodulator_value::fec_frame_size;
        else if (param_name == "rvt")
            demod_val = demodulator_value::rvt;
        else if (param_name == "mode_gain_control")
            demod_val = demodulator_value::mode_gain_control;
        else if (param_name == "sig_min")
            demod_val = demodulator_value::sig_min;
        else if (param_name == "sig_max")
            demod_val = demodulator_value::sig_max;
        else if (param_name == "afc_rst")
            demod_val = demodulator_value::afc_rst;
        else if (param_name == "mode_demod")
            demod_val = demodulator_value::mode_demod;
        else if (param_name == "gold_seq")
            demod_val = demodulator_value::gold_seq;
        else if (param_name == "attitude_signals")
            demod_val = demodulator_value::attitude_signals;
        else if (param_name == "reset_cinc")
            demod_val = demodulator_value::is_reset_CinC;
        else return ERROR;
        auto resp = clients[sid]->send_set_demodulator_param(demod_val,value);
        if (resp == response_type::error)
            return ERROR;

        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}
EXTERNC CP_Result CP_ModulatorParams(TSID sid, const char *modulator_param, uint32_t value)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        modulator_value mod_val;
        std::string param_name(modulator_param);
        if(param_name == "baudrate")
            mod_val = modulator_value::baud_rate;
        else if (param_name == "center_freq")
            mod_val = modulator_value::lo_freaquency;
        else if (param_name == "temp_treshold")
            mod_val = modulator_value::temp_treshold;
        else if (param_name == "modcod")
            mod_val = modulator_value::modcod;
        else if (param_name == "reset")
            mod_val = modulator_value::mod_reset;
        else if (param_name == "rollof")
            mod_val = modulator_value::rollof;
        else if (param_name == "mode_trans")
            mod_val = modulator_value::mode_transmitt;
        else if (param_name == "gold_seq")
            mod_val = modulator_value::gold_seq;

        else return ERROR;
        auto resp = clients[sid]->send_modulator_param(mod_val,value);
        if (resp == response_type::error)
            return ERROR;

        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_RadioEnable(TSID sid, const char *radio_interface, bool on_of)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        cmd_radio radio;
        std::string radio_name(radio_interface);
        if(radio_name == "TX1")
            radio = cmd_radio::tx1;
        else if (radio_name == "TX2")
            radio = cmd_radio::tx2;
        else if (radio_name == "RX1")
            radio = cmd_radio::rx1;
        else if (radio_name == "RX2")
            radio = cmd_radio::rx2;
        else if(radio_name == "GLOBAL")
            radio = cmd_radio::global;
        else if(radio_name == "GLOBAL2")
            radio = cmd_radio::global2;
        else return ERROR;

        auto resp = clients[sid]->send_radio_enable(radio,on_of);
        if (resp == response_type::error)
            return ERROR;

        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }

}
EXTERNC CP_Result CP_SetGain(TSID sid,const char *gain_interface,double gain){
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        gain_value value;
        std::string gain_name(gain_interface);
        if(gain_name == "TX1")
            value = gain_value::tx1;
        else if (gain_name == "TX2")
            value = gain_value::tx2;
        else if (gain_name == "RX1")
            value = gain_value::rx1;
        else if (gain_name == "RX2")
            value = gain_value::rx2;
        else return ERROR;

        auto resp = clients[sid]->send_set_gain_param(value,gain);
        if (resp == response_type::error)
            return ERROR;

        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetGain(TSID sid, const char *gain_interface, double *gain){
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
             return ERROR;
        double gain_from_server;
        gain_value value;
        std::string gain_name(gain_interface);
        if(gain_name == "TX1")
            value = gain_value::tx1;
        else if (gain_name == "TX2")
            value = gain_value::tx2;
        else if (gain_name == "RX1")
            value = gain_value::rx1;
        else if (gain_name == "RX2")
            value = gain_value::rx2;
        else if (gain_name == "TXPWD")
            value = gain_value::txpwd;
        else if (gain_name == "RXPWD")
            value = gain_value::rxpwd;
        else return ERROR;

        auto resp = clients[sid]->send_get_gain_param(value,gain_from_server);
        if (resp == response_type::error)
            return ERROR;
         *gain = gain_from_server;
        return OK;
    }
    catch(const std::exception& e)
    {
        return ERROR;
    }

}

EXTERNC CP_Result CP_SetDemFreq(TSID sid, uint32_t freq)
{
    std::shared_lock lock(mtx);
    try
    {
       if (clients.find(sid) == clients.end())
           return ERROR;

       auto resp = clients[sid]->send_set_dem_freq_cmd(freq);
       if (resp == response_type::error)
           return ERROR;

       return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_GetDemFreq(TSID sid, uint32_t * freq)
{
    std::shared_lock lock(mtx);
    try
    {
       if (clients.find(sid) == clients.end())
           return ERROR;

       uint32_t freq_from_serv;
       auto resp = clients[sid]->send_get_dem_freq_cmd(freq_from_serv);
       if (resp == response_type::error)
           return ERROR;

       *freq = freq_from_serv;
       return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}


EXTERNC CP_Result CP_Login(const char * user, const char * pwd, TSID * sid, unsigned int * access)
{
    std::unique_lock lock(mtx);
    try
    {
        clients[sid_counter] = std::make_unique<system_client>("/tmp/control.sock");
        access_rights access_lvl;
        auto resp = clients[sid_counter]->send_login_cmd(user, pwd, access_lvl);
        *access = static_cast<unsigned int>(access_lvl);
        *sid    = sid_counter;

        if (resp == response_type::error)
            return ERROR;

        while (clients.find(sid_counter) != clients.end())
            sid_counter++;

        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_Logout(TSID sid)
{
    std::unique_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return ERROR;

        auto resp = clients[sid]->send_logout_cmd();
        if (resp == response_type::error)
            return ERROR;

        clients.erase(sid);
        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC void CP_SetCmdStdoutCallback(TSID sid, CP_cmd_stdout_cb cb)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return;

        clients[sid]->set_stdout_callback(cb);
    }
    catch(const std::exception & e)
    {
        return;
    }
}

EXTERNC CP_Result CP_Ping(TSID sid, const char * ip_address, int packet_count)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end() || packet_count < 1)
            return ERROR;

        auto resp = clients[sid]->send_ping_cmd(ip_address, static_cast<size_t>(packet_count));
        if (resp == response_type::error)
            return ERROR;
        else if (resp == response_type::abort)
            return ABORT;

        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_Traceroute(TSID sid, const char * ip_address)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return ERROR;

        auto resp = clients[sid]->send_traceroute_cmd(ip_address);
        if (resp == response_type::error)
            return ERROR;
        else if (resp == response_type::abort)
            return ABORT;

        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC void CP_CmdAbort(TSID sid)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return;

        clients[sid]->abort();
    }
    catch(const std::exception & e)
    {
        return;
    }
}

EXTERNC CP_Result CP_ShowInterface(TSID sid, const char * interface)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return ERROR;

        std::string interface_name(interface);
        interface_value value;
        if (interface_name == "all")
            value = interface_value::all;
        else if (interface_name == "sat0")
            value = interface_value::sat0;
        else if (interface_name == "gigabit")
            value = interface_value::gigabit;
        else if (interface_name == "modulator")
            value = interface_value::modulator;
        else if (interface_name == "demodulator")
            value = interface_value::demodulator;
        else
            return ERROR;

        auto resp = clients[sid]->send_show_interface_cmd(value);
        if (resp == response_type::error)
            return ERROR;
        else if (resp == response_type::abort)
            return ABORT;

        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}

EXTERNC CP_Result CP_Copy(TSID sid, const char * src, const char * dst)
{
    std::shared_lock lock(mtx);
    try
    {
        if (clients.find(sid) == clients.end())
            return ERROR;

        std::string src_str(src);
        std::string dst_str(dst);

        fm::side_description src_desc;
        fm::side_description dst_desc;

        if (src_str.find("flash:") == 0)
        {
            src_desc.s_type = fm::side_type::local;
            size_t pos = src_str.find(":");
            src_desc.filepath = src_str.substr(pos + 1, src_str.size() - pos - 1);
        }
        else if (src_str.find("tftp://") == 0)
        {
            src_desc.s_type = fm::side_type::tftp;
            size_t pos = src_str.find("://");
            src_desc.filepath = src_str.substr(pos + 3, src_str.size() - pos - 3);
        }
        else
            return ERROR;

        if (dst_str.find("flash:") == 0)
        {
            dst_desc.s_type = fm::side_type::local;
            size_t pos = dst_str.find(":");
            dst_desc.filepath = dst_str.substr(pos + 1, dst_str.size() - pos - 1);
        }
        else if (dst_str.find("tftp://") == 0)
        {
            dst_desc.s_type = fm::side_type::tftp;
            size_t pos = dst_str.find("://");
            dst_desc.filepath = dst_str.substr(pos + 3, dst_str.size() - pos - 3);
        }
        else
            return ERROR;

        if (src_desc.s_type == fm::side_type::tftp && dst_desc.s_type == fm::side_type::tftp)
            return ERROR;

        auto resp = clients[sid]->send_copy_cmd(src_desc, dst_desc);
        if (resp == response_type::error)
            return ERROR;
        else if (resp == response_type::abort)
            return ABORT;

        return OK;
    }
    catch(const std::exception & e)
    {
        return ERROR;
    }
}
