#include "HeaderValueParamsParser.h"

namespace panda { namespace protocol { namespace websocket {

static const int MAX_HEADER_VALUE = 8*1024;
using uchar = unsigned char;
static bool header_value_special[256];

static int init () {
    header_value_special[(uchar)';'] = true;
    header_value_special[(uchar)','] = true;
    header_value_special[(uchar)'='] = true;
    header_value_special[(uchar)' '] = true;
    return 0;
}
static const auto __init = init();


void parse_header_value(const string& strval, HeaderValues& values) {
    auto cur = strval.begin();
    auto end = strval.end();

    enum { PARSE_MODE_NAME, PARSE_MODE_KEY, PARSE_MODE_VAL } mode = PARSE_MODE_NAME;
    char accstr[MAX_HEADER_VALUE];
    string key;
    char* acc = accstr;
    HeaderValue* elem = NULL;

    for (; cur != end; ++cur) {
        char c = *cur;
        if (!header_value_special[(uchar)c]) {
            *acc++ = c;
            continue;
        }
        if (c == ' ') continue;

        if (mode == PARSE_MODE_NAME) {
            if (c == ';' || c == ',') {
                auto sz = values.size();
                values.resize(sz+1);
                elem = &values[sz];
                elem->name.assign(accstr, acc-accstr);
                acc = accstr;
                if (c == ';') mode = PARSE_MODE_KEY;
            }
            else *acc++ = c;
        }
        else if (mode == PARSE_MODE_KEY) {
            if (c == ';' || c == ',') {
                elem->params.emplace(string(accstr, acc-accstr), string());
                acc = accstr;
                if (c == ',') mode = PARSE_MODE_NAME;
            }
            else if (c == '=') {
                key.assign(accstr, acc-accstr);
                acc = accstr;
                mode = PARSE_MODE_VAL;
            }
            else *acc++ = c;
        }
        else { // PARSE_MODE_VAL
            if (c == ';' || c == ',') {
                elem->params.emplace(key, string(accstr, acc-accstr));
                acc = accstr;
                if (c == ',') mode = PARSE_MODE_NAME;
                else mode = PARSE_MODE_KEY;
            }
            else *acc++ = c;
        }
    }

    // finish
    if      (mode == PARSE_MODE_NAME) values.push_back(HeaderValue{string(accstr, acc-accstr), HeaderValueParams()});
    else if (mode == PARSE_MODE_KEY)  elem->params.emplace(string(accstr, acc-accstr), string());
    else    /* PARSE_MODE_VAL */      elem->params.emplace(key, string(accstr, acc-accstr));
}

string compile_header_value(const HeaderValues& values) {
    string str;

    for (const auto& elem : values) {
        str += elem.name;
        for (const auto& param : elem.params) {
            str += "; ";
            str += param.first;
            if (param.second) {
                str += '=';
                str += param.second;
            }
        }
        str += ", ";
    }

    if (str) str.length(str.length() - 2);

    return str;
}

}}}