MODULE = Protocol::HTTP              PACKAGE = Protocol::HTTP::Message
PROTOTYPES: DISABLE

BOOT {
    Stash stash(__PACKAGE__);
    xs::exp::create_constants(stash, {
        {"STATE_HEADERS",       (int)State::headers},
        {"STATE_BODY",          (int)State::body},
        {"STATE_CHUNK",         (int)State::chunk},
        {"STATE_CHUNK_BODY",    (int)State::chunk_body},
        {"STATE_CHUNK_TRAILER", (int)State::chunk_trailer},
        {"STATE_DONE",          (int)State::done},
        {"STATE_ERROR",         (int)State::error}
    });
    xs::exp::autoexport(stash);
}

Hash Message::headers (Hash new_headers = Hash()) {
    if (new_headers) {
        set_headers(THIS, new_headers);
        XSRETURN_UNDEF;
    }

    RETVAL = Hash::create(THIS->headers.size());
    for (const auto& elem : THIS->headers.fields) {
        auto len = elem.name.length();
        auto key = (char*)alloca(sizeof(char)*len);
        std::transform(elem.name.data(), elem.name.data() + len, key, ::tolower);
        RETVAL.store(string_view(key, len), xs::out(elem.value));
    }
}

string Message::header (string_view name, SV* val = nullptr) {
    if (val) {
        if (!sv_defined(val)) THIS->headers.remove(name);
        else                  THIS->headers.set(string(name), xs::in<string>(val));
        XSRETURN_UNDEF;
    }

    auto it = THIS->headers.find(name);
    if (it == THIS->headers.fields.end()) XSRETURN_UNDEF;
    RETVAL = it->value;
}

void Message::multiheader (string_view name, ...) {
    if (items > 2) {
        auto key = string(name);
        for (auto i = 2; i < items; ++i) {
            auto val = ST(i);
            if (sv_defined(val)) THIS->headers.add(key, xs::in<string>(val));
        }
        XSRETURN_EMPTY;
    }
    for (auto& v : THIS->headers.get_multi(name)) {
        mXPUSHs(xs::out(v).detach());
    }
}

size_t Message::headers_size () {
    RETVAL = THIS->headers.size();
}

Simple Message::body (SV* newval = nullptr) {
    if (newval) {
        THIS->body = xs::in<string>(newval);
        XSRETURN_UNDEF;
    }
    RETVAL = strings_to_sv(THIS->body.parts);
}

int Message::http_version (SV* newval = nullptr) {
    if (newval) {
        THIS->http_version = SvIV(newval);
        XSRETURN_UNDEF;
    }
    RETVAL = THIS->http_version;
}

bool Message::chunked (SV* newval = nullptr) {
    if (newval) {
        THIS->chunked = SvTRUE(newval);
        XSRETURN_UNDEF;
    }
    RETVAL = THIS->chunked;
}

Simple Message::make_chunk(string s) {
    RETVAL = strings_to_sv(THIS->make_chunk(s));
}

Simple Message::final_chunk(string s = {}) {
    RETVAL = strings_to_sv(THIS->final_chunk(s));
}

void Message::compression () {
    if (GIMME_V == G_ARRAY) {
        mPUSHs(Simple((int)THIS->compression.type).detach());
        mXPUSHs(Simple((int)THIS->compression.level).detach());
        XSRETURN(2);
    }
    else {
        dXSTARG;
        PUSHi((int)THIS->compression.type);
    }
}

void Message::compress (int method, int level = (int)Compression::Level::min) {
    if (level < (int)Compression::Level::min || level > (int)Compression::Level::max) throw "bad compression level";
    if (!is_valid_compression(method)) throw "bad compression";
    
    THIS->compress((Compression::Type)method, (Compression::Level)level);
}

SV* CLONE_SKIP (...) {
    XSRETURN_YES;
    PERL_UNUSED_VAR(items);
}