#include "MEg40.h"
#include <thread>

using namespace std::string_literals;

namespace myapp::modbus::devices {

MEg40::MEg40(std::string name, int slave_id, std::shared_ptr<connection> conn, std::shared_ptr<logging::logger> logger)
  : device(std::move(name), slave_id, std::move(conn), std::move(logger))
  , register_poll_()
  , read_period_{ 0 }
  , last_read_time_{ std::chrono::system_clock::now() - std::chrono::hours(1) }
{
}

void MEg40::initialize()
{
    executor_([self = shared_from_base<MEg40>()]
    {
        try
        {
            auto vec = self->data_access_->read_input_registers(self->slave_id_, 37, 2);
            self->voltage_range_update(vec[0]);
            self->power_range_update(vec[1]);
        }
        catch (std::runtime_error& e)
        {
            self->logger_->warning(self->get_name(), "Reading registers failed with error: "s + e.what());
        }
    });

	register_poll_ = [self = shared_from_base<MEg40>()]
    {
        auto t = std::chrono::system_clock::now();
        auto const dt = std::chrono::duration_cast<std::chrono::seconds>(t - self->last_read_time_);
        
        if (dt.count() >= self->read_period_)
        {
            try
            {
                auto vec = self->data_access_->read_input_registers(self->slave_id_, 1, 36);
                self->phase_voltage_update(vec[0], vec[1], vec[2]);
                self->line_voltage_update(vec[3], vec[4], vec[5]);
                self->phase_current_update(vec[6], vec[7], vec[8]);
                self->real_power_update(vec[9], vec[10], vec[11]);
                self->reactive_power_update(vec[12], vec[13], vec[14]);
                self->power_factor_update(vec[15], vec[16], vec[17]);
                self->EPP(vec[18], vec[19], vec[20]);
                self->EPM(vec[21], vec[22], vec[23]);
                self->EQPP(vec[24], vec[25], vec[26]);
                self->EQPM(vec[27], vec[28], vec[29]);
                self->EQMP(vec[30], vec[31], vec[32]);
                self->EQMM(vec[33], vec[34], vec[35]);
            }
            catch (std::runtime_error& e)
            {
                self->logger_->warning(self->get_name(), "Reading registers failed with error: "s + e.what());
            }

            self->last_read_time_ = std::move(t);
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::seconds{ 1 });
        }

        self->executor_(self->register_poll_);
    };

    executor_(register_poll_);
}

void MEg40::set_read_period(std::chrono::seconds::rep t)
{
    read_period_ = std::move(t);
}

std::string MEg40::handle_command_impl(std::string const& str)
{
    throw std::runtime_error("MEg40 is read-only device");
}

void MEg40::phase_voltage_update(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("U1"s, std::to_string(L1));
    raise_status_update("U2"s, std::to_string(L2));
    raise_status_update("U3"s, std::to_string(L3));
}

void MEg40::line_voltage_update(uint16_t L1_L2, uint16_t L2_L3, uint16_t L3_L1)
{
    raise_status_update("U12"s, std::to_string(L1_L2));
    raise_status_update("U23"s, std::to_string(L2_L3));
    raise_status_update("U31"s, std::to_string(L3_L1));
}

void MEg40::phase_current_update(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("I1"s, std::to_string(L1));
    raise_status_update("I2"s, std::to_string(L2));
    raise_status_update("I3"s, std::to_string(L3));
}

void MEg40::real_power_update(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("P1"s, std::to_string(L1));
    raise_status_update("P2"s, std::to_string(L2));
    raise_status_update("P3"s, std::to_string(L3));
}

void MEg40::reactive_power_update(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("Q1"s, std::to_string(L1));
    raise_status_update("Q2"s, std::to_string(L2));
    raise_status_update("Q3"s, std::to_string(L3));
}

void MEg40::power_factor_update(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("PF1"s, std::to_string(L1));
    raise_status_update("PF2"s, std::to_string(L2));
    raise_status_update("PF3"s, std::to_string(L3));
}

void MEg40::EPP(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EPP1"s, std::to_string(L1));
    raise_status_update("EPP2"s, std::to_string(L2));
    raise_status_update("EPP3"s, std::to_string(L3));
}

void MEg40::EPM(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EPM1"s, std::to_string(L1));
    raise_status_update("EPM2"s, std::to_string(L2));
    raise_status_update("EPM3"s, std::to_string(L3));
}

void MEg40::EQPP(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EQPP1"s, std::to_string(L1));
    raise_status_update("EQPP2"s, std::to_string(L2));
    raise_status_update("EQPP3"s, std::to_string(L3));
}

void MEg40::EQPM(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EQPM1"s, std::to_string(L1));
    raise_status_update("EQPM2"s, std::to_string(L2));
    raise_status_update("EQPM3"s, std::to_string(L3));
}

void MEg40::EQMP(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EQMP1"s, std::to_string(L1));
    raise_status_update("EQMP2"s, std::to_string(L2));
    raise_status_update("EQMP3"s, std::to_string(L3));
}

void MEg40::EQMM(uint16_t L1, uint16_t L2, uint16_t L3)
{
    raise_status_update("EQMM1"s, std::to_string(L1));
    raise_status_update("EQMM2"s, std::to_string(L2));
    raise_status_update("EQMM3"s, std::to_string(L3));
}

void MEg40::voltage_range_update(uint16_t U_10)
{
    raise_status_update("RangeU"s, std::to_string(U_10 * 10));
}

void MEg40::power_range_update(uint16_t I)
{
    raise_status_update("RangeI"s, std::to_string(I));
}

} /* namespace myapp::modbus::devices */
