// 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 "chromecast/renderer/media/cma_message_filter_proxy.h"

#include "base/bind.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/time/time.h"
#include "chromecast/common/media/cma_messages.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_sender.h"

namespace chromecast {
namespace media {

CmaMessageFilterProxy::MediaDelegate::MediaDelegate() {
}

CmaMessageFilterProxy::MediaDelegate::~MediaDelegate() {
}

CmaMessageFilterProxy::AudioDelegate::AudioDelegate() {
}

CmaMessageFilterProxy::AudioDelegate::~AudioDelegate() {
}

CmaMessageFilterProxy::VideoDelegate::VideoDelegate() {
}

CmaMessageFilterProxy::VideoDelegate::~VideoDelegate() {
}

CmaMessageFilterProxy::DelegateEntry::DelegateEntry() {
}

CmaMessageFilterProxy::DelegateEntry::~DelegateEntry() {
}

CmaMessageFilterProxy* CmaMessageFilterProxy::filter_ = NULL;

// static
CmaMessageFilterProxy* CmaMessageFilterProxy::Get() {
  return filter_;
}

CmaMessageFilterProxy::CmaMessageFilterProxy(
    const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
    : sender_(NULL),
      io_message_loop_(io_message_loop) {
  DCHECK(!filter_);
  filter_ = this;
}

int CmaMessageFilterProxy::CreateChannel() {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  DelegateEntry* entry = new DelegateEntry();
  int id = delegates_.Add(entry);
  return id;
}

void CmaMessageFilterProxy::DestroyChannel(int id) {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  delegates_.Remove(id);
  delete entry;
}

bool CmaMessageFilterProxy::SetMediaDelegate(
    int id, const MediaDelegate& media_delegate) {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return false;
  entry->media_delegate = media_delegate;
  return true;
}

bool CmaMessageFilterProxy::SetAudioDelegate(
    int id, const AudioDelegate& audio_delegate) {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return false;
  entry->audio_delegate = audio_delegate;
  return true;
}

bool CmaMessageFilterProxy::SetVideoDelegate(
    int id, const VideoDelegate& video_delegate) {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return false;
  entry->video_delegate = video_delegate;
  return true;
}

bool CmaMessageFilterProxy::Send(scoped_ptr<IPC::Message> message) {
  DCHECK(io_message_loop_->BelongsToCurrentThread());
  if (!sender_)
    return false;
  bool status = sender_->Send(message.release());
  return status;
}

bool CmaMessageFilterProxy::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(CmaMessageFilterProxy, message)
    IPC_MESSAGE_HANDLER(CmaMsg_AvPipeCreated, OnAvPipeCreated)
    IPC_MESSAGE_HANDLER(CmaMsg_NotifyPipeRead, OnPipeRead)
    IPC_MESSAGE_HANDLER(CmaMsg_MediaStateChanged, OnMediaStateChanged)
    IPC_MESSAGE_HANDLER(CmaMsg_TrackStateChanged, OnTrackStateChanged)
    IPC_MESSAGE_HANDLER(CmaMsg_Eos, OnEos)
    IPC_MESSAGE_HANDLER(CmaMsg_TimeUpdate, OnTimeUpdate)
    IPC_MESSAGE_HANDLER(CmaMsg_BufferingNotification, OnBufferingNotification)
    IPC_MESSAGE_HANDLER(CmaMsg_PlaybackError, OnPlaybackError)
    IPC_MESSAGE_HANDLER(CmaMsg_PlaybackStatistics, OnStatisticsUpdated)
    IPC_MESSAGE_HANDLER(CmaMsg_NaturalSizeChanged, OnNaturalSizeChanged)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void CmaMessageFilterProxy::OnFilterAdded(IPC::Sender* sender) {
  sender_ = sender;
}

void CmaMessageFilterProxy::OnFilterRemoved() {
  sender_ = NULL;
}

void CmaMessageFilterProxy::OnChannelClosing() {
  sender_ = NULL;
}

CmaMessageFilterProxy::~CmaMessageFilterProxy() {
  DCHECK(filter_);
  filter_ = NULL;
}

void CmaMessageFilterProxy::OnAvPipeCreated(
    int id,
    TrackId track_id,
    bool status,
    base::SharedMemoryHandle shared_memory_handle,
    base::FileDescriptor socket_handle) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const AvPipeCB& cb = (track_id == kAudioTrackId) ?
      entry->audio_delegate.av_pipe_cb :
      entry->video_delegate.av_pipe_cb;
  if (!cb.is_null())
    cb.Run(status, shared_memory_handle, socket_handle);
}

void CmaMessageFilterProxy::OnPipeRead(
    int id, TrackId track_id) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const base::Closure& cb = (track_id == kAudioTrackId) ?
      entry->audio_delegate.pipe_read_cb :
      entry->video_delegate.pipe_read_cb;
  if (!cb.is_null())
    cb.Run();
}

void CmaMessageFilterProxy::OnMediaStateChanged(
    int id, ::media::PipelineStatus status) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const ::media::PipelineStatusCB& cb =
      entry->media_delegate.state_changed_cb;
  if (!cb.is_null())
    cb.Run(status);
}

void CmaMessageFilterProxy::OnTrackStateChanged(
    int id, TrackId track_id, ::media::PipelineStatus status) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const ::media::PipelineStatusCB& cb = (track_id == kAudioTrackId) ?
      entry->audio_delegate.state_changed_cb :
      entry->video_delegate.state_changed_cb;
  if (!cb.is_null())
    cb.Run(status);
}

void CmaMessageFilterProxy::OnEos(int id, TrackId track_id) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const base::Closure& cb = (track_id == kAudioTrackId) ?
      entry->audio_delegate.client.eos_cb :
      entry->video_delegate.client.av_pipeline_client.eos_cb;
  if (!cb.is_null())
    cb.Run();
}

void CmaMessageFilterProxy::OnTimeUpdate(
    int id,
    base::TimeDelta time,
    base::TimeDelta max_time,
    base::TimeTicks stc) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const MediaPipelineClient::TimeUpdateCB& cb =
      entry->media_delegate.client.time_update_cb;
  if (!cb.is_null())
    cb.Run(time, max_time, stc);
}

void CmaMessageFilterProxy::OnBufferingNotification(
    int id, ::media::BufferingState buffering_state) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const ::media::BufferingStateCB& cb =
      entry->media_delegate.client.buffering_state_cb;
  if (!cb.is_null())
    cb.Run(buffering_state);
}

void CmaMessageFilterProxy::OnPlaybackError(
    int id, TrackId track_id, ::media::PipelineStatus status) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const ::media::PipelineStatusCB& cb =
      (track_id == kNoTrackId) ? entry->media_delegate.client.error_cb :
      (track_id == kAudioTrackId) ?
          entry->audio_delegate.client.playback_error_cb :
          entry->video_delegate.client.av_pipeline_client.playback_error_cb;
  if (!cb.is_null())
    cb.Run(status);
}

void CmaMessageFilterProxy::OnStatisticsUpdated(
    int id, TrackId track_id, const ::media::PipelineStatistics& stats) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  const ::media::StatisticsCB& cb = (track_id == kAudioTrackId) ?
      entry->audio_delegate.client.statistics_cb :
      entry->video_delegate.client.av_pipeline_client.statistics_cb;
  if (!cb.is_null())
    cb.Run(stats);
}

void CmaMessageFilterProxy::OnNaturalSizeChanged(
    int id, TrackId track_id, const gfx::Size& size) {
  DelegateEntry* entry = delegates_.Lookup(id);
  if (!entry)
    return;
  if (track_id == kAudioTrackId)
    return;
  const VideoPipelineClient::NaturalSizeChangedCB& cb =
      entry->video_delegate.client.natural_size_changed_cb;
  if (!cb.is_null())
    cb.Run(size);
}

}  // namespace media
}  // namespace chromecast