#include "communication/Command.h"
#include "communication/SocketDisconnectionException.h"
#include "device/DisconnectionException.h"
#include "util/StringUtils.h"
#include "IOException.h"
#include "util/BlockingChannel.h"
#include "Cres.h"

Command::Command()
{
    timeout_ = 0;
}

Command::Command(const AgString & data)
{
    timeout_ = 0;
    add(data);
}

Command::Command(const std::vector<unsigned char> &data)
{
    timeout_ = 0;
    write(&data[0], 0, data.size());
}

AgString Command::header()
{
    return "";
}

AgString Command::footer()
{
    return "";
}

AgString Command::toString()
{
    return getContent();
}

AgString Command::getContent()
{
    return AgString::fromBytes(buf_, UTF8_CHARSET);
}

void Command::add(const AgString & data)
{
    std::vector<unsigned char> utf8data = data.getBytes(UTF8_CHARSET);
    write(&utf8data[0], 0, utf8data.size());
}

void Command::send(BlockingChannelPtr byteChannel)
{
    send(byteChannel, true);
}

void Command::send(BlockingChannelPtr byteChannel, bool encapsulate)
{
    if (byteChannel == NULL || !byteChannel->isOpen()){
        throw DisconnectionException(Cres::get()->getString("disconnected"));
    }

    try
    {
        AgString header = encapsulate ? this->header() : "";
        AgString footer = encapsulate ? this->footer() : "";

        int size = header.length() + buf_.size() + footer.length();

        std::vector<unsigned char> buff;

        if (!header.empty()) {
            std::vector<unsigned char> head = header.getBytes(UTF8_CHARSET);
            std::copy(head.begin(), head.end(), std::back_inserter(buff));
            head.clear();
        }

        std::copy(buf_.begin(), buf_.end(), std::back_inserter(buff));
        buf_.clear();

        if (!footer.empty()) {
            std::vector<unsigned char> foot = footer.getBytes(UTF8_CHARSET);
            std::copy(foot.begin(), foot.end(), std::back_inserter(buff));
            foot.clear();
        }

        int sent = 0;
        do {
            sent += byteChannel->write(buff);
        }while (sent < size);

        buff.clear();

    }catch (SocketDisconnectionException ex) {
        throw DisconnectionException(Cres::get()->getString("disconnected"), ex.getMessage());
    }catch (IOException ex) {
        throw ex;
    }
}

AgString Command::getId()
{
    return "";
}

bool Command::isAsync()
{
    return false;
}

int64_t Command::getTimeout()
{
    return timeout_;
}

void Command::setTimeout(int64_t  timeout)
{
    this->timeout_ = timeout;
}

void Command::write(unsigned char data)
{
    buf_.push_back(data);
}

void Command::write(const unsigned char* data, int off_src, int size)
{
    if (data == NULL)
        return;

    if ((off_src < 0) || (off_src > size) || ((off_src+size) < 0) )
        throw IOException("Bad index");

    std::copy(data+off_src, data+size, std::back_inserter(buf_));
}

int Command::size()
{
    return buf_.size();
}
