// 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 "content/renderer/browser_plugin/browser_plugin_manager.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/browser_plugin/browser_plugin_constants.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/frame_messages.h"
#include "content/public/renderer/browser_plugin_delegate.h"
#include "content/renderer/browser_plugin/browser_plugin.h"
#include "content/renderer/render_thread_impl.h"
#include "ipc/ipc_message_macros.h"

namespace content {

// static
BrowserPluginManager* BrowserPluginManager::Get() {
  if (!RenderThreadImpl::current())
    return nullptr;
  return RenderThreadImpl::current()->browser_plugin_manager();
}

BrowserPluginManager::BrowserPluginManager() {
}

BrowserPluginManager::~BrowserPluginManager() {
}

void BrowserPluginManager::AddBrowserPlugin(
    int browser_plugin_instance_id,
    BrowserPlugin* browser_plugin) {
  instances_.AddWithID(browser_plugin, browser_plugin_instance_id);
}

void BrowserPluginManager::RemoveBrowserPlugin(int browser_plugin_instance_id) {
  instances_.Remove(browser_plugin_instance_id);
}

BrowserPlugin* BrowserPluginManager::GetBrowserPlugin(
    int browser_plugin_instance_id) const {
  return instances_.Lookup(browser_plugin_instance_id);
}

int BrowserPluginManager::GetNextInstanceID() {
  return RenderThreadImpl::current()->GenerateRoutingID();
}

void BrowserPluginManager::UpdateFocusState() {
  IDMap<BrowserPlugin>::iterator iter(&instances_);
  while (!iter.IsAtEnd()) {
    iter.GetCurrentValue()->UpdateGuestFocusState(blink::WebFocusTypeNone);
    iter.Advance();
  }
}

void BrowserPluginManager::Attach(int browser_plugin_instance_id) {
  BrowserPlugin* plugin = GetBrowserPlugin(browser_plugin_instance_id);
  if (plugin)
    plugin->Attach();
}

void BrowserPluginManager::Detach(int browser_plugin_instance_id) {
  BrowserPlugin* plugin = GetBrowserPlugin(browser_plugin_instance_id);
  if (plugin)
    plugin->Detach();
}

BrowserPlugin* BrowserPluginManager::CreateBrowserPlugin(
    RenderFrame* render_frame,
    const base::WeakPtr<BrowserPluginDelegate>& delegate) {
  return new BrowserPlugin(render_frame, delegate);
}

void BrowserPluginManager::DidCommitCompositorFrame(
    int render_frame_routing_id) {
  IDMap<BrowserPlugin>::iterator iter(&instances_);
  while (!iter.IsAtEnd()) {
    if (iter.GetCurrentValue()->render_frame_routing_id() ==
        render_frame_routing_id) {
      iter.GetCurrentValue()->DidCommitCompositorFrame();
    }
    iter.Advance();
  }
}

bool BrowserPluginManager::OnControlMessageReceived(
    const IPC::Message& message) {
  if (!BrowserPlugin::ShouldForwardToBrowserPlugin(message))
    return false;

  int browser_plugin_instance_id = browser_plugin::kInstanceIDNone;
  // All allowed messages must have |browser_plugin_instance_id| as their
  // first parameter.
  base::PickleIterator iter(message);
  bool success = iter.ReadInt(&browser_plugin_instance_id);
  DCHECK(success);
  BrowserPlugin* plugin = GetBrowserPlugin(browser_plugin_instance_id);
  if (plugin && plugin->OnMessageReceived(message))
    return true;

  // TODO(fsamuel): This is probably forcing the compositor to continue working
  // even on display:none. We should optimize this.
  if (message.type() == BrowserPluginMsg_CompositorFrameSwapped::ID) {
    OnCompositorFrameSwappedPluginUnavailable(message);
    return true;
  }

  return false;
}

bool BrowserPluginManager::Send(IPC::Message* msg) {
  return RenderThreadImpl::current()->Send(msg);
}

void BrowserPluginManager::OnCompositorFrameSwappedPluginUnavailable(
    const IPC::Message& message) {
  BrowserPluginMsg_CompositorFrameSwapped::Param param;
  if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
    return;

  FrameHostMsg_CompositorFrameSwappedACK_Params params;
  params.producing_host_id = base::get<1>(param).producing_host_id;
  params.producing_route_id = base::get<1>(param).producing_route_id;
  params.output_surface_id = base::get<1>(param).output_surface_id;
  Send(new BrowserPluginHostMsg_CompositorFrameSwappedACK(
      base::get<0>(param), params));
}

}  // namespace content
