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

#include "chrome/browser/media/webrtc/test_stats_dictionary.h"

#include <memory>
#include <optional>

#include "base/check.h"
#include "base/json/json_writer.h"

namespace content {

TestStatsReportDictionary::TestStatsReportDictionary(base::DictValue report)
    : report_(std::move(report)) {}

TestStatsReportDictionary::~TestStatsReportDictionary() = default;

void TestStatsReportDictionary::ForEach(
    std::function<void(const TestStatsDictionary&)> iteration) {
  for (auto it : report_) {
    const base::DictValue* it_value = it.second.GetIfDict();
    CHECK(it_value);
    iteration(TestStatsDictionary(this, it_value));
  }
}

std::vector<TestStatsDictionary> TestStatsReportDictionary::Filter(
    std::function<bool(const TestStatsDictionary&)> filter) {
  std::vector<TestStatsDictionary> result;
  ForEach([&result, &filter](const TestStatsDictionary& stats) {
    if (filter(stats))
      result.push_back(stats);
  });
  return result;
}

std::unique_ptr<TestStatsDictionary> TestStatsReportDictionary::Get(
    const std::string& id) {
  const base::DictValue* dictionary = report_.FindDict(id);
  if (!dictionary)
    return nullptr;
  return std::make_unique<TestStatsDictionary>(this, dictionary);
}

std::vector<TestStatsDictionary> TestStatsReportDictionary::GetAll() {
  return Filter([](const TestStatsDictionary&) { return true; });
}

std::vector<TestStatsDictionary> TestStatsReportDictionary::GetByType(
    const std::string& type) {
  return Filter([&type](const TestStatsDictionary& stats) {
    return stats.GetString("type") == type;
  });
}

TestStatsDictionary::TestStatsDictionary(TestStatsReportDictionary* report,
                                         const base::DictValue* stats)
    : report_(report), stats_(stats) {
  CHECK(report_);
  CHECK(stats_);
}

TestStatsDictionary::TestStatsDictionary(
    const TestStatsDictionary& other) = default;

TestStatsDictionary::~TestStatsDictionary() = default;

bool TestStatsDictionary::IsBoolean(const std::string& key) const {
  bool value;
  return GetBoolean(key, &value);
}

bool TestStatsDictionary::GetBoolean(const std::string& key) const {
  bool value;
  CHECK(GetBoolean(key, &value));
  return value;
}

bool TestStatsDictionary::IsNumber(const std::string& key) const {
  double value;
  return GetNumber(key, &value);
}

double TestStatsDictionary::GetNumber(const std::string& key) const {
  double value;
  CHECK(GetNumber(key, &value));
  return value;
}

bool TestStatsDictionary::IsString(const std::string& key) const {
  return stats_->FindString(key) != nullptr;
}

std::string TestStatsDictionary::GetString(const std::string& key) const {
  const std::string* value = stats_->FindString(key);
  CHECK(value);
  return *value;
}

bool TestStatsDictionary::IsSequenceBoolean(const std::string& key) const {
  std::vector<bool> value;
  return GetSequenceBoolean(key, &value);
}

std::vector<bool> TestStatsDictionary::GetSequenceBoolean(
    const std::string& key) const {
  std::vector<bool> value;
  CHECK(GetSequenceBoolean(key, &value));
  return value;
}

bool TestStatsDictionary::IsSequenceNumber(const std::string& key) const {
  std::vector<double> value;
  return GetSequenceNumber(key, &value);
}

std::vector<double> TestStatsDictionary::GetSequenceNumber(
    const std::string& key) const {
  std::vector<double> value;
  CHECK(GetSequenceNumber(key, &value));
  return value;
}

bool TestStatsDictionary::IsSequenceString(const std::string& key) const {
  std::vector<std::string> value;
  return GetSequenceString(key, &value);
}

std::vector<std::string> TestStatsDictionary::GetSequenceString(
    const std::string& key) const {
  std::vector<std::string> value;
  CHECK(GetSequenceString(key, &value));
  return value;
}

bool TestStatsDictionary::GetBoolean(
    const std::string& key, bool* out) const {
  if (std::optional<bool> value = stats_->FindBool(key)) {
    *out = *value;
    return true;
  }
  return false;
}

bool TestStatsDictionary::GetNumber(
    const std::string& key, double* out) const {
  if (std::optional<double> value = stats_->FindDouble(key)) {
    *out = *value;
    return true;
  }
  return false;
}

bool TestStatsDictionary::GetSequenceBoolean(
    const std::string& key,
    std::vector<bool>* out) const {
  const base::ListValue* list = stats_->FindList(key);
  if (!list)
    return false;
  std::vector<bool> sequence;
  for (const base::Value& arg : *list) {
    std::optional<bool> bool_value = arg.GetIfBool();
    if (!bool_value.has_value())
      return false;
    sequence.push_back(*bool_value);
  }
  *out = std::move(sequence);
  return true;
}

bool TestStatsDictionary::GetSequenceNumber(
    const std::string& key,
    std::vector<double>* out) const {
  const base::ListValue* number_sequence = stats_->FindList(key);
  if (!number_sequence)
    return false;

  out->clear();
  for (const base::Value& element : *number_sequence) {
    std::optional<double> double_value = element.GetIfDouble();
    if (!double_value)
      return false;

    out->push_back(*double_value);
  }

  return true;
}

bool TestStatsDictionary::GetSequenceString(
    const std::string& key,
    std::vector<std::string>* out) const {
  const base::ListValue* list = stats_->FindList(key);
  if (!list)
    return false;
  std::vector<std::string> sequence;
  for (const base::Value& i : *list) {
    const std::string* element = i.GetIfString();
    if (!element)
      return false;
    sequence.push_back(*element);
  }
  *out = std::move(sequence);
  return true;
}

std::string TestStatsDictionary::ToString() const {
  std::string str;
  CHECK(base::JSONWriter::WriteWithOptions(
      *stats_, base::JSONWriter::OPTIONS_PRETTY_PRINT, &str));
  return str;
}

}  // namespace content
