// 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 "content/browser/indexed_db/cursor_impl.h"

#include "base/memory/ptr_util.h"
#include "base/sequenced_task_runner.h"
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"

namespace content {

// Expected to be constructed on IO thread, and used/destroyed on IDB sequence.
class CursorImpl::IDBSequenceHelper {
 public:
  explicit IDBSequenceHelper(std::unique_ptr<IndexedDBCursor> cursor);
  ~IDBSequenceHelper();

  void Advance(uint32_t count, scoped_refptr<IndexedDBCallbacks> callbacks);
  void Continue(const IndexedDBKey& key,
                const IndexedDBKey& primary_key,
                scoped_refptr<IndexedDBCallbacks> callbacks);
  void Prefetch(int32_t count, scoped_refptr<IndexedDBCallbacks> callbacks);
  void PrefetchReset(int32_t used_prefetches, int32_t unused_prefetches);

 private:
  std::unique_ptr<IndexedDBCursor> cursor_;

  DISALLOW_COPY_AND_ASSIGN(IDBSequenceHelper);
};

CursorImpl::CursorImpl(std::unique_ptr<IndexedDBCursor> cursor,
                       const url::Origin& origin,
                       IndexedDBDispatcherHost* dispatcher_host,
                       scoped_refptr<base::SequencedTaskRunner> idb_runner)
    : helper_(new IDBSequenceHelper(std::move(cursor))),
      dispatcher_host_(dispatcher_host),
      origin_(origin),
      idb_runner_(std::move(idb_runner)) {}

CursorImpl::~CursorImpl() {
  idb_runner_->DeleteSoon(FROM_HERE, helper_);
}

void CursorImpl::Advance(
    uint32_t count,
    ::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info) {
  scoped_refptr<IndexedDBCallbacks> callbacks(
      new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_,
                             std::move(callbacks_info), idb_runner_));
  idb_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&IDBSequenceHelper::Advance, base::Unretained(helper_),
                     count, base::Passed(&callbacks)));
}

void CursorImpl::Continue(
    const IndexedDBKey& key,
    const IndexedDBKey& primary_key,
    ::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info) {
  scoped_refptr<IndexedDBCallbacks> callbacks(
      new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_,
                             std::move(callbacks_info), idb_runner_));
  idb_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&IDBSequenceHelper::Continue, base::Unretained(helper_),
                     key, primary_key, base::Passed(&callbacks)));
}

void CursorImpl::Prefetch(
    int32_t count,
    ::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info) {
  scoped_refptr<IndexedDBCallbacks> callbacks(
      new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_,
                             std::move(callbacks_info), idb_runner_));
  idb_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&IDBSequenceHelper::Prefetch, base::Unretained(helper_),
                     count, base::Passed(&callbacks)));
}

void CursorImpl::PrefetchReset(
    int32_t used_prefetches,
    int32_t unused_prefetches,
    const std::vector<std::string>& unused_blob_uuids) {
  for (const auto& uuid : unused_blob_uuids)
    dispatcher_host_->DropBlobData(uuid);

  idb_runner_->PostTask(
      FROM_HERE, base::BindOnce(&IDBSequenceHelper::PrefetchReset,
                                base::Unretained(helper_), used_prefetches,
                                unused_prefetches));
}

CursorImpl::IDBSequenceHelper::IDBSequenceHelper(
    std::unique_ptr<IndexedDBCursor> cursor)
    : cursor_(std::move(cursor)) {}

CursorImpl::IDBSequenceHelper::~IDBSequenceHelper() {}

void CursorImpl::IDBSequenceHelper::Advance(
    uint32_t count,
    scoped_refptr<IndexedDBCallbacks> callbacks) {
  cursor_->Advance(count, std::move(callbacks));
}

void CursorImpl::IDBSequenceHelper::Continue(
    const IndexedDBKey& key,
    const IndexedDBKey& primary_key,
    scoped_refptr<IndexedDBCallbacks> callbacks) {
  cursor_->Continue(
      key.IsValid() ? base::MakeUnique<IndexedDBKey>(key) : nullptr,
      primary_key.IsValid() ? base::MakeUnique<IndexedDBKey>(primary_key)
                            : nullptr,
      std::move(callbacks));
}

void CursorImpl::IDBSequenceHelper::Prefetch(
    int32_t count,
    scoped_refptr<IndexedDBCallbacks> callbacks) {
  cursor_->PrefetchContinue(count, std::move(callbacks));
}

void CursorImpl::IDBSequenceHelper::PrefetchReset(int32_t used_prefetches,
                                                  int32_t unused_prefetches) {
  leveldb::Status s =
      cursor_->PrefetchReset(used_prefetches, unused_prefetches);
  // TODO(cmumford): Handle this error (crbug.com/363397)
  if (!s.ok())
    DLOG(ERROR) << "Unable to reset prefetch";
}

}  // namespace content
