#include "rconfacade.h"
#include <myapp/logging/loggerfactory.h>
#include <myapp/rcon/unixprovider.h>
#include "utils/stringtrim.h"
#include <iostream>
#include <unistd.h>

using namespace myapp::async;
using namespace myapp::cmdargs;
using namespace myapp::configuration;
using namespace myapp::logging;
using namespace myapp::rcon;

namespace myapp {

static void stop_loop(event_loop& loop)
{
    loop.stop();
}

rcon_facade::rcon_facade(options& opts, config& cfg)
  : options_(opts)
  , config_(cfg)
  , logger_(logger_factory::create_console_logger())
  , loop_(event_loop::create())
  , sigint_(signal::create(loop_))
  , sigterm_(signal::create(loop_))
  , stdin_(pipe::create(loop_))
  , rcon_(logger_, rcon::unix_provider::create(loop_))
{
    set_console_termination_handlers();
    redirect_console_to_remote();

    rcon_.connect(cfg.remote_console_unix_socket_path);
}

void rcon_facade::set_console_termination_handlers()
{
    sigint_->on(SIGINT, [this](int) { stop_loop(*loop_); });
    sigterm_->on(SIGTERM, [this](int) { stop_loop(*loop_); });
}

void rcon_facade::redirect_console_to_remote()
{
    stdin_->on_error([this](auto async_result)
    {
        try { async_result(); }
        catch (std::exception& e) { std::cerr << e.what() << std::endl; }

        stop_loop(*loop_);
    });

    stdin_->open(STDIN_FILENO);

    class stop_helper_t
    {
        bool transaction_in_progress = false;
        bool stop_requested = false;
    public:
        void begin_transaction() noexcept { transaction_in_progress = true; }
        void end_transaction() noexcept { transaction_in_progress = true; }
        void request_stop() noexcept { stop_requested = true; }
        operator bool() const noexcept { return !transaction_in_progress && stop_requested; }
    };

    auto stop_helper = std::make_shared<stop_helper_t>();

    rcon_.set_on_connected([=] {
        stdin_->read([=] (std::string data)
        {
            utils::trim(data);
            if (data.empty()) 
                return;

            stop_helper->begin_transaction(); 
            rcon_.write(std::move(data));
        }, [=]
        {
            stop_helper->request_stop();

            if (*stop_helper) 
                stop_loop(*loop_);
        });
    });

    rcon_.set_on_closed([this] { stop_loop(*loop_); });
    rcon_.set_on_data([this, stop_helper](std::string data)
    {
        std::cout << data << std::endl;
        stop_helper->end_transaction();

        if (*stop_helper) 
            stop_loop(*loop_);
    });
}

void rcon_facade::loop()
{
    loop_->run();
}


} /* namespace myapp */
