// 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 "ppapi/proxy/graphics_2d_resource.h"

#include "ppapi/c/pp_bool.h"
#include "ppapi/c/pp_point.h"
#include "ppapi/c/pp_rect.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_size.h"
#include "ppapi/c/ppb_graphics_2d.h"
#include "ppapi/proxy/dispatch_reply_message.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/resource_tracker.h"
#include "ppapi/shared_impl/tracked_callback.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_image_data_api.h"

namespace ppapi {
namespace proxy {

Graphics2DResource::Graphics2DResource(Connection connection,
                                       PP_Instance instance,
                                       const PP_Size& size,
                                       PP_Bool is_always_opaque)
    : PluginResource(connection, instance),
      size_(size),
      is_always_opaque_(is_always_opaque),
      scale_(1.0f) {
  // These checks are copied from PPB_ImageData_Impl::Init to make tests passed.
  // Let's remove/refactor this when start to refactor ImageData.
  bool bad_args = size.width <= 0 || size.height <= 0 ||
      static_cast<int64>(size.width) * static_cast<int64>(size.height) >=
          std::numeric_limits<int32>::max() / 4;
  if (!bad_args && !sent_create_to_renderer()) {
    SendCreate(RENDERER,
        PpapiHostMsg_Graphics2D_Create(size, is_always_opaque));
  }
}

Graphics2DResource::~Graphics2DResource() {
}

PP_Bool Graphics2DResource::Describe(PP_Size* size, PP_Bool* is_always_opaque) {
  *size = size_;
  *is_always_opaque = is_always_opaque_;
  return PP_TRUE;
}

thunk::PPB_Graphics2D_API* Graphics2DResource::AsPPB_Graphics2D_API() {
  return this;
}

void Graphics2DResource::PaintImageData(PP_Resource image_data,
                                        const PP_Point* top_left,
                                        const PP_Rect* src_rect) {
  Resource* image_object =
      PpapiGlobals::Get()->GetResourceTracker()->GetResource(image_data);
  if (!image_object || pp_instance() != image_object->pp_instance()) {
    Log(PP_LOGLEVEL_ERROR,
        "Graphics2DResource.PaintImageData: Bad image resource.");
    return;
  }

  PP_Rect dummy;
  memset(&dummy, 0, sizeof(PP_Rect));
  Post(RENDERER, PpapiHostMsg_Graphics2D_PaintImageData(
      image_object->host_resource(), *top_left,
      !!src_rect, src_rect ? *src_rect : dummy));
}

void Graphics2DResource::Scroll(const PP_Rect* clip_rect,
                                const PP_Point* amount) {
  PP_Rect dummy;
  memset(&dummy, 0, sizeof(PP_Rect));
  Post(RENDERER, PpapiHostMsg_Graphics2D_Scroll(
      !!clip_rect, clip_rect ? *clip_rect : dummy, *amount));
}

void Graphics2DResource::ReplaceContents(PP_Resource image_data) {
  thunk::EnterResourceNoLock<thunk::PPB_ImageData_API> enter_image(
      image_data, true);
  if (enter_image.failed())
    return;

  // Check that the PP_Instance matches.
  Resource* image_object =
      PpapiGlobals::Get()->GetResourceTracker()->GetResource(image_data);
  if (!image_object || pp_instance() != image_object->pp_instance()) {
    Log(PP_LOGLEVEL_ERROR,
        "Graphics2DResource.PaintImageData: Bad image resource.");
    return;
  }
  enter_image.object()->SetIsCandidateForReuse();

  Post(RENDERER, PpapiHostMsg_Graphics2D_ReplaceContents(
      image_object->host_resource()));
}

PP_Bool Graphics2DResource::SetScale(float scale) {
  if (scale <= 0.0f)
    return PP_FALSE;
  Post(RENDERER, PpapiHostMsg_Graphics2D_Dev_SetScale(scale));
  scale_ = scale;
  return PP_TRUE;
}

float Graphics2DResource::GetScale() {
  return scale_;
}

void Graphics2DResource::SetOffset(const PP_Point* offset) {
  Post(RENDERER, PpapiHostMsg_Graphics2D_SetOffset(*offset));
}

void Graphics2DResource::SetResizeMode(
    PP_Graphics2D_Dev_ResizeMode resize_mode) {
  Post(RENDERER, PpapiHostMsg_Graphics2D_SetResizeMode(resize_mode));
}

int32_t Graphics2DResource::Flush(scoped_refptr<TrackedCallback> callback) {
  // If host is not even created, return failure immediately.  This can happen
  // when failed to initialize (in constructor).
  if (!sent_create_to_renderer())
    return PP_ERROR_FAILED;

  if (TrackedCallback::IsPending(current_flush_callback_))
    return PP_ERROR_INPROGRESS;  // Can't have >1 flush pending.
  current_flush_callback_ = callback;

  // Send the current view data with the Flush() message. This allows the
  // renderer to know what the plugin's view of the renderer is at the time
  // Flush was called.
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(
      pp_instance());
  ppapi::ViewData view_data;
  if (dispatcher) {
    InstanceData* data = dispatcher->GetInstanceData(pp_instance());
    if (data)
      view_data = data->view;
  }
  Call<PpapiPluginMsg_Graphics2D_FlushAck>(
      RENDERER,
      PpapiHostMsg_Graphics2D_Flush(view_data),
      base::Bind(&Graphics2DResource::OnPluginMsgFlushACK, this));
  return PP_OK_COMPLETIONPENDING;
}

bool Graphics2DResource::ReadImageData(PP_Resource image,
                                       const PP_Point* top_left) {
  if (!top_left)
    return false;
  int32_t result = SyncCall<PpapiPluginMsg_Graphics2D_ReadImageDataAck>(
      RENDERER,
      PpapiHostMsg_Graphics2D_ReadImageData(image, *top_left));
  return result == PP_OK;
}

void Graphics2DResource::OnPluginMsgFlushACK(
    const ResourceMessageReplyParams& params) {
  current_flush_callback_->Run(params.result());
}

}  // namespace proxy
}  // namespace ppapi