#include "Client.hpp"
#include "Server.hpp"
#include "error.hpp"
#include "log.hpp"
#include <unistd.h>
#include <cassert>

Client::Client(Server& server):
    _server(server),
    _addrsize(sizeof(_addr)),
    _addr(),
    _fd(-1),
    _buffer(),
    _pass(false),
    _registered(false),
    _deleted(false),
    _welcomed(false),
    _channelsInvitedTo(),
    nick(""),
    username(""),
    realname("")
    {}

Client::~Client()
{
    if (_deleted) {
        return;
    }
    _deleted = true;
    if (_server.isLive()) {
        _server.removeClient(*this);
    }
    if (_fd >= 0) {
        close(_fd);
        LOG << "client disconnected" << std::endl;
        _fd = -1;
    }
}

int
Client::init(int servfd)
{
    _fd = accept(servfd, &_addr, &_addrsize);
    if (_fd < 0) {
        error("accept");
        return -1;
    }
    return 0;
}

int
Client::getFd() const
{
    return _fd;
}

int
Client::recv()
{
    char buf[4096] = {};
    const int rv = ::recv(getFd(), buf, sizeof(buf) - 1, 0);
    if (rv < 0) {
        error("recv");
        _server.removeClient(*this);
        return -1;
    }
    if (rv == 0) {
        /* disconnection */
        _server.quitClient(*this);
        return 1;
    }

    _buffer += std::string(buf);
    return 0;
}

bool
Client::queuedMsg() const
{
    return (_buffer.find("\r\n") != std::string::npos);
}

std::string
Client::pollMsg()
{
    const std::size_t div = _buffer.find("\r\n");
    assert(div != std::string::npos);

    const std::string msg = _buffer.substr(0, div);
    _buffer = _buffer.substr(div + 2);

    return msg;
}

int
Client::sendCode(const std::string& code, const std::string& msg)
{
    const std::string out = code + " :" + msg + "\r\n";
    if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
        LOG << "send failed cringe kekw" << std::endl;
        _server.removeClient(*this);
        return -1;
    }
    return 0;
}

int
Client::sendCode(const std::string& code, const std::string& msg0, const std::string& msg1)
{
    const std::string out = code + " " + msg0 + " :" + msg1 + "\r\n";
    if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
        LOG << "send failed cringe kekw" << std::endl;
        _server.removeClient(*this);
        return -1;
    }
    return 0;
}

void
Client::fatalSendCode(const std::string &code, const std::string &msg)
{
    if (!sendCode(code, msg)) {
        _server.removeClient(*this);
    }
}

int
Client::sendMsg(std::string const& sender, std::string const& cmd,
        std::string const& trailing)
{
    std::string const out = ':' + sender + ' ' + cmd + " :" + trailing + "\r\n";
    if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
        LOG << "send failed cringe kekw" << std::endl;
        _server.removeClient(*this);
        return -1;
    }
    return 0;
}

int
Client::sendRaw(std::string const& msg)
{
    if (::send(_fd, msg.c_str(), msg.length(), 0) != (ssize_t)msg.length()) {
        LOG << "send failed cringe kekw" << std::endl;
        _server.removeClient(*this);
        return -1;
    }
    return 0;
}

bool
Client::getPass() const
{
    return _pass;
}

void
Client::setPass()
{
    _pass = true;
}

bool
Client::getRegistered() const
{
    return _registered;
}

void
Client::setRegistered()
{
    _registered = true;
}

bool
Client::getWelcomed() const
{
    return _welcomed;
}

void
Client::setWelcomed()
{
    _welcomed = true;
}

void
Client::addInvite(Channel const& channel)
{
    _channelsInvitedTo.insert(channel.getName());
}

bool
Client::isInvited(Channel const& channel)
{
    return _channelsInvitedTo.erase(channel.getName());
}

bool
Client::operator==(Client const& rhs) const
{
    return this == &rhs;
}

bool
Client::operator==(std::string const& nick) const
{
    return this->nick == nick;
}
