#include "quiche/balsa/header_properties.h"

#include <array>

#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "quiche/common/quiche_text_utils.h"

namespace quiche::header_properties {

namespace {

using MultivaluedHeadersSet =
    absl::flat_hash_set<absl::string_view, StringPieceCaseHash,
                        StringPieceCaseEqual>;

MultivaluedHeadersSet* buildMultivaluedHeaders() {
  return new MultivaluedHeadersSet({
      "accept",
      "accept-charset",
      "accept-encoding",
      "accept-language",
      "accept-ranges",
      // The follow four headers are all CORS standard headers
      "access-control-allow-headers",
      "access-control-allow-methods",
      "access-control-expose-headers",
      "access-control-request-headers",
      "allow",
      "cache-control",
      // IETF draft makes this have cache-control syntax
      "cdn-cache-control",
      "connection",
      "content-encoding",
      "content-language",
      "expect",
      "if-match",
      "if-none-match",
      // See RFC 5988 section 5
      "link",
      "pragma",
      "proxy-authenticate",
      "te",
      // Used in the opening handshake of the WebSocket protocol.
      "sec-websocket-extensions",
      // Not mentioned in RFC 2616, but it can have multiple values.
      "set-cookie",
      "trailer",
      "transfer-encoding",
      "upgrade",
      "vary",
      "via",
      "warning",
      "www-authenticate",
      // De facto standard not in the RFCs
      "x-forwarded-for",
      // Internal Google usage gives this cache-control syntax
      "x-go" /**/ "ogle-cache-control",
  });
}

std::array<bool, 256> buildInvalidHeaderKeyCharLookupTable() {
  std::array<bool, 256> invalidCharTable;
  invalidCharTable.fill(false);
  for (uint8_t c : kInvalidHeaderKeyCharList) {
    invalidCharTable[c] = true;
  }
  return invalidCharTable;
}

std::array<bool, 256> buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote() {
  std::array<bool, 256> invalidCharTable;
  invalidCharTable.fill(false);
  for (uint8_t c : kInvalidHeaderKeyCharListAllowDoubleQuote) {
    invalidCharTable[c] = true;
  }
  return invalidCharTable;
}

std::array<bool, 256> buildInvalidCharLookupTable() {
  std::array<bool, 256> invalidCharTable;
  invalidCharTable.fill(false);
  for (uint8_t c : kInvalidHeaderCharList) {
    invalidCharTable[c] = true;
  }
  return invalidCharTable;
}

}  // anonymous namespace

bool IsMultivaluedHeader(absl::string_view header) {
  static const MultivaluedHeadersSet* const multivalued_headers =
      buildMultivaluedHeaders();
  return multivalued_headers->contains(header);
}

bool IsInvalidHeaderKeyChar(uint8_t c) {
  static const std::array<bool, 256> invalidHeaderKeyCharTable =
      buildInvalidHeaderKeyCharLookupTable();

  return invalidHeaderKeyCharTable[c];
}

bool IsInvalidHeaderKeyCharAllowDoubleQuote(uint8_t c) {
  static const std::array<bool, 256> invalidHeaderKeyCharTable =
      buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote();

  return invalidHeaderKeyCharTable[c];
}

bool IsInvalidHeaderChar(uint8_t c) {
  static const std::array<bool, 256> invalidCharTable =
      buildInvalidCharLookupTable();

  return invalidCharTable[c];
}

bool HasInvalidHeaderChars(absl::string_view value) {
  for (const char c : value) {
    if (IsInvalidHeaderChar(c)) {
      return true;
    }
  }
  return false;
}

}  // namespace quiche::header_properties
