#include "communication/SimpleCommandParser.h"
#include "util/AggreReadableByteChannel.h"
#include "util/Log.h"
#include "Cres.h"
#include "IOException.h"
#include <boost/thread.hpp>

SimpleCommandParser::SimpleCommandParser(AggreReadableByteChannelPtr channel, unsigned char startChar, unsigned char endChar)
    : AbstractCommandParser()
{
    init(channel, startChar, endChar);
}

SimpleCommandParser::SimpleCommandParser(AggreReadableByteChannelPtr channel, unsigned char startChar,
                                         unsigned char endChar, unsigned char endChar2, bool needBoth)
    : AbstractCommandParser()
{
    init(channel, startChar, endChar);
    this->endChar2 = endChar2;
    this->needBoth = needBoth;
}

void SimpleCommandParser::close()
{
    inputChannel->close();
}


void SimpleCommandParser::init(AggreReadableByteChannelPtr channel, unsigned char startChar, unsigned char endChar)
{
    this->inputChannel = channel;
    this->startChar = startChar;
    this->endChar = endChar;
    this->needBoth = false;
    this->waitingEndChar2 = false;
    this->started = false;
    this->full = false;

    clearCommand();
}

void SimpleCommandParser::clearCommand()
{
    started = !startChar.is_initialized();
    waitingEndChar2 = false;
    full = false;
    reset();
}

CommandPtr SimpleCommandParser::readCommand()
{
    int read;

    try {
        if (processBufferContents()) {
            return buildCommand();
        }

        while (true) {

            try {
                read = inputChannel->read(streamBuf);
            }
            catch(...)
            {
                read = -1;
                break;
            }

            if (read > 0) {
                CommandParserListener* listener = getListener();
                if (listener != NULL) {
                    listener->newDataReceived();
                }
            }else {
                break;
            }

            if (processBufferContents()) {
                return buildCommand();
            }
        }

        if (read == -1) {
            inputChannel->close();
            throw DisconnectionException(Cres::get()->getString("disconnected"));
        }

        return buildCommand();
    }catch (IOException ex) {
        throw ex;
    }
}

CommandPtr SimpleCommandParser::buildCommand()
{
    if (isFull()) {
        CommandPtr command = createCommandFromBufferContent();
        clearCommand();
        return command;
    }else {
        return CommandPtr();
    }
}

bool SimpleCommandParser::processBufferContents()
{
    std::istream stream(&streamBuf);

    char cur;
    while (stream.get(cur))
    {
        if (processByte(cur)) {
            return true;
        }
    }

    return false;
}

bool SimpleCommandParser::processByte(unsigned char cur)
{
    if (startChar.is_initialized() &&  cur == startChar) {
        started = true;
        reset();
        //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString("Cleared command buffer"));
    }else {
        if (started) {
            if (waitingEndChar2) {
                if (cur == endChar2) {
                    //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString("Command buffer is full, command is now complete"));
                    full = true;
                    return true;
                }
            }else {
                if (cur == endChar || (endChar2.is_initialized() && !needBoth && cur == endChar2)) {
                    if (endChar2.is_initialized() && needBoth) {
                        //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString("Received end char 1, waiting for end char 2"));
                        waitingEndChar2 = true;
                        full = false;
                        return false;
                    }else {
                        //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString("Command buffer is full, waiting for end char 2 disabled, command is now complete"));
                        full = true;
                        return true;
                    }
                }else {
                    //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString("Received: ")  +  AgString((char)cur));
                    addData(cur);
                }
            }
        }else {
            //AGDEBUG(DBG_DEBUG, "COMMANDS", AgString()+"Data byte '" + AgString((char)cur) + "' discarded in command mode, buffer: " + toString());
        }
    }

    return false;
}

bool SimpleCommandParser::isFull()
{
    return full;
}
