// Copyright (c) 2012 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 "storage/browser/fileapi/file_system_file_stream_reader.h"

#include "base/files/file_util_proxy.h"
#include "base/single_thread_task_runner.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "storage/browser/blob/file_stream_reader.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_operation_runner.h"

using storage::FileStreamReader;

// TODO(kinuko): Remove this temporary namespace hack after we move both
// blob and fileapi into content namespace.
namespace storage {

FileStreamReader* FileStreamReader::CreateForFileSystemFile(
    storage::FileSystemContext* file_system_context,
    const storage::FileSystemURL& url,
    int64 initial_offset,
    const base::Time& expected_modification_time) {
  return new storage::FileSystemFileStreamReader(
      file_system_context, url, initial_offset, expected_modification_time);
}

}  // namespace storage

namespace storage {

namespace {

void ReadAdapter(base::WeakPtr<FileSystemFileStreamReader> reader,
                 net::IOBuffer* buf, int buf_len,
                 const net::CompletionCallback& callback) {
  if (!reader.get())
    return;
  int rv = reader->Read(buf, buf_len, callback);
  if (rv != net::ERR_IO_PENDING)
    callback.Run(rv);
}

void GetLengthAdapter(base::WeakPtr<FileSystemFileStreamReader> reader,
                      const net::Int64CompletionCallback& callback) {
  if (!reader.get())
    return;
  int rv = reader->GetLength(callback);
  if (rv != net::ERR_IO_PENDING)
    callback.Run(rv);
}

void Int64CallbackAdapter(const net::Int64CompletionCallback& callback,
                          int value) {
  callback.Run(value);
}

}  // namespace

FileSystemFileStreamReader::~FileSystemFileStreamReader() {
}

int FileSystemFileStreamReader::Read(
    net::IOBuffer* buf, int buf_len,
    const net::CompletionCallback& callback) {
  if (local_file_reader_)
    return local_file_reader_->Read(buf, buf_len, callback);
  return CreateSnapshot(
      base::Bind(&ReadAdapter, weak_factory_.GetWeakPtr(),
                 make_scoped_refptr(buf), buf_len, callback),
      callback);
}

int64 FileSystemFileStreamReader::GetLength(
    const net::Int64CompletionCallback& callback) {
  if (local_file_reader_)
    return local_file_reader_->GetLength(callback);
  return CreateSnapshot(
      base::Bind(&GetLengthAdapter, weak_factory_.GetWeakPtr(), callback),
      base::Bind(&Int64CallbackAdapter, callback));
}

FileSystemFileStreamReader::FileSystemFileStreamReader(
    FileSystemContext* file_system_context,
    const FileSystemURL& url,
    int64 initial_offset,
    const base::Time& expected_modification_time)
    : file_system_context_(file_system_context),
      url_(url),
      initial_offset_(initial_offset),
      expected_modification_time_(expected_modification_time),
      has_pending_create_snapshot_(false),
      weak_factory_(this) {
}

int FileSystemFileStreamReader::CreateSnapshot(
    const base::Closure& callback,
    const net::CompletionCallback& error_callback) {
  DCHECK(!has_pending_create_snapshot_);
  has_pending_create_snapshot_ = true;
  file_system_context_->operation_runner()->CreateSnapshotFile(
      url_,
      base::Bind(&FileSystemFileStreamReader::DidCreateSnapshot,
                 weak_factory_.GetWeakPtr(),
                 callback,
                 error_callback));
  return net::ERR_IO_PENDING;
}

void FileSystemFileStreamReader::DidCreateSnapshot(
    const base::Closure& callback,
    const net::CompletionCallback& error_callback,
    base::File::Error file_error,
    const base::File::Info& file_info,
    const base::FilePath& platform_path,
    const scoped_refptr<storage::ShareableFileReference>& file_ref) {
  DCHECK(has_pending_create_snapshot_);
  DCHECK(!local_file_reader_.get());
  has_pending_create_snapshot_ = false;

  if (file_error != base::File::FILE_OK) {
    error_callback.Run(net::FileErrorToNetError(file_error));
    return;
  }

  // Keep the reference (if it's non-null) so that the file won't go away.
  snapshot_ref_ = file_ref;

  local_file_reader_.reset(
      FileStreamReader::CreateForLocalFile(
          file_system_context_->default_file_task_runner(),
          platform_path, initial_offset_, expected_modification_time_));

  callback.Run();
}

}  // namespace storage
