// Copyright (c) 2013 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/keyboard/keyboard_controller_proxy.h"

#include "base/command_line.h"
#include "base/values.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/bindings_policy.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/window.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/keyboard/keyboard_constants.h"
#include "ui/keyboard/keyboard_switches.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/wm/core/shadow.h"

namespace {

// The WebContentsDelegate for the keyboard.
// The delegate deletes itself when the keyboard is destroyed.
class KeyboardContentsDelegate : public content::WebContentsDelegate,
                                 public content::WebContentsObserver {
 public:
  KeyboardContentsDelegate(keyboard::KeyboardControllerProxy* proxy)
      : proxy_(proxy) {}
  virtual ~KeyboardContentsDelegate() {}

 private:
  // Overridden from content::WebContentsDelegate:
  virtual content::WebContents* OpenURLFromTab(
      content::WebContents* source,
      const content::OpenURLParams& params) OVERRIDE {
    source->GetController().LoadURL(
        params.url, params.referrer, params.transition, params.extra_headers);
    Observe(source);
    return source;
  }

  virtual bool IsPopupOrPanel(
      const content::WebContents* source) const OVERRIDE {
    return true;
  }

  virtual void MoveContents(content::WebContents* source,
                            const gfx::Rect& pos) OVERRIDE {
    aura::Window* keyboard = proxy_->GetKeyboardWindow();
    // keyboard window must have been added to keyboard container window at this
    // point. Otherwise, wrong keyboard bounds is used and may cause problem as
    // described in crbug.com/367788.
    DCHECK(keyboard->parent());
    gfx::Rect bounds = keyboard->bounds();
    int new_height = pos.height();
    bounds.set_y(bounds.y() + bounds.height() - new_height);
    bounds.set_height(new_height);
    keyboard->SetBounds(bounds);
  }

  // Overridden from content::WebContentsDelegate:
  virtual void RequestMediaAccessPermission(content::WebContents* web_contents,
      const content::MediaStreamRequest& request,
      const content::MediaResponseCallback& callback) OVERRIDE {
    proxy_->RequestAudioInput(web_contents, request, callback);
  }

  // Overridden from content::WebContentsObserver:
  virtual void WebContentsDestroyed() OVERRIDE {
    delete this;
  }

  keyboard::KeyboardControllerProxy* proxy_;

  DISALLOW_COPY_AND_ASSIGN(KeyboardContentsDelegate);
};

}  // namespace

namespace keyboard {

KeyboardControllerProxy::KeyboardControllerProxy()
    : default_url_(kKeyboardURL) {
}

KeyboardControllerProxy::~KeyboardControllerProxy() {
}

const GURL& KeyboardControllerProxy::GetVirtualKeyboardUrl() {
  if (keyboard::IsInputViewEnabled()) {
    const GURL& override_url = GetOverrideContentUrl();
    return override_url.is_valid() ? override_url : default_url_;
  } else {
    return default_url_;
  }
}

void KeyboardControllerProxy::LoadContents(const GURL& url) {
  if (keyboard_contents_) {
    content::OpenURLParams params(
        url,
        content::Referrer(),
        SINGLETON_TAB,
        ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
        false);
    keyboard_contents_->OpenURL(params);
  }
}

aura::Window* KeyboardControllerProxy::GetKeyboardWindow() {
  if (!keyboard_contents_) {
    content::BrowserContext* context = GetBrowserContext();
    keyboard_contents_.reset(content::WebContents::Create(
        content::WebContents::CreateParams(context,
            content::SiteInstance::CreateForURL(context,
                                                GetVirtualKeyboardUrl()))));
    keyboard_contents_->SetDelegate(new KeyboardContentsDelegate(this));
    SetupWebContents(keyboard_contents_.get());
    LoadContents(GetVirtualKeyboardUrl());
    keyboard_contents_->GetNativeView()->AddObserver(this);
  }

  return keyboard_contents_->GetNativeView();
}

bool KeyboardControllerProxy::HasKeyboardWindow() const {
  return keyboard_contents_;
}

void KeyboardControllerProxy::ShowKeyboardContainer(aura::Window* container) {
  GetKeyboardWindow()->Show();
  container->Show();
}

void KeyboardControllerProxy::HideKeyboardContainer(aura::Window* container) {
  container->Hide();
  GetKeyboardWindow()->Hide();
}

void KeyboardControllerProxy::SetUpdateInputType(ui::TextInputType type) {
}

void KeyboardControllerProxy::EnsureCaretInWorkArea() {
  if (GetInputMethod()->GetTextInputClient()) {
    aura::Window* keyboard_window = GetKeyboardWindow();
    aura::Window* root_window = keyboard_window->GetRootWindow();
    gfx::Rect available_bounds = root_window->bounds();
    gfx::Rect keyboard_bounds = keyboard_window->bounds();
    available_bounds.set_height(available_bounds.height() -
        keyboard_bounds.height());
    GetInputMethod()->GetTextInputClient()->EnsureCaretInRect(available_bounds);
  }
}

void KeyboardControllerProxy::LoadSystemKeyboard() {
  DCHECK(keyboard_contents_);
  if (keyboard_contents_->GetURL() != default_url_) {
    // TODO(bshe): The height of system virtual keyboard and IME virtual
    // keyboard may different. The height needs to be restored too.
    LoadContents(default_url_);
  }
}

void KeyboardControllerProxy::ReloadKeyboardIfNeeded() {
  DCHECK(keyboard_contents_);
  if (keyboard_contents_->GetURL() != GetVirtualKeyboardUrl()) {
    if (keyboard_contents_->GetURL().GetOrigin() !=
        GetVirtualKeyboardUrl().GetOrigin()) {
      // Sets keyboard window height to 0 before navigate to a keyboard in a
      // different extension. This keeps the UX the same as Android.
      gfx::Rect bounds = GetKeyboardWindow()->bounds();
      bounds.set_y(bounds.y() + bounds.height());
      bounds.set_height(0);
      GetKeyboardWindow()->SetBounds(bounds);
    }
    LoadContents(GetVirtualKeyboardUrl());
  }
}

void KeyboardControllerProxy::SetupWebContents(content::WebContents* contents) {
}

void KeyboardControllerProxy::OnWindowBoundsChanged(
    aura::Window* window,
    const gfx::Rect& old_bounds,
    const gfx::Rect& new_bounds) {
  if (!shadow_) {
    shadow_.reset(new wm::Shadow());
    shadow_->Init(wm::Shadow::STYLE_ACTIVE);
    shadow_->layer()->SetVisible(true);
    DCHECK(keyboard_contents_->GetNativeView()->parent());
    keyboard_contents_->GetNativeView()->parent()->layer()->Add(
        shadow_->layer());
  }

  shadow_->SetContentBounds(new_bounds);
}

void KeyboardControllerProxy::OnWindowDestroyed(aura::Window* window) {
  window->RemoveObserver(this);
}

}  // namespace keyboard