// Copyright 2014 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 "mojo/examples/html_viewer/weburlloader_impl.h"

#include "base/bind.h"
#include "base/logging.h"
#include "mojo/services/public/interfaces/network/network_service.mojom.h"
#include "third_party/WebKit/public/platform/WebURLError.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"

namespace mojo {
namespace examples {
namespace {

blink::WebURLResponse ToWebURLResponse(const URLResponsePtr& url_response) {
  blink::WebURLResponse result;
  result.initialize();
  result.setURL(GURL(url_response->url));
  // TODO(darin): Copy other fields.
  return result;
}

}  // namespace

WebURLLoaderImpl::WebURLLoaderImpl(NetworkService* network_service)
    : client_(NULL),
      weak_factory_(this) {
  network_service->CreateURLLoader(Get(&url_loader_));
  url_loader_.set_client(this);
}

WebURLLoaderImpl::~WebURLLoaderImpl() {
}

void WebURLLoaderImpl::loadSynchronously(
    const blink::WebURLRequest& request,
    blink::WebURLResponse& response,
    blink::WebURLError& error,
    blink::WebData& data) {
  NOTIMPLEMENTED();
}

void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request,
                                          blink::WebURLLoaderClient* client) {
  client_ = client;

  URLRequestPtr url_request(URLRequest::New());
  url_request->url = request.url().spec();
  url_request->auto_follow_redirects = false;
  // TODO(darin): Copy other fields.

  DataPipe pipe;
  url_loader_->Start(url_request.Pass(), pipe.producer_handle.Pass());
  response_body_stream_ = pipe.consumer_handle.Pass();
}

void WebURLLoaderImpl::cancel() {
  url_loader_.reset();
  response_body_stream_.reset();
  // TODO(darin): Need to asynchronously call didFail.
}

void WebURLLoaderImpl::setDefersLoading(bool defers_loading) {
  NOTIMPLEMENTED();
}

void WebURLLoaderImpl::OnReceivedRedirect(URLResponsePtr url_response,
                                          const String& new_url,
                                          const String& new_method) {
  blink::WebURLRequest new_request;
  new_request.initialize();
  new_request.setURL(GURL(new_url));

  client_->willSendRequest(this, new_request, ToWebURLResponse(url_response));
  // TODO(darin): Check if new_request was rejected.

  url_loader_->FollowRedirect();
}

void WebURLLoaderImpl::OnReceivedResponse(URLResponsePtr url_response) {
  client_->didReceiveResponse(this, ToWebURLResponse(url_response));

  // Start streaming data
  ReadMore();
}

void WebURLLoaderImpl::OnReceivedError(NetworkErrorPtr error) {
  // TODO(darin): Construct a meaningful WebURLError.
  client_->didFail(this, blink::WebURLError());
}

void WebURLLoaderImpl::OnReceivedEndOfResponseBody() {
  // This is the signal that the response body was not truncated.
}

void WebURLLoaderImpl::ReadMore() {
  const void* buf;
  uint32_t buf_size;
  MojoResult rv = BeginReadDataRaw(response_body_stream_.get(),
                                   &buf,
                                   &buf_size,
                                   MOJO_READ_DATA_FLAG_NONE);
  if (rv == MOJO_RESULT_OK) {
    client_->didReceiveData(this, static_cast<const char*>(buf), buf_size, -1);
    EndReadDataRaw(response_body_stream_.get(), buf_size);
    WaitToReadMore();
  } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
    WaitToReadMore();
  } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
    // We reached end-of-file.
    double finish_time = base::Time::Now().ToDoubleT();
    client_->didFinishLoading(
        this,
        finish_time,
        blink::WebURLLoaderClient::kUnknownEncodedDataLength);
  } else {
    // TODO(darin): Oops!
  }
}

void WebURLLoaderImpl::WaitToReadMore() {
  handle_watcher_.Start(
      response_body_stream_.get(),
      MOJO_HANDLE_SIGNAL_READABLE,
      MOJO_DEADLINE_INDEFINITE,
      base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady,
                 weak_factory_.GetWeakPtr()));
}

void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
  ReadMore();
}

}  // namespace examples
}  // namespace mojo