#include "ClientCore.h"
#include <InfoEnhance.hpp>

ClientCore::ClientCore()
{
    socket_ = new wxSocketClient();
}

ClientCore::~ClientCore()
{
    Disconnect();
    thRun_ = false;
    if (recvThread_.joinable()) {
        recvThread_.join();
    }
}

bool ClientCore::Connect(const wxString& host, uint16_t port)
{
    wxIPV4address addr;
    addr.Hostname(host);
    addr.Service(port);

    socket_->SetEventHandler(*this, wxID_ANY);
    socket_->SetNotify(wxSOCKET_LOST_FLAG);
    socket_->SetFlags(wxSOCKET_BLOCK);
    socket_->Notify(true);

    if (socket_->IsConnected()) {
        return true;
    }

    if (!socket_->Connect(addr)) {
        return false;
    }
    Bind(wxEVT_SOCKET, &ClientCore::OnSocketEvent, this);

    if (recvThread_.joinable()) {
        recvThread_.join();
    }
    thRun_ = true;
    recvThread_ = std::thread(&ClientCore::Recv, this);
    return true;
}

void ClientCore::Disconnect()
{
    socket_->Close();
}

wxString ClientCore::GetErr() const
{
    return err_;
}

wxString ClientCore::GetOwnId() const
{
    return id_;
}

bool ClientCore::IsOk()
{
    return socket_->IsConnected();
}

void ClientCore::SetLogCallback(const std::function<void(const wxString&)>& callback)
{
    logCall_ = callback;
}

bool ClientCore::ReqOnline()
{
    InfoCommunicate infoCommunicate;
    if (!Send<InfoCommunicate>(infoCommunicate, FBT_SER_MSG_ASKCLIENTS)) {
        return false;
    }
    return true;
}

void ClientCore::ReqOnlineCallback(const std::function<void(const InfoClientVec&)>& callback)
{
    onlineCallback_ = callback;
}

void ClientCore::SetHomeCallback(const std::function<void(const wxString&)>& callback)
{
    homeCall_ = callback;
}

void ClientCore::SetDirFileCallback(const std::function<void(const DirFileInfoVec&)>& callback)
{
    dirFileCall_ = callback;
}

bool ClientCore::AskDirectory(const wxString& id, const wxString& path, DirFileInfoVec& dirInfoVec)
{
    return false;
}

void ClientCore::OnSocketEvent(wxSocketEvent& event)
{
    auto* sock = event.GetSocket();
    auto type = event.GetSocketEvent();
    switch (type) {
    case wxSOCKET_LOST: {
        logCall_(wxString::Format(_("Lost connection to server.")));
        break;
    }
    default:
        logCall_(wxString::Format(_T("No Handled Type: %d."), static_cast<int>(type)));
        break;
    }
}

void ClientCore::UseFrame(FrameBuffer* buf)
{
    std::stringstream ss;
    switch (buf->dataType) {
    case FBT_SER_MSG_ASKCLIENTS: {
        InfoClientVec vec;
        ZeroCopyInput input(buf->dataMut, buf->len);
        input.archive() >> vec;
        logCall_(wxString::Format(_("Online clients: %d."), static_cast<int>(vec.vec.size())));
        if (onlineCallback_) {
            onlineCallback_(vec);
        }
        break;
    }
    case FBT_SER_MSG_YOURID: {
        InfoCommunicate info;
        ZeroCopyInput input(buf->dataMut, buf->len);
        input.archive() >> info;
        id_ = wxString(info.msg.c_str());
        logCall_(wxString::Format(_("Your id is %s."), id_));
        break;
    }
    case FBT_SER_MSG_RESPONSE: {
        InfoCommunicate info;
        break;
    }
    case FBT_CLI_ASK_HOME: {
        InfoCommunicate info;
        wxString home = wxGetHomeDir();
        info.msg = home.ToStdWstring();
        Send<InfoCommunicate>(info, FBT_CLI_ANS_HOME, buf->fid);
        break;
    }
    case FBT_CLI_ANS_HOME: {
        InfoCommunicate info;
        ZeroCopyInput input(buf->dataMut, buf->len);
        input.archive() >> info;
        if (homeCall_) {
            homeCall_(wxString(info.msg.c_str()));
        }
        break;
    }
    case FBT_CLI_ASK_DIRFILE: {
        InfoCommunicate info;
        ZeroCopyInput input(buf->dataMut, buf->len);
        input.archive() >> info;
        DirFileInfoVec vec;
        if (!cf_.GetDirFiles(info.msg, vec)) {
            break;
        }
        Send<DirFileInfoVec>(vec, FBT_CLI_ANS_DIRFILE, buf->fid);
        break;
    }
    case FBT_CLI_ANS_DIRFILE: {
        DirFileInfoVec vec;
        ZeroCopyInput input(buf->dataMut, buf->len);
        input.archive() >> vec;
        if (dirFileCall_) {
            dirFileCall_(vec);
        }
        break;
    }
    default:
        break;
    }
}

void ClientCore::HeartBeat()
{
}

void ClientCore::Recv()
{
    while (thRun_) {
        socket_->Read(buf_.data(), GBUFFER_SIZE);
        auto len = socket_->LastCount();
        if (len == 0) {
            break;
        }
        buffer_.Push(buf_.data(), len);
        while (true) {
            auto* frame = Communicate::ParseBuffer(buffer_);
            if (frame == nullptr) {
                break;
            }
            UseFrame(frame);
            delete frame;
        }
    }
}

bool ClientCore::Send(FrameBuffer* buf)
{
    if (buf == nullptr) {
        return false;
    }
    char* od = nullptr;
    int odLen = 0;
    if (!Communicate::PackBuffer(buf, &od, odLen)) {
        return false;
    }

    socket_->Write(od, odLen);
    if (socket_->Error()) {
        delete[] od;
        wxLogError(wxT("Send error: %s"), socket_->LastError());
        return false;
    }
    delete[] od;
    return true;
}