// Copyright 2015 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 "ui/platform_window/android/platform_window_android.h"

#include <android/input.h>
#include <android/native_window_jni.h>

#include "base/android/jni_android.h"
#include "jni/PlatformWindowAndroid_jni.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_code_conversion_android.h"
#include "ui/gfx/geometry/point.h"
#include "ui/platform_window/platform_window_delegate.h"

using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;

namespace ui {

namespace {

ui::EventType MotionEventActionToEventType(jint action) {
  switch (action) {
    case AMOTION_EVENT_ACTION_DOWN:
    case AMOTION_EVENT_ACTION_POINTER_DOWN:
      return ui::ET_TOUCH_PRESSED;
    case AMOTION_EVENT_ACTION_UP:
    case AMOTION_EVENT_ACTION_POINTER_UP:
      return ui::ET_TOUCH_RELEASED;
    case AMOTION_EVENT_ACTION_MOVE:
      return ui::ET_TOUCH_MOVED;
    case AMOTION_EVENT_ACTION_CANCEL:
      return ui::ET_TOUCH_CANCELLED;
    case AMOTION_EVENT_ACTION_OUTSIDE:
    case AMOTION_EVENT_ACTION_HOVER_MOVE:
    case AMOTION_EVENT_ACTION_SCROLL:
    case AMOTION_EVENT_ACTION_HOVER_ENTER:
    case AMOTION_EVENT_ACTION_HOVER_EXIT:
    default:
      NOTIMPLEMENTED() << "Unimplemented motion action: " << action;
  }
  return ui::ET_UNKNOWN;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// PlatformWindowAndroid, public:

PlatformWindowAndroid::PlatformWindowAndroid(PlatformWindowDelegate* delegate)
    : delegate_(delegate),
      window_(NULL),
      id_generator_(0),
      weak_factory_(this) {
}

PlatformWindowAndroid::~PlatformWindowAndroid() {
  if (window_)
    ReleaseWindow();
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> scoped_obj =
      java_platform_window_android_.get(env);
  if (!scoped_obj.is_null()) {
    Java_PlatformWindowAndroid_detach(env, scoped_obj);
  }
}

void PlatformWindowAndroid::Destroy(JNIEnv* env,
                                    const JavaParamRef<jobject>& obj) {
  delegate_->OnClosed();
}

void PlatformWindowAndroid::SurfaceCreated(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    const JavaParamRef<jobject>& jsurface,
    float device_pixel_ratio) {
  // Note: This ensures that any local references used by
  // ANativeWindow_fromSurface are released immediately. This is needed as a
  // workaround for https://code.google.com/p/android/issues/detail?id=68174
  {
    base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
    window_ = ANativeWindow_fromSurface(env, jsurface);
  }
  delegate_->OnAcceleratedWidgetAvailable(window_, device_pixel_ratio);
}

void PlatformWindowAndroid::SurfaceDestroyed(JNIEnv* env,
                                             const JavaParamRef<jobject>& obj) {
  DCHECK(window_);
  delegate_->OnAcceleratedWidgetDestroyed();
  ReleaseWindow();
}

void PlatformWindowAndroid::SurfaceSetSize(JNIEnv* env,
                                           const JavaParamRef<jobject>& obj,
                                           jint width,
                                           jint height,
                                           jfloat density) {
  size_ = gfx::Size(static_cast<int>(width), static_cast<int>(height));
  delegate_->OnBoundsChanged(gfx::Rect(size_));
}

bool PlatformWindowAndroid::TouchEvent(JNIEnv* env,
                                       const JavaParamRef<jobject>& obj,
                                       jlong time_ms,
                                       jint masked_action,
                                       jint pointer_id,
                                       jfloat x,
                                       jfloat y,
                                       jfloat pressure,
                                       jfloat touch_major,
                                       jfloat touch_minor,
                                       jfloat orientation,
                                       jfloat h_wheel,
                                       jfloat v_wheel) {
  ui::EventType event_type = MotionEventActionToEventType(masked_action);
  if (event_type == ui::ET_UNKNOWN)
    return false;
  ui::TouchEvent touch(
      event_type, gfx::Point(),
      base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms),
      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, pointer_id,
                         touch_major, touch_minor, pressure),
      ui::EF_NONE, orientation);
  touch.set_location_f(gfx::PointF(x, y));
  touch.set_root_location_f(gfx::PointF(x, y));
  delegate_->DispatchEvent(&touch);
  return true;
}

bool PlatformWindowAndroid::KeyEvent(JNIEnv* env,
                                     const JavaParamRef<jobject>& obj,
                                     bool pressed,
                                     jint key_code,
                                     jint unicode_character) {
  ui::KeyEvent key_event(pressed ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
                         ui::KeyboardCodeFromAndroidKeyCode(key_code), 0);
  delegate_->DispatchEvent(&key_event);
  if (pressed && unicode_character) {
    ui::KeyEvent char_event(unicode_character,
                            ui::KeyboardCodeFromAndroidKeyCode(key_code), 0);
    delegate_->DispatchEvent(&char_event);
  }
  return true;
}

void PlatformWindowAndroid::ReleaseWindow() {
  ANativeWindow_release(window_);
  window_ = NULL;
}

////////////////////////////////////////////////////////////////////////////////
// PlatformWindowAndroid, PlatformWindow implementation:

void PlatformWindowAndroid::Show() {
  if (!java_platform_window_android_.is_uninitialized())
    return;
  JNIEnv* env = base::android::AttachCurrentThread();
  java_platform_window_android_ = JavaObjectWeakGlobalRef(
      env, Java_PlatformWindowAndroid_createForActivity(
               env, reinterpret_cast<jlong>(this),
               reinterpret_cast<jlong>(&platform_ime_controller_))
               .obj());
}

void PlatformWindowAndroid::Hide() {
  // Nothing to do. View is always visible.
}

void PlatformWindowAndroid::Close() {
  delegate_->OnCloseRequest();
}

void PlatformWindowAndroid::PrepareForShutdown() {}

void PlatformWindowAndroid::SetBounds(const gfx::Rect& bounds) {
  NOTIMPLEMENTED();
}

gfx::Rect PlatformWindowAndroid::GetBounds() {
  return gfx::Rect(size_);
}

void PlatformWindowAndroid::SetTitle(const base::string16& title) {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::SetCapture() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::ReleaseCapture() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::ToggleFullscreen() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::Maximize() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::Minimize() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::Restore() {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::SetCursor(PlatformCursor cursor) {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::MoveCursorTo(const gfx::Point& location) {
  NOTIMPLEMENTED();
}

void PlatformWindowAndroid::ConfineCursorToBounds(const gfx::Rect& bounds) {
  NOTIMPLEMENTED();
}

PlatformImeController* PlatformWindowAndroid::GetPlatformImeController() {
  return &platform_ime_controller_;
}

}  // namespace ui
