// Copyright 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 "net/third_party/http2/hpack/decoder/hpack_block_collector.h"

#include <algorithm>
#include <memory>

#include "base/logging.h"
#include "base/rand_util.h"
#include "net/third_party/http2/tools/failure.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::AssertionResult;
using ::testing::AssertionSuccess;

namespace http2 {
namespace test {

HpackBlockCollector::HpackBlockCollector() = default;
HpackBlockCollector::HpackBlockCollector(const HpackBlockCollector& other)
    : pending_entry_(other.pending_entry_), entries_(other.entries_) {}
HpackBlockCollector::~HpackBlockCollector() = default;

void HpackBlockCollector::OnIndexedHeader(size_t index) {
  pending_entry_.OnIndexedHeader(index);
  PushPendingEntry();
}
void HpackBlockCollector::OnDynamicTableSizeUpdate(size_t size) {
  pending_entry_.OnDynamicTableSizeUpdate(size);
  PushPendingEntry();
}
void HpackBlockCollector::OnStartLiteralHeader(HpackEntryType header_type,
                                               size_t maybe_name_index) {
  pending_entry_.OnStartLiteralHeader(header_type, maybe_name_index);
}
void HpackBlockCollector::OnNameStart(bool huffman_encoded, size_t len) {
  pending_entry_.OnNameStart(huffman_encoded, len);
}
void HpackBlockCollector::OnNameData(const char* data, size_t len) {
  pending_entry_.OnNameData(data, len);
}
void HpackBlockCollector::OnNameEnd() {
  pending_entry_.OnNameEnd();
}
void HpackBlockCollector::OnValueStart(bool huffman_encoded, size_t len) {
  pending_entry_.OnValueStart(huffman_encoded, len);
}
void HpackBlockCollector::OnValueData(const char* data, size_t len) {
  pending_entry_.OnValueData(data, len);
}
void HpackBlockCollector::OnValueEnd() {
  pending_entry_.OnValueEnd();
  PushPendingEntry();
}

void HpackBlockCollector::PushPendingEntry() {
  EXPECT_TRUE(pending_entry_.IsComplete());
  DVLOG(2) << "PushPendingEntry: " << pending_entry_;
  entries_.push_back(pending_entry_);
  EXPECT_TRUE(entries_.back().IsComplete());
  pending_entry_.Clear();
}
void HpackBlockCollector::Clear() {
  pending_entry_.Clear();
  entries_.clear();
}

void HpackBlockCollector::ExpectIndexedHeader(size_t index) {
  entries_.push_back(
      HpackEntryCollector(HpackEntryType::kIndexedHeader, index));
}
void HpackBlockCollector::ExpectDynamicTableSizeUpdate(size_t size) {
  entries_.push_back(
      HpackEntryCollector(HpackEntryType::kDynamicTableSizeUpdate, size));
}
void HpackBlockCollector::ExpectNameIndexAndLiteralValue(
    HpackEntryType type,
    size_t index,
    bool value_huffman,
    const Http2String& value) {
  entries_.push_back(HpackEntryCollector(type, index, value_huffman, value));
}
void HpackBlockCollector::ExpectLiteralNameAndValue(HpackEntryType type,
                                                    bool name_huffman,
                                                    const Http2String& name,
                                                    bool value_huffman,
                                                    const Http2String& value) {
  entries_.push_back(
      HpackEntryCollector(type, name_huffman, name, value_huffman, value));
}

void HpackBlockCollector::ShuffleEntries(RandomBase* rng) {
  base::RandomShuffle(entries_.begin(), entries_.end());
}

void HpackBlockCollector::AppendToHpackBlockBuilder(
    HpackBlockBuilder* hbb) const {
  CHECK(IsNotPending());
  for (const auto& entry : entries_) {
    entry.AppendToHpackBlockBuilder(hbb);
  }
}

AssertionResult HpackBlockCollector::ValidateSoleIndexedHeader(
    size_t ndx) const {
  VERIFY_TRUE(pending_entry_.IsClear());
  VERIFY_EQ(1u, entries_.size());
  VERIFY_TRUE(entries_.front().ValidateIndexedHeader(ndx));
  return AssertionSuccess();
}
AssertionResult HpackBlockCollector::ValidateSoleLiteralValueHeader(
    HpackEntryType expected_type,
    size_t expected_index,
    bool expected_value_huffman,
    Http2StringPiece expected_value) const {
  VERIFY_TRUE(pending_entry_.IsClear());
  VERIFY_EQ(1u, entries_.size());
  VERIFY_TRUE(entries_.front().ValidateLiteralValueHeader(
      expected_type, expected_index, expected_value_huffman, expected_value));
  return AssertionSuccess();
}
AssertionResult HpackBlockCollector::ValidateSoleLiteralNameValueHeader(
    HpackEntryType expected_type,
    bool expected_name_huffman,
    Http2StringPiece expected_name,
    bool expected_value_huffman,
    Http2StringPiece expected_value) const {
  VERIFY_TRUE(pending_entry_.IsClear());
  VERIFY_EQ(1u, entries_.size());
  VERIFY_TRUE(entries_.front().ValidateLiteralNameValueHeader(
      expected_type, expected_name_huffman, expected_name,
      expected_value_huffman, expected_value));
  return AssertionSuccess();
}
AssertionResult HpackBlockCollector::ValidateSoleDynamicTableSizeUpdate(
    size_t size) const {
  VERIFY_TRUE(pending_entry_.IsClear());
  VERIFY_EQ(1u, entries_.size());
  VERIFY_TRUE(entries_.front().ValidateDynamicTableSizeUpdate(size));
  return AssertionSuccess();
}

AssertionResult HpackBlockCollector::VerifyEq(
    const HpackBlockCollector& that) const {
  VERIFY_EQ(pending_entry_, that.pending_entry_);
  VERIFY_EQ(entries_, that.entries_);
  return AssertionSuccess();
}

}  // namespace test
}  // namespace http2
