// Copyright 2022 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_stream_priority.h"

#include "quiche/common/platform/api/quiche_bug_tracker.h"
#include "quiche/common/structured_headers.h"

namespace quic {

std::string SerializePriorityFieldValue(HttpStreamPriority priority) {
  quiche::structured_headers::Dictionary dictionary;

  if (priority.urgency != HttpStreamPriority::kDefaultUrgency &&
      priority.urgency >= HttpStreamPriority::kMinimumUrgency &&
      priority.urgency <= HttpStreamPriority::kMaximumUrgency) {
    dictionary[HttpStreamPriority::kUrgencyKey] =
        quiche::structured_headers::ParameterizedMember(
            quiche::structured_headers::Item(
                static_cast<int64_t>(priority.urgency)),
            {});
  }

  if (priority.incremental != HttpStreamPriority::kDefaultIncremental) {
    dictionary[HttpStreamPriority::kIncrementalKey] =
        quiche::structured_headers::ParameterizedMember(
            quiche::structured_headers::Item(priority.incremental), {});
  }

  std::optional<std::string> priority_field_value =
      quiche::structured_headers::SerializeDictionary(dictionary);
  if (!priority_field_value.has_value()) {
    QUICHE_BUG(priority_field_value_serialization_failed);
    return "";
  }

  return *priority_field_value;
}

std::optional<HttpStreamPriority> ParsePriorityFieldValue(
    absl::string_view priority_field_value) {
  std::optional<quiche::structured_headers::Dictionary> parsed_dictionary =
      quiche::structured_headers::ParseDictionary(priority_field_value);
  if (!parsed_dictionary.has_value()) {
    return std::nullopt;
  }

  uint8_t urgency = HttpStreamPriority::kDefaultUrgency;
  bool incremental = HttpStreamPriority::kDefaultIncremental;

  for (const auto& [name, value] : *parsed_dictionary) {
    if (value.member_is_inner_list) {
      continue;
    }

    const std::vector<quiche::structured_headers::ParameterizedItem>& member =
        value.member;
    if (member.size() != 1) {
      // If `member_is_inner_list` is false above,
      // then `member` should have exactly one element.
      QUICHE_BUG(priority_field_value_parsing_internal_error);
      continue;
    }

    const quiche::structured_headers::Item item = member[0].item;
    if (name == HttpStreamPriority::kUrgencyKey && item.is_integer()) {
      int parsed_urgency = item.GetInteger();
      // Ignore out-of-range values.
      if (parsed_urgency >= HttpStreamPriority::kMinimumUrgency &&
          parsed_urgency <= HttpStreamPriority::kMaximumUrgency) {
        urgency = parsed_urgency;
      }
    } else if (name == HttpStreamPriority::kIncrementalKey &&
               item.is_boolean()) {
      incremental = item.GetBoolean();
    }
  }

  return HttpStreamPriority{urgency, incremental};
}

}  // namespace quic
