#include "RequestParser.h"
#include "ResponseParser.h"

namespace panda { namespace protocol { namespace http {

#define ADD_DIGIT(dest) \
    dest *= 10;         \
    dest += *p - '0';
    
#define CURSTR     buffer.substr(mark, p - ps - mark)
#define SAVE(dest) dest = CURSTR

%%{
    machine cookie_rules;
    include rules "Rules.rl";
    
    action mark {
        mark = fpc - ps;
    }
    
    cookie_octet = any - (CTL | WSP | '"' | ',' | ';' | '\\');
    cookie_value = cookie_octet* | ('"' cookie_octet* '"');
}%%

%%{
    machine cookie_parser;
    include cookie_rules;

    cookie_pair = token >mark %{ cont.emplace_back(CURSTR, string()); }  "=" cookie_value >mark %{ SAVE(cont.back().value); };
    main       := cookie_pair ("; " cookie_pair)* OWS;
    
    write data;
}%%

void RequestParser::parse_cookie (const string& buffer) {
    const char* ps  = buffer.data();
    const char* p   = ps;
    const char* pe  = ps + buffer.size();
    const char* eof = pe;
    int         cs  = cookie_parser_start;
    auto&     cont  = request->cookies.fields;
    size_t mark;
    %% write exec;
}

%%{
    machine set_cookie_parser;
    include cookie_rules;
    
    cookie_pair = token >mark %{ cont.emplace_back(CURSTR, Response::Cookie()); v = &cont.back().value; }  "=" cookie_value >mark %{ SAVE(v->_value); };
    expires_av  = "Expires=" (alnum | SP | ":" | ",")* >mark %{ SAVE(v->_expires); }; # RFC 1123, will be lazy-parsed later by panda::date framework
    max_age_av  = "Max-Age=" ([1-9] digit*) >{ v->_max_age = 0; } ${ ADD_DIGIT(v->_max_age); };
    domain_av   = "Domain=" (alnum | "." | "-")+  >mark %{ SAVE(v->_domain); };
    path_av     = "Path=" ((any - CTL) - ";")+  >mark %{ SAVE(v->_path); };
    secure_av   = "Secure"  %{ v->_secure = true; };
    httponly_av = "HttpOnly"  %{ v->_http_only = true; };
    samesite_av = "SameSite" (
                      ""        %{ v->_same_site = Response::Cookie::SameSite::Strict; }
                    | "=Strict" %{ v->_same_site = Response::Cookie::SameSite::Strict; }
                    | "=Lax"    %{ v->_same_site = Response::Cookie::SameSite::Lax; }
                    | "=None"   %{ v->_same_site = Response::Cookie::SameSite::None; }
                  );
    extension_av = ((any - CTL) - ";")+;
    cookie_av    = expires_av | max_age_av | domain_av | path_av | secure_av | httponly_av | samesite_av | extension_av;
    main        := cookie_pair ("; " cookie_av)*;
    
    write data;
}%%

void ResponseParser::parse_cookie (const string& buffer) {
    const char* ps  = buffer.data();
    const char* p   = ps;
    const char* pe  = ps + buffer.size();
    const char* eof = pe;
    int         cs  = set_cookie_parser_start;
    auto&     cont  = response->cookies.fields;
    size_t mark;
    Response::Cookie* v;
    %% write exec;
}

}}}