// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/quic/core/quic_tag.h"

#include <algorithm>
#include <string>

#include "absl/base/macros.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_split.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/common/quiche_text_utils.h"

namespace quic {

bool FindMutualQuicTag(const QuicTagVector& our_tags,
                       const QuicTagVector& their_tags, QuicTag* out_result,
                       size_t* out_index) {
  const size_t num_our_tags = our_tags.size();
  const size_t num_their_tags = their_tags.size();
  for (size_t i = 0; i < num_our_tags; i++) {
    for (size_t j = 0; j < num_their_tags; j++) {
      if (our_tags[i] == their_tags[j]) {
        *out_result = our_tags[i];
        if (out_index != nullptr) {
          *out_index = j;
        }
        return true;
      }
    }
  }

  return false;
}

std::string QuicTagToString(QuicTag tag) {
  if (tag == 0) {
    return "0";
  }
  char chars[sizeof tag];
  bool ascii = true;
  const QuicTag orig_tag = tag;

  for (size_t i = 0; i < ABSL_ARRAYSIZE(chars); i++) {
    chars[i] = static_cast<char>(tag);
    if ((chars[i] == 0 || chars[i] == '\xff') &&
        i == ABSL_ARRAYSIZE(chars) - 1) {
      chars[i] = ' ';
    }
    if (!absl::ascii_isprint(static_cast<unsigned char>(chars[i]))) {
      ascii = false;
      break;
    }
    tag >>= 8;
  }

  if (ascii) {
    return std::string(chars, sizeof(chars));
  }

  return absl::BytesToHexString(absl::string_view(
      reinterpret_cast<const char*>(&orig_tag), sizeof(orig_tag)));
}

uint32_t MakeQuicTag(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  return static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 |
         static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24;
}

bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) {
  return std::find(tag_vector.begin(), tag_vector.end(), tag) !=
         tag_vector.end();
}

QuicTag ParseQuicTag(absl::string_view tag_string) {
  quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&tag_string);
  std::string tag_bytes;
  if (tag_string.length() == 8) {
    tag_bytes = absl::HexStringToBytes(tag_string);
    tag_string = tag_bytes;
  }
  QuicTag tag = 0;
  // Iterate over every character from right to left.
  for (auto it = tag_string.rbegin(); it != tag_string.rend(); ++it) {
    // The cast here is required on platforms where char is signed.
    unsigned char token_char = static_cast<unsigned char>(*it);
    tag <<= 8;
    tag |= token_char;
  }
  return tag;
}

QuicTagVector ParseQuicTagVector(absl::string_view tags_string) {
  QuicTagVector tag_vector;
  quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&tags_string);
  if (!tags_string.empty()) {
    std::vector<absl::string_view> tag_strings =
        absl::StrSplit(tags_string, ',');
    for (absl::string_view tag_string : tag_strings) {
      tag_vector.push_back(ParseQuicTag(tag_string));
    }
  }
  return tag_vector;
}

}  // namespace quic
