#include "net_base.h"

CTcpClient::CTcpClient(asio::io_context& io_context) : io_context_(io_context), socket_(io_context_)
{
}

CTcpClient::~CTcpClient() = default;

bool CTcpClient::connect(const std::string& host, const std::string& port)
{
    try {
        asio::ip::tcp::resolver resolver(io_context_);
        asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, port);
        asio::connect(socket_, endpoints);
        mpinfo("Connected to {}:{}", host, port);
        is_normal_ = true;
        return true;
    } catch (const std::exception& ex) {
        mperror("Connection failed: {}", ex.what());
        return false;
    }
}

void CTcpClient::disconnect()
{
    if (socket_.is_open()) {
        try {
            socket_.shutdown(asio::ip::tcp::socket::shutdown_both);
            socket_.close();
            mpinfo("Disconnected.");
        } catch (const std::exception& ex) {
            mperror("Error during disconnection: {}", ex.what());
        }
    }
}

bool CTcpClient::send(const char* data, int len)
{
    if (!is_normal_) {
        mperror("abnormal state, will not send.");
        return false;
    }
    try {
        auto send_size = asio::write(socket_, asio::buffer(data, len));
        // mpinfo("Need Send len: {} Real Send len: {}", len, send_size);
        return static_cast<int>(send_size) == len;
    } catch (const std::exception& ex) {
        mperror("Send failed: {}", ex.what());
        return false;
    }
}

void CTcpClient::register_func(ExFun_t&& f)
{
    fun_ = f;
}

void CTcpClient::async_recv()
{
    auto self(shared_from_this());
    socket_.async_read_some(asio::buffer(tmp_buf_), [this, self](std::error_code ec, std::size_t length) {
        if (ec) {
            is_normal_ = false;
            if (ec.value() == 995) {
                // 正常中止退出
                return;
            }
            if (ec.value() == 125) {
                mpinfo("{} exit.", __FUNCTION__);
                return;
            }
            mperror("{} {} error => {}", __FUNCTION__, ec.value(), ec.message());
        } else {
            buffer_.push(tmp_buf_.data(), length);
            while (true) {
                auto* frame = CTransProtocal::parse(buffer_);
                if (frame) {
                    if (fun_) {
                        fun_(frame);
                    }
                    delete frame;
                    continue;
                }
                break;
            }
            async_recv();
        }
    });
}

bool CTcpClient::is_normal() const
{
    return is_normal_;
}
