#include "relay485.h"

using namespace std::string_literals;

namespace myapp::modbus::devices {

relay485::relay485(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))
  , relay_status_(8)
  , relay_status_poll_()
{
}

void relay485::initialize()
{
    relay_status_poll_ = [self = shared_from_base<relay485>()]
    {
        try
        {
            auto vec = self->data_access_->read_holding_registers(self->slave_id_, 1, 8);
            self->relay_status_update(vec);
        }
        catch (std::runtime_error& e)
        {
            self->logger_->warning(self->get_name(), "Reading relay status failed with error: "s + e.what());
        }

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

    executor_(relay_status_poll_);
}

std::string relay485::handle_command_impl(std::string const& str)
{
    if (str == "ch1 on") return turn_relay_on(1), std::string{};
    if (str == "ch2 on") return turn_relay_on(2), std::string{};
    if (str == "ch3 on") return turn_relay_on(3), std::string{};
    if (str == "ch4 on") return turn_relay_on(4), std::string{};
    if (str == "ch5 on") return turn_relay_on(5), std::string{};
    if (str == "ch6 on") return turn_relay_on(6), std::string{};
    if (str == "ch7 on") return turn_relay_on(7), std::string{};
    if (str == "ch8 on") return turn_relay_on(8), std::string{};

    if (str == "ch1 off") return turn_relay_off(1), std::string{};
    if (str == "ch2 off") return turn_relay_off(2), std::string{};
    if (str == "ch3 off") return turn_relay_off(3), std::string{};
    if (str == "ch4 off") return turn_relay_off(4), std::string{};
    if (str == "ch5 off") return turn_relay_off(5), std::string{};
    if (str == "ch6 off") return turn_relay_off(6), std::string{};
    if (str == "ch7 off") return turn_relay_off(7), std::string{};
    if (str == "ch8 off") return turn_relay_off(8), std::string{};

    throw std::runtime_error("No such known command");
}

void relay485::turn_relay_on(int ch)
{
    logger_->notice(get_name(), "Turning on relay "s + std::to_string(ch));
    executor_([self = shared_from_base<relay485>(), ch]
    {
        try {
            self->data_access_->write_single_register(self->slave_id_, ch, 0x100);
        }
        catch (std::runtime_error& e) {
            self->logger_->warning(self->get_name(), "Turning on relay failed with error: "s + e.what());
        }
    });
}

void relay485::turn_relay_off(int ch)
{
    logger_->notice(get_name(), "Turning off relay "s + std::to_string(ch));
    executor_([self = shared_from_base<relay485>(), ch]
    {
        try {
            self->data_access_->write_single_register(self->slave_id_, ch, 0x200);
        }
        catch (std::runtime_error& e) {
            self->logger_->warning(self->get_name(), "Turning off relay failed with error: "s + e.what());
        }
    });
}

void relay485::relay_status_update(std::vector<uint16_t> const& vec)
{
    for (auto i = 0u; i < vec.size(); ++i)
    {
        auto const i_state = !!vec[i];
        if (relay_status_[i] != i_state)
        {
            relay_status_[i] = i_state;

            auto ch_no = "ch"s + std::to_string(1+i);
            auto i_state_string = std::to_string(i_state);

            logger_->notice(get_name(), "Relay "s + ch_no + " status changed to "s + i_state_string);
            raise_status_update(std::move(ch_no), std::move(i_state_string));
        }
    }
}

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