#include "datatable/encoding/TransferEncodingHelper.h"
#include "protocol/AggreGateCommandUtils.h"
#include "datatable/DataTableUtils.h"

std::map<AgChar, AgChar> TransferEncodingHelper::DIRECT = TransferEncodingHelper::initDIRECT();
std::map<AgChar, AgChar> TransferEncodingHelper::REVERSE = TransferEncodingHelper::initREVERSE();

#ifdef __GNUC__
const AgChar TransferEncodingHelper::ESC_CHAR;
const AgChar AggreGateCommandUtils::CLIENT_COMMAND_SEPARATOR;
const AgChar TransferEncodingHelper::ELEMENT_START;
const AgChar TransferEncodingHelper::ELEMENT_END;
const AgChar TransferEncodingHelper::ELEMENT_NAME_VALUE_SEPARATOR;
const AgChar TransferEncodingHelper::ELEMENT_VISIBLE_START;
const AgChar TransferEncodingHelper::ELEMENT_VISIBLE_END;
const AgChar TransferEncodingHelper::ELEMENT_VISIBLE_NAME_VALUE_SEPARATOR;
#endif

std::map<AgChar, AgChar> TransferEncodingHelper::initDIRECT()
{
    std::map<AgChar, AgChar> d;
    d[TransferEncodingHelper::ESC_CHAR] = '%';
    d[AggreGateCommandUtils::CLIENT_COMMAND_SEPARATOR] = '/';
    d[TransferEncodingHelper::ELEMENT_START] = DataTableUtils::ELEMENT_VISIBLE_START;
    d[TransferEncodingHelper::ELEMENT_END] = DataTableUtils::ELEMENT_VISIBLE_END;
    d[TransferEncodingHelper::ELEMENT_NAME_VALUE_SEPARATOR] = DataTableUtils::ELEMENT_VISIBLE_NAME_VALUE_SEPARATOR;
    d[AggreGateCommand::START_CHAR] = '^';
    d[AggreGateCommand::END_CHAR] = '$';
    return d;
}

std::map<AgChar, AgChar> TransferEncodingHelper::initREVERSE()
{
    std::map<AgChar, AgChar> r;
    r['%'] = TransferEncodingHelper::ESC_CHAR;
    r['/'] = AggreGateCommandUtils::CLIENT_COMMAND_SEPARATOR;
    r[TransferEncodingHelper::ELEMENT_VISIBLE_START] = DataTableUtils::ELEMENT_START;
    r[TransferEncodingHelper::ELEMENT_VISIBLE_END] = DataTableUtils::ELEMENT_END;
    r[TransferEncodingHelper::ELEMENT_VISIBLE_NAME_VALUE_SEPARATOR] = DataTableUtils::ELEMENT_NAME_VALUE_SEPARATOR;
    r['^'] = AggreGateCommand::START_CHAR;
    r['$'] = AggreGateCommand::END_CHAR;
    return r;
}

AgString TransferEncodingHelper::encode(const AgString &s)
{
    if (s.empty())
    {
        return AgString();
    }

    int len = s.length();

    AgString out;

    for (int i = 0; i < len; i++)
    {
        AgChar c = s[i];

        if (TransferEncodingHelper::DIRECT.find(c) != TransferEncodingHelper::DIRECT.end())
        {
            out += TransferEncodingHelper::ESC_CHAR;
            out += TransferEncodingHelper::DIRECT[c];
        }
        else
        {
            out += c;
        }
    }

    return out;
}

AgString TransferEncodingHelper::decode(const AgString &s)
{
    if (s.empty())
    {
        return AgString();
    }

    int len = s.length();

    AgString out;

    for (int i = 0; i < len; i++)
    {
        AgChar c = s[i];

        if (c == TransferEncodingHelper::ESC_CHAR && i < len - 1)
        {
            AgChar next = s[i + 1];

            if (TransferEncodingHelper::REVERSE.find(next) != TransferEncodingHelper::REVERSE.end())
            {
              out += TransferEncodingHelper::REVERSE[next];
              i += 1;
            }
            else
            {
              // Failover code
              out += c;
              out += next;
            }
        }
        else
        {
            out += c;
        }
    }

    return out;
}
