// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/common/masque/connect_udp_datagram_payload.h"

#include <cstdint>
#include <memory>
#include <string>
#include <utility>

#include "absl/strings/string_view.h"
#include "quiche/common/platform/api/quiche_bug_tracker.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_data_reader.h"
#include "quiche/common/quiche_data_writer.h"

namespace quiche {

// static
std::unique_ptr<ConnectUdpDatagramPayload> ConnectUdpDatagramPayload::Parse(
    absl::string_view datagram_payload) {
  QuicheDataReader data_reader(datagram_payload);

  uint64_t context_id;
  if (!data_reader.ReadVarInt62(&context_id)) {
    QUICHE_DVLOG(1) << "Could not parse malformed UDP proxy payload";
    return nullptr;
  }

  if (ContextId{context_id} == ConnectUdpDatagramUdpPacketPayload::kContextId) {
    return std::make_unique<ConnectUdpDatagramUdpPacketPayload>(
        data_reader.ReadRemainingPayload());
  } else {
    return std::make_unique<ConnectUdpDatagramUnknownPayload>(
        ContextId{context_id}, data_reader.ReadRemainingPayload());
  }
}

std::string ConnectUdpDatagramPayload::Serialize() const {
  std::string buffer(SerializedLength(), '\0');
  QuicheDataWriter writer(buffer.size(), buffer.data());

  bool result = SerializeTo(writer);
  QUICHE_DCHECK(result);
  QUICHE_DCHECK_EQ(writer.remaining(), 0u);

  return buffer;
}

ConnectUdpDatagramUdpPacketPayload::ConnectUdpDatagramUdpPacketPayload(
    absl::string_view udp_packet)
    : udp_packet_(udp_packet) {}

ConnectUdpDatagramPayload::ContextId
ConnectUdpDatagramUdpPacketPayload::GetContextId() const {
  return kContextId;
}

ConnectUdpDatagramPayload::Type ConnectUdpDatagramUdpPacketPayload::GetType()
    const {
  return Type::kUdpPacket;
}

absl::string_view ConnectUdpDatagramUdpPacketPayload::GetUdpProxyingPayload()
    const {
  return udp_packet_;
}

size_t ConnectUdpDatagramUdpPacketPayload::SerializedLength() const {
  return udp_packet_.size() +
         QuicheDataWriter::GetVarInt62Len(uint64_t{kContextId});
}

bool ConnectUdpDatagramUdpPacketPayload::SerializeTo(
    QuicheDataWriter& writer) const {
  if (!writer.WriteVarInt62(uint64_t{kContextId})) {
    return false;
  }

  if (!writer.WriteStringPiece(udp_packet_)) {
    return false;
  }

  return true;
}

ConnectUdpDatagramUnknownPayload::ConnectUdpDatagramUnknownPayload(
    ContextId context_id, absl::string_view udp_proxying_payload)
    : context_id_(context_id), udp_proxying_payload_(udp_proxying_payload) {
  if (context_id == ConnectUdpDatagramUdpPacketPayload::kContextId) {
    QUICHE_BUG(udp_proxy_unknown_payload_udp_context)
        << "ConnectUdpDatagramUnknownPayload created with UDP packet context "
           "type (0). Should instead create a "
           "ConnectUdpDatagramUdpPacketPayload.";
  }
}

ConnectUdpDatagramPayload::ContextId
ConnectUdpDatagramUnknownPayload::GetContextId() const {
  return context_id_;
}

ConnectUdpDatagramPayload::Type ConnectUdpDatagramUnknownPayload::GetType()
    const {
  return Type::kUnknown;
}
absl::string_view ConnectUdpDatagramUnknownPayload::GetUdpProxyingPayload()
    const {
  return udp_proxying_payload_;
}

size_t ConnectUdpDatagramUnknownPayload::SerializedLength() const {
  return udp_proxying_payload_.size() +
         QuicheDataWriter::GetVarInt62Len(uint64_t{context_id_});
}

bool ConnectUdpDatagramUnknownPayload::SerializeTo(
    QuicheDataWriter& writer) const {
  if (!writer.WriteVarInt62(uint64_t{context_id_})) {
    return false;
  }

  if (!writer.WriteStringPiece(udp_proxying_payload_)) {
    return false;
  }

  return true;
}

}  // namespace quiche
