// Copyright (c) 2010 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 "chrome/browser/device_orientation/dispatcher_host.h"

#include "base/scoped_ptr.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/device_orientation/orientation.h"
#include "chrome/browser/device_orientation/provider.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_notification_task.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/render_messages_params.h"
#include "ipc/ipc_message.h"

namespace device_orientation {

DispatcherHost::DispatcherHost(int process_id)
    : process_id_(process_id),
      observers_map_(),
      provider_(NULL) {
}

DispatcherHost::~DispatcherHost() {
}

class DispatcherHost::ObserverDelegate
    : public base::RefCounted<ObserverDelegate>, public Provider::Observer {
 public:
    // Create ObserverDelegate that observes provider and forwards updates to
    // render_view_id in process_id.
    // Will stop observing provider when destructed.
    ObserverDelegate(Provider* provider,
                     int process_id,
                     int render_view_id);

    // From Provider::Observer.
    virtual void OnOrientationUpdate(const Orientation& orientation);

 private:
  friend class base::RefCounted<ObserverDelegate>;
  virtual ~ObserverDelegate();

  scoped_refptr<Provider> provider_;
  int process_id_;
  int render_view_id_;

  DISALLOW_COPY_AND_ASSIGN(ObserverDelegate);
};

DispatcherHost::ObserverDelegate::ObserverDelegate(Provider* provider,
                                                   int process_id,
                                                   int render_view_id)
    : provider_(provider),
      process_id_(process_id),
      render_view_id_(render_view_id) {
  provider_->AddObserver(this);
}

DispatcherHost::ObserverDelegate::~ObserverDelegate() {
  provider_->RemoveObserver(this);
}

void DispatcherHost::ObserverDelegate::OnOrientationUpdate(
    const Orientation& orientation) {
  ViewMsg_DeviceOrientationUpdated_Params params;
  params.can_provide_alpha = orientation.can_provide_alpha_;
  params.alpha = orientation.alpha_;
  params.can_provide_beta = orientation.can_provide_beta_;
  params.beta = orientation.beta_;
  params.can_provide_gamma = orientation.can_provide_gamma_;
  params.gamma = orientation.gamma_;

  IPC::Message* message = new ViewMsg_DeviceOrientationUpdated(render_view_id_,
                                                               params);
  CallRenderViewHost(process_id_, render_view_id_, &RenderViewHost::Send,
                     message);
}

bool DispatcherHost::OnMessageReceived(const IPC::Message& msg,
                                       bool* msg_was_ok) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(DispatcherHost, msg, *msg_was_ok)
    IPC_MESSAGE_HANDLER(ViewHostMsg_DeviceOrientation_StartUpdating,
                        OnStartUpdating)
    IPC_MESSAGE_HANDLER(ViewHostMsg_DeviceOrientation_StopUpdating,
                        OnStopUpdating)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void DispatcherHost::OnStartUpdating(int render_view_id) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));

  if (!provider_)
    provider_ = Provider::GetInstance();

  observers_map_[render_view_id] = new ObserverDelegate(provider_,
                                                        process_id_,
                                                        render_view_id);
}

void DispatcherHost::OnStopUpdating(int render_view_id) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));

  observers_map_.erase(render_view_id);
}

}  // namespace device_orientation