#pragma once
#include "Singleton.h"
#include "l_squirrel.h"
#include <iostream>
#include <asio.hpp>

extern HSQUIRRELVM v;
extern std::recursive_mutex SqMtx;

using namespace asio;
using asio::ip::tcp;

class Client
{
private:
    asio::io_context io_context;
    tcp::resolver resolver;
    tcp::socket socket;
    tcp::resolver::results_type endpoint;
    std::array<char, 8192> buffer;
    std::array<char, 8192000> PackData;
    std::size_t writeDataSize = 0;
    std::size_t receivedDataSize = 0;
    std::size_t TestCode = 0;
    std::string Ip = "192.168.200.27";
    // std::string Ip = "127.0.0.1";
    std::string Port = "65109";
    std::mutex PackMtx;

public:
    bool ConnectState = false;

public:
    Client(asio::io_context &io_context, std::string Ip, std::string Port)
        : resolver(io_context),
          socket(io_context)
    {
        endpoint = resolver.resolve(Ip, Port);
        this->Ip = Ip;
        this->Port = Port;
        PackData.fill(0);
    }

    void start()
    {
        async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &endpoint)
                      {
        if (!error) {
            ConnectState = true;
            read();
            std::lock_guard<std::recursive_mutex> lock(SqMtx);
            SQInteger top = sq_gettop(v); // saves the stack size before the call
            sq_pushroottable(v);          // pushes the global table
            sq_pushstring(v, _SC("OnGatewaySocketConnect"), -1);
            if (SQ_SUCCEEDED(sq_get(v, -2)))
            {                        // gets the field 'foo' from the global table
                sq_pushroottable(v); // push the 'this' (in this case is the global table)
                sq_call(v, 1, SQFalse, SQTrue); // calls the function
            }
            sq_settop(v, top); // restores the original stack size
            io_context.poll(); // 处理一次事件循环，避免主线程阻塞
        } else {
            std::cerr << "Error connecting to server: " << error.message() << std::endl;
        } });
    }

    void read()
    {
        socket.async_read_some(asio::buffer(buffer), [this](const asio::error_code &error, std::size_t bytes_transferred)
                               {
        if (!error) {
            PackMtx.lock();
            for (std::size_t i = 0; i < bytes_transferred; ++i)
            {
                PackData[writeDataSize + i] = buffer[i];
            }
            writeDataSize += bytes_transferred;
            PackMtx.unlock();
            read();
        } else {
            std::cerr << "Error reading data: " << error.message() << std::endl;
            reconnect();
        } });
    }

    void reconnect()
    {
        ConnectState = false;
        std::cout << "服务器断开连接,尝试重连." << std::endl;
        // 执行重连操作
        // 关闭当前连接
        socket.close();
        // 重新解析端点
        endpoint = resolver.resolve(Ip, Port);
        // 重新连接
        async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &endpoint)
                      {
            if (!error) {
                ConnectState = true;
                std::lock_guard<std::recursive_mutex> lock(SqMtx);
                SQInteger top = sq_gettop(v); // saves the stack size before the call
                sq_pushroottable(v);          // pushes the global table
                sq_pushstring(v, _SC("OnGatewaySocketConnect"), -1);
                if (SQ_SUCCEEDED(sq_get(v, -2)))
                {                                   // gets the field 'foo' from the global table
                    sq_pushroottable(v);            // push the 'this' (in this case is the global table)
                    sq_call(v, 1, SQFalse, SQTrue); // calls the function
                }
                sq_settop(v, top); // restores the original stack size

                read();
            } else {
                std::cerr << "Error reconnecting to server: " << error.message() << std::endl;
                // 可以在此处添加重连失败的处理逻辑
                reconnect();
            } });
    }

    void send(unsigned char *message, int Length)
    {
        async_write(socket, asio::buffer(message, Length), [this](const asio::error_code &error, std::size_t bytes_transferred)
                    {
        if (!error) {
            // std::cout << "Message sent" << std::endl;
        } else {
            std::cerr << "Error sending message: " << error.message() << std::endl;
        } });
    }

    void PackLogic()
    {
        // 如果包长度不够直接返回
        if (PackData.size() < 4)
            return;
        // 如果里面已经读了超过一半的大小了
        if (writeDataSize >= 40960)
        {
            PackMtx.lock();
            // 删除前XX个元素
            std::copy(PackData.begin() + receivedDataSize, PackData.end(), PackData.begin());
            // 将最后16个元素设置为0
            std::fill(PackData.end() - receivedDataSize, PackData.end(), 0);
            // 写的大小重置
            writeDataSize -= receivedDataSize;
            // 读的大小重置
            receivedDataSize = 0;
            PackMtx.unlock();
        }

        unsigned char *Count = new unsigned char[4];
        std::copy(PackData.begin() + receivedDataSize, PackData.begin() + 4 + receivedDataSize, Count);
        int PackSize = ByteLittleToInt(Count);
        delete[] Count;
        // 如果长度不够直接返回
        if (PackSize <= 0)
            return;
        char *StrBuffer = new char[PackSize + 1];
        std::copy(PackData.begin() + 4 + receivedDataSize, PackData.begin() + 4 + PackSize + receivedDataSize, StrBuffer);
        StrBuffer[PackSize] = '\0';
        // 这次读了多少
        receivedDataSize += (4 + PackSize);

        std::string Str = StrBuffer;
        delete[] StrBuffer;
        // std::cout << "包大小:  " << PackSize << std::endl;
        // std::cout << "包内容:  " << Str << std::endl;
        // std::cout << "收到了第:  " << TestCode << "个包" << std::endl;

        std::lock_guard<std::recursive_mutex> lock(SqMtx);
        SQInteger top = sq_gettop(v); // saves the stack size before the call
        sq_pushroottable(v);          // pushes the global table
        sq_pushstring(v, _SC("OnGatewaySocketMsg"), -1);
        if (SQ_SUCCEEDED(sq_get(v, -2)))
        {                        // gets the field 'foo' from the global table
            sq_pushroottable(v); // push the 'this' (in this case is the global table)
            sq_pushstring(v, Str.c_str(), -1);
            sq_call(v, 2, SQFalse, SQTrue); // calls the function
        }
        sq_settop(v, top); // restores the original stack size

        TestCode++;
    }

    static int ByteLittleToInt(unsigned char *Count)
    {
        int int1 = Count[0] & 0xff;
        int int2 = (Count[1] & 0xff) << 8;
        int int3 = (Count[2] & 0xff) << 16;
        int int4 = (Count[3] & 0xff) << 24;

        return int1 | int2 | int3 | int4;
    }
};

class l_socket
{
public:
private:
    l_socket() {}                                   // private constructor to prevent instantiation
    l_socket(const l_socket &) = delete;            // disable copy constructor
    l_socket &operator=(const l_socket &) = delete; // disable assignment operator
    Client *ClientObj;

public:
    bool InitState = false;
    static l_socket &getInstance()
    {
        static l_socket instance;
        return instance;
    }

    void InitSqr();
    void InitPackLogic();
    void Init(std::string Ip, std::string Port);
    void Send(const SQChar *Pck);
    void Logic();
    void IntToByteLittle(unsigned char *b, int Count);

public:
};
