#include <myapp/logging/loggerfactory.h>
#include <myapp/modbus/rawtcpserver.h>
#include <myapp/modbus/rawtcpclient.h>
#include <exception>
#include <iostream>
#include <limits>

using namespace myapp::logging;
using namespace myapp::modbus;

static uint8_t string_to_byte(std::string const& str)
{
    auto num = std::stoi(str);
    if (num < std::numeric_limits<uint8_t>::min() || num > std::numeric_limits<uint8_t>::max())
        throw std::out_of_range("slave id is out of range");

    return uint8_t(num);
}

static void inner_main(std::string tcp_cli_str, std::string tcp_srv_str, std::string slave_id)
{
	auto logger = logger_factory::create_console_logger();
	auto mbtcpsrv = raw_tcp_server::create(logger, tcp_srv_str);
	auto mbtcpcli = raw_tcp_client::create(logger, tcp_cli_str);
    std::exception_ptr eptr;

	mbtcpsrv->set_data_handler([&, slave_id = string_to_byte(slave_id)](raw_data& adu) -> std::unique_ptr<raw_data>
	{
		try 
        {
            *adu.data() = slave_id;
			return mbtcpcli->request(adu);
		}
        catch (std::system_error const& e)
        {
            // BUG fixed in gcc-6.5, gcc-7.4, gcc-8.3, gcc-9.1
            if (e.code() == std::errc::broken_pipe)
                eptr = std::make_exception_ptr(e);

            return { nullptr };
        }
		catch (std::runtime_error const&) 
        {
			return { nullptr };
		}
	});

	mbtcpsrv->listen(1);
	mbtcpcli->connect();

    do
        mbtcpsrv->poll();
    while (!eptr);

    if (eptr)
    {
        std::rethrow_exception(eptr);
    }
}

int main(int argc, char* argv[])
{
	if (argc != 4)
	{
		std::cerr << "Usage: modbustcpslave tcp:127.0.0.1:502 tcp:0.0.0.0:30001 1" << std::endl;
		return 1;
	}

	try
	{
		inner_main(argv[1], argv[2], argv[3]);
	}
	catch (std::exception& e)
	{
		std::cerr << e.what() << std::endl;
		return 1;
	}

	return 0;
}
