#include "terminal_api_driver.h"
#include "terminal_api/ControlProtoCInterface.h"
#include <sstream>
#include <cmath>

/*
timer_->timeout().connect([=]{
    double txpwd = 0;
    CP_GetGain(sid, "TXPWD", &txpwd);
    transmitt_active->set_end(!txpwd);
    CP_GetLevelDemod(sid, "modcod_tx", &txpwd);
    modcod_tx->setText(std::to_string(static_cast<uint32_t>(txpwd) >> 2));
    uint8_t type_pack = static_cast<uint8_t>(txpwd);
    type_pack = type_pack & 0b00000010;
    if(type_pack)
        type_pack_lbl->setText("short");
    else
        type_pack_lbl->setText("normal");

    uint8_t is_pilots = static_cast<uint8_t>(txpwd);
    is_pilots = is_pilots & 0b00000001;
    if(is_pilots)
        pilot_lbl->setText("pilots");
    else
        pilot_lbl->setText("no pilots");

    CP_GetLevelDemod(sid, "snr_acm", &txpwd);
    std::stringstream buf_double;
    buf_double << std::fixed <<
    std::setprecision(2) << txpwd;
    snr_acm->setText(buf_double.str());
});

 */

api_driver::ApiDriver::ApiDriver() {
    CP_Login("admin", "pass", &sid, &access);
}

static bool DriverCP_GetLevelDemod(TSID sid, const char* param) {
    double variable_dbl = 0;
    CP_GetLevelDemod(sid, param, &variable_dbl);
    return variable_dbl == 0;
}

static bool DriverCP_GetTxPower(TSID sid) {
    double variable_dbl = 0;
    CP_GetGain(sid, "TXPWD", &variable_dbl);
    return variable_dbl != 0;
}

static bool DriverCp_GetIsCinC(TSID sid) {
    uint32_t mode_demod = 0;
    CP_GetDemodulatorParams(sid, "mode_demod", &mode_demod);
    return mode_demod == 1;
}

static const char* boolAsStr(bool value) {
    return value ? "true" : "false";
}

std::string api_driver::ApiDriver::loadTerminalState() {
    std::stringstream result;
    result << "{";

    const auto txEnable = DriverCP_GetTxPower(sid);
    const auto isCinC = DriverCp_GetIsCinC(sid);
    result << "\"isCinC\":" << boolAsStr(isCinC);

    // формируем структуру для TX
    {
        result << ",\"tx.state\":" << boolAsStr(txEnable);

        double tmp = 0;
        char tmpStr[32];

        CP_GetLevelDemod(sid, "snr_acm", &tmp);
        sprintf(tmpStr, "%.2f", tmp);
        result << ",\"tx.snr\":" << tmpStr;

        CP_GetLevelDemod(sid, "modcod_tx", &tmp);
        result << ",\"tx.modcod\":" << (static_cast<uint32_t>(tmp) >> 2);

        if (static_cast<uint8_t>(tmp) & 0b00000010) {
            result << R"(,"tx.frameSize":"short")";
        } else {
            result << R"(,"tx.frameSize":"normal")";
        }

        if (static_cast<uint8_t>(tmp) & 0b00000001) {
            result << R"(,"tx.pilots":"pilots")";
        } else {
            result << R"(,"tx.pilots":"no pilots")";
        }

        std::string speed;
        CP_GetDmaDebug(sid, "speed_tx", &speed);
        sprintf(tmpStr, "%.3f", std::atof(speed.c_str()) / 128.0);
        result << ",\"tx.speedOnTxKbit\":" << tmpStr;

        CP_GetDmaDebug(sid, "speed_tx_iface", &speed);
        sprintf(tmpStr, "%.3f", std::atof(speed.c_str()) / 128.0);
        result << ",\"tx.speedOnIifKbit\":" << tmpStr;
    }

    // формируем структуру для RX
    {
        const auto sym_sync_lock = DriverCP_GetLevelDemod(sid, "sym_sync_lock"); // захват символьной
        const auto freq_search_lock = DriverCP_GetLevelDemod(sid, "freq_lock"); // Захват поиска по частоте
        const auto afc_lock = DriverCP_GetLevelDemod(sid, "afc_lock"); // захват ФАПЧ
        const auto pkt_sync = DriverCP_GetLevelDemod(sid, "pkt_sync"); // захват пакетной синхронизации
        const auto receive_active = sym_sync_lock && freq_search_lock && afc_lock && pkt_sync;

        result << ",\"rx.state\":" << boolAsStr(receive_active);
        result << ",\"rx.sym_sync_lock\":" << boolAsStr(sym_sync_lock);
        result << ",\"rx.freq_search_lock\":" << boolAsStr(freq_search_lock);
        result << ",\"rx.afc_lock\":" << boolAsStr(afc_lock);
        result << ",\"rx.pkt_sync\":" << boolAsStr(pkt_sync);

        double tmpd = 0; uint32_t tmpu32 = 0;
        char tmpStr[32];

        CP_GetLevelDemod(sid, "snr", &tmpd);
        sprintf(tmpStr, "%.2f", tmpd);
        result << ",\"rx.snr\":" << tmpStr;

        CP_GetLevelDemod(sid, "rssi", &tmpd);
        sprintf(tmpStr, "%.2f", -tmpd);
        result << ",\"rx.rssi\":" << tmpStr;

        CP_GetDemodulatorParams(sid, "modcod", &tmpu32);
        result << ",\"rx.modcod\":" << tmpu32;

        CP_GetDemodulatorParams(sid, "type_pack", &tmpu32);
        if (tmpu32) {
            result << R"(,"rx.frameSize":"short")";
        } else {
            result << R"(,"rx.frameSize":"normal")";
        }

        CP_GetDemodulatorParams(sid, "is_pilots", &tmpu32);
        if (tmpu32) {
            result << R"(,"rx.pilots":"pilots")";
        } else {
            result << R"(,"rx.pilots":"no pilots")";
        }

        CP_GetLevelDemod(sid, "sym_err", &tmpd);
        sprintf(tmpStr, "%.2f", tmpd);
        result << ",\"rx.symError\":" << tmpStr;

        CP_GetLevelDemod(sid, "crs_freq_err", &tmpd); // freqErr
        sprintf(tmpStr, "%.2f", tmpd);
        result << ",\"rx.freqErr\":" << tmpStr;

        CP_GetLevelDemod(sid, "fine_freq_err", &tmpd); // freqErrAcc
        sprintf(tmpStr, "%.2f", tmpd);
        result << ",\"rx.freqErrAcc\":" << tmpStr;

        CP_GetModulatorParams(sid, "if_overload", &tmpu32); // inputSignalLevel
        result << ",\"rx.inputSignalLevel\":" << tmpStr;

        CP_GetLevelDemod(sid, "afc_err", &tmpd); // PLL
        sprintf(tmpStr, "%.2f", tmpd);
        result << ",\"rx.pllError\":" << tmpStr;

        std::string speed;
        CP_GetDmaDebug(sid, "speed_rx", &speed);
        sprintf(tmpStr, "%.3f", std::atof(speed.c_str()) / 128.0);
        result << ",\"rx.speedOnRxKbit\":" << tmpStr;

        speed.clear(); CP_GetDmaDebug(sid, "speed_rx_iface", &speed);
        sprintf(tmpStr, "%.3f", std::atof(speed.c_str()) / 128.0);
        result << ",\"rx.speedOnIifKbit\":" << tmpStr;

        speed.clear(); CP_GetDmaDebug(sid, "packet_ok_rx", &speed);
        result << ",\"rx.packetsOk\":" << std::atoi(speed.c_str());

        speed.clear(); CP_GetDmaDebug(sid, "drop_bad_rx", &speed);
        result << ",\"rx.packetsBad\":" << std::atoi(speed.c_str());

        CP_GetDemodulatorParams(sid, "dummy_cnt", &tmpu32);
        result << ",\"rx.packetsDummy\":" << tmpu32;
    }

    // формируем структуру для CinC
    {
        std::string tmps;
        char tmpbuff[32];
        CP_GetDmaDebug(sid, "ratio_signal_signal", &tmps);
        sprintf(tmpbuff, "%.3f", std::atof(tmps.c_str()));
        result << ",\"cinc.occ\":" << tmpbuff;

        const bool carrierOk = DriverCP_GetLevelDemod(sid, "carrier_lock");
        if (isCinC && txEnable) {
            if (carrierOk) {
                result << R"(,"cinc.correlator":true)";
            } else {
                result << R"(,"cinc.correlator":false)";
            }
        } else {
            result << R"(,"cinc.correlator":null)";
        }

        uint32_t tmpu32 = 0;
        CP_GetDemodulatorParams(sid, "cnt_bad_lock_cinc", &tmpu32);
        result << ",\"cinc.correlatorFails\":" << tmpu32;

        tmps.clear(); CP_GetDmaDebug(sid, "freq_error_offset", &tmps);
        // тут так сделано из-за приколов со знаками...
        result << ",\"cinc.freqErr\":" << std::to_string(static_cast<int32_t>(std::atol(tmps.c_str())));

        tmps.clear(); CP_GetDmaDebug(sid, "freq_fine_estimate", &tmps);
        result << ",\"cinc.freqErrAcc\":" << tmps;

        tmps.clear(); CP_GetDmaDebug(sid, "delay_dpdi", &tmps);
        result << ",\"cinc.channelDelay\":" << std::to_string(static_cast<uint32_t>(std::round(std::atof(tmps.c_str()) * 1000)));
    }

    // структура температур девайса
    {
        char tmp[32];
        double tmp_adrv_c = 0;
        double tmp_ps = 0;
        double tmp_pl = 0;
        CP_ZynqParams(sid,"adrv-c_temper_celsius", &tmp_adrv_c);
        CP_ZynqParams(sid,"pl_temper_celsius", &tmp_pl);
        CP_ZynqParams(sid,"ps_temper_celsius", &tmp_ps);

        sprintf(tmp, "%.1f", tmp_adrv_c / 1000); result << ",\"device.adrv\":" << tmp;
        sprintf(tmp, "%.1f", tmp_pl / 1000); result << ",\"device.fpga\":" << tmp;
        sprintf(tmp, "%.1f", tmp_ps / 1000); result << ",\"device.zync\":" << tmp;
    }

    result << "}";

    return result.str();
}


void api_driver::ApiDriver::resetPacketStatistics() const {
    std::string tmp;
    CP_GetDmaDebug(sid, "reset_cnt_rx", &tmp);
}

std::string api_driver::ApiDriver::loadSettings() {
    return "{}";
}

api_driver::ApiDriver::~ApiDriver() = default;
