// 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 "remoting/client/plugin/pepper_cursor_setter.h"

#include "base/logging.h"
#include "ppapi/cpp/image_data.h"
#include "ppapi/cpp/mouse_cursor.h"
#include "remoting/client/plugin/empty_cursor_filter.h"
#include "remoting/proto/control.pb.h"

namespace remoting {

PepperCursorSetter::PepperCursorSetter(const pp::InstanceHandle& instance)
    : instance_(instance), delegate_stub_(nullptr) {
}

PepperCursorSetter::~PepperCursorSetter() {}

void PepperCursorSetter::SetCursorShape(
    const protocol::CursorShapeInfo& cursor_shape) {
  if (SetInstanceCursor(cursor_shape)) {
    // PPAPI cursor was set successfully, so clear delegate cursor, if any.
    if (delegate_stub_) {
      delegate_stub_->SetCursorShape(EmptyCursorShape());
    }
  } else if (delegate_stub_) {
    // Clear PPAPI cursor and fall-back to rendering cursor via delegate.
    pp::MouseCursor::SetCursor(instance_, PP_MOUSECURSOR_TYPE_NONE);
    delegate_stub_->SetCursorShape(cursor_shape);
  } else {
    // TODO(wez): Fall-back to cropping & re-trying the cursor iff there is no
    // delegate and the cursor is >32x32?
    DLOG(FATAL) << "Failed to set PPAPI cursor, and no delegate provided.";
  }
}

bool PepperCursorSetter::SetInstanceCursor(
    const protocol::CursorShapeInfo& cursor_shape) {
  // If the cursor is empty then tell PPAPI there is no cursor.
  if (IsCursorShapeEmpty(cursor_shape)) {
    pp::MouseCursor::SetCursor(instance_, PP_MOUSECURSOR_TYPE_NONE);
    return true;
  }

  // pp::MouseCursor requires image to be in the native format.
  if (pp::ImageData::GetNativeImageDataFormat() !=
      PP_IMAGEDATAFORMAT_BGRA_PREMUL) {
    LOG(WARNING) << "Unable to set cursor shape - native image format is not"
                    " premultiplied BGRA";
    return false;
  }

  // Create a new ImageData to pass to SetCursor().
  pp::Size size(cursor_shape.width(), cursor_shape.height());
  pp::Point hotspot(cursor_shape.hotspot_x(), cursor_shape.hotspot_y());
  pp::ImageData image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, false);
  if (image.is_null()) {
    LOG(WARNING) << "Unable to create cursor image";
    return false;
  }

  // Fill the pixel data and pass the cursor to PPAPI to set.
  const int kBytesPerPixel = sizeof(uint32_t);
  const uint32_t* src_row_data = reinterpret_cast<const uint32_t*>(
      cursor_shape.data().data());
  const int bytes_per_row = size.width() * kBytesPerPixel;
  uint8* dst_row_data = reinterpret_cast<uint8*>(image.data());
  for (int row = 0; row < size.height(); row++) {
    memcpy(dst_row_data, src_row_data, bytes_per_row);
    src_row_data += size.width();
    dst_row_data += image.stride();
  }

  return pp::MouseCursor::SetCursor(
      instance_, PP_MOUSECURSOR_TYPE_CUSTOM, image, hotspot);
}

}  // namespace remoting
