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

#include "cc/test/transfer_cache_test_helper.h"

#include <memory>
#include <utility>

#include "base/check.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "third_party/skia/include/gpu/ganesh/mock/GrMockTypes.h"

namespace cc {

TransferCacheTestHelper::TransferCacheTestHelper() {
  GrMockOptions options;
  options.fMipmapSupport = true;
  options.fConfigOptions[static_cast<int>(GrColorType::kGray_8)].fTexturable =
      true;
  options.fConfigOptions[static_cast<int>(GrColorType::kRGBA_F16)].fTexturable =
      true;
  options.fConfigOptions[static_cast<int>(GrColorType::kAlpha_16)].fTexturable =
      true;
  options.fConfigOptions[static_cast<int>(GrColorType::kAlpha_F16)]
      .fTexturable = true;

  context_ = GrDirectContext::MakeMock(&options);
}
TransferCacheTestHelper::~TransferCacheTestHelper() = default;

bool TransferCacheTestHelper::LockEntryDirect(const EntryKey& key) {
  return LockEntryInternal(key);
}

void TransferCacheTestHelper::CreateEntryDirect(const EntryKey& key,
                                                base::span<uint8_t> data) {
  // Deserialize into a service transfer cache entry.
  std::unique_ptr<ServiceTransferCacheEntry> service_entry =
      ServiceTransferCacheEntry::Create(key.first);
  if (!service_entry) {
    return;
  }

  bool success = service_entry->Deserialize(
      context_.get(), /*graphite_recorder=*/nullptr, data);
  if (!success) {
    return;
  }

  last_added_entry_ = key;

  // Put things into the cache.
  entries_.emplace(key, std::move(service_entry));
  locked_entries_.insert(key);
  EnforceLimits();
}

void TransferCacheTestHelper::CreateLocalEntry(
    uint32_t id,
    std::unique_ptr<ServiceTransferCacheEntry> entry) {
  auto key = std::make_pair(entry->Type(), id);

  DeleteEntryDirect(key);

  entries_[key] = std::move(entry);
  local_entries_.insert(key);
  last_added_entry_ = key;
}

void TransferCacheTestHelper::UnlockEntriesDirect(
    const std::vector<EntryKey>& keys) {
  for (const auto& key : keys) {
    locked_entries_.erase(key);
  }
  EnforceLimits();
}

void TransferCacheTestHelper::DeleteEntryDirect(const EntryKey& key) {
  locked_entries_.erase(key);
  local_entries_.erase(key);
  entries_.erase(key);
}

void TransferCacheTestHelper::SetCachedItemsLimit(size_t limit) {
  cached_items_limit_ = limit;
  EnforceLimits();
}

ServiceTransferCacheEntry* TransferCacheTestHelper::GetEntryInternal(
    TransferCacheEntryType type,
    uint32_t id) {
  auto key = std::make_pair(type, id);
  if (locked_entries_.count(key) + local_entries_.count(key) == 0) {
    return nullptr;
  }
  if (!entries_.contains(key)) {
    return nullptr;
  }
  return entries_[key].get();
}

bool TransferCacheTestHelper::LockEntryInternal(const EntryKey& key) {
  if (!entries_.contains(key)) {
    return false;
  }

  locked_entries_.insert(key);
  EnforceLimits();
  return true;
}

uint32_t TransferCacheTestHelper::CreateEntryInternal(
    const ClientTransferCacheEntry& client_entry,
    base::span<uint8_t> memory) {
  auto key = std::make_pair(client_entry.Type(), client_entry.Id());
  DCHECK(!entries_.contains(key));

  // Serialize data.
  uint32_t size = client_entry.SerializedSize();
  auto data = base::HeapArray<uint8_t>::Uninit(size);
  auto span = base::span(data);
  bool success = client_entry.Serialize(span);
  DCHECK(success);
  CreateEntryDirect(key, span);
  return 0u;
}

void TransferCacheTestHelper::FlushEntriesInternal(std::set<EntryKey> keys) {
  for (auto& key : keys) {
    locked_entries_.erase(key);
  }
  EnforceLimits();
}

void TransferCacheTestHelper::EnforceLimits() {
  for (auto it = entries_.begin(); it != entries_.end();) {
    if (entries_.size() <= cached_items_limit_) {
      break;
    }

    auto found = locked_entries_.find(it->first);
    if (found == locked_entries_.end()) {
      it = entries_.erase(it);
    } else {
      ++it;
    }
  }
}

}  // namespace cc
