#include "rawrtuclient.h"
#include "utils/stringcompare.h"

using namespace std::string_literals;

namespace myapp::modbus {

static void checked(int result, std::string const& prefix)
{
	if (result == -1)
		throw std::runtime_error(prefix + ": " + ::modbus_strerror(errno));
}

std::shared_ptr<raw_rtu_client> raw_rtu_client::create(std::shared_ptr<logging::logger> logger, std::string const& connection_string)
{
	try
	{
		if (!utils::starts_with(connection_string, "rtu:"))
			throw std::runtime_error("Not a RTU connection string");

		context ctx(connection_string);
		logger->info("MODBUS RTU", "Created modbus context "s + ctx.description());
		return std::make_shared<raw_rtu_client_impl>(std::move(logger), std::move(ctx));
	}
	catch (std::runtime_error& e)
	{
		logger->error("MODBUS RTU", "Unable to initialize modbus context: "s + e.what());
		throw;
	}
}

raw_rtu_client_impl::raw_rtu_client_impl(std::shared_ptr<logging::logger> logger, context ctx)
  : ctx_(std::move(ctx))
  , logger_(std::move(logger))
{
	modbus_set_error_recovery(ctx_, MODBUS_ERROR_RECOVERY_PROTOCOL);
}

raw_rtu_client_impl::~raw_rtu_client_impl()
{
	modbus_close(ctx_);
}

void raw_rtu_client_impl::connect()
{
	checked(modbus_connect(ctx_), "Connect");
}

std::unique_ptr<raw_data> raw_rtu_client_impl::request(raw_data& adu)
{
	try 
	{
		checked(modbus_send_raw_request(ctx_, adu.data(), adu.data_length()), "rtu send raw request");

		int rc;
		auto response = raw_data::rtu();
		checked(rc = modbus_receive_confirmation(ctx_, &(*response)[0]),
			"rtu receive confirmation");
		if (rc == 0)
			return { nullptr };

		response->size(rc);
		return response;
	}
	catch (std::runtime_error& e)
	{
		logger_->info("MODBUS RTU", "Raw request: "s + e.what());
		throw;
	}
}


}