/*******************************************************************************
 * Copyright (c) 2014, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace libhttppp::qpack {

struct HeaderField {
    std::string name;
    std::string value;
    bool incremental_index = false;
};

class Encoder {
public:
    static std::vector<uint8_t> encodeResponseHeaders(uint16_t status_code,
                                                      const std::string &content_type,
                                                      size_t content_length,
                                                      const std::vector<HeaderField> &extra = {});
    static std::vector<uint8_t> encodeRequestHeaders(const std::string &method,
                                                     const std::string &scheme,
                                                     const std::string &authority,
                                                     const std::string &path,
                                                     const std::vector<HeaderField> &extra = {},
                                                     bool include_content_length = false,
                                                     size_t content_length = 0);

private:
    static void encodeInt(std::vector<uint8_t> &out, uint8_t prefix_byte,
                          uint8_t prefix_bits, uint64_t value);
    static void encodeString(std::vector<uint8_t> &out, const std::string &s);
    static void encodeIndexedStatic(std::vector<uint8_t> &out, uint64_t index);
    static void encodeLiteralStaticNameRef(std::vector<uint8_t> &out,
                                           uint64_t name_index,
                                           const std::string &value);
    static void encodeLiteralName(std::vector<uint8_t> &out,
                                  const std::string &name,
                                  const std::string &value);
    // First static-table index whose name matches, or -1. Lets "extra"
    // headers whose name is one of the ~99 QPACK static-table entries
    // (accept, user-agent, cookie, authorization, referer, ...) encode as a
    // 1-byte name-index + literal value instead of a fully literal name+value.
    static int findStaticNameIndex(const std::string &name);
    // Emits a name-referenced literal when the name is in the static table,
    // otherwise falls back to a fully literal name+value.
    static void encodeExtraHeader(std::vector<uint8_t> &out, const std::string &name,
                                  const std::string &value);
};

class Decoder {
public:
    static std::vector<HeaderField> decode(const uint8_t *data, size_t len);
private:
    struct StaticEntry { const char *name; const char *value; };
    static const StaticEntry STATIC_TABLE[];
    static const size_t STATIC_TABLE_SIZE;
    friend class Encoder; // Encoder::findStaticNameIndex() scans STATIC_TABLE
};

} // namespace libhttppp::qpack
