// 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 "content/renderer/media/aec_dump_message_filter.h"

#include "base/single_thread_task_runner.h"
#include "content/common/media/aec_dump_messages.h"
#include "content/renderer/media/webrtc_logging.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_sender.h"

namespace {
const int kInvalidDelegateId = -1;
}

namespace content {

AecDumpMessageFilter* AecDumpMessageFilter::g_filter = NULL;

AecDumpMessageFilter::AecDumpMessageFilter(
    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
    const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner)
    : sender_(NULL),
      delegate_id_counter_(1),
      io_task_runner_(io_task_runner),
      main_task_runner_(main_task_runner) {
  DCHECK(!g_filter);
  g_filter = this;
}

AecDumpMessageFilter::~AecDumpMessageFilter() {
  DCHECK_EQ(g_filter, this);
  g_filter = NULL;
}

// static
scoped_refptr<AecDumpMessageFilter> AecDumpMessageFilter::Get() {
  return g_filter;
}

void AecDumpMessageFilter::AddDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate) {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  DCHECK(delegate);
  DCHECK_EQ(kInvalidDelegateId, GetIdForDelegate(delegate));

  int id = delegate_id_counter_++;
  delegates_[id] = delegate;

  io_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&AecDumpMessageFilter::RegisterAecDumpConsumer, this, id));
}

void AecDumpMessageFilter::RemoveDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate) {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  DCHECK(delegate);

  int id = GetIdForDelegate(delegate);
  DCHECK_NE(kInvalidDelegateId, id);
  delegates_.erase(id);

  io_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&AecDumpMessageFilter::UnregisterAecDumpConsumer, this, id));
}

void AecDumpMessageFilter::Send(IPC::Message* message) {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  if (sender_)
    sender_->Send(message);
  else
    delete message;
}

void AecDumpMessageFilter::RegisterAecDumpConsumer(int id) {
  Send(new AecDumpMsg_RegisterAecDumpConsumer(id));
}

void AecDumpMessageFilter::UnregisterAecDumpConsumer(int id) {
  Send(new AecDumpMsg_UnregisterAecDumpConsumer(id));
}

bool AecDumpMessageFilter::OnMessageReceived(const IPC::Message& message) {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(AecDumpMessageFilter, message)
    IPC_MESSAGE_HANDLER(AecDumpMsg_EnableAecDump, OnEnableAecDump)
    IPC_MESSAGE_HANDLER(AecDumpMsg_DisableAecDump, OnDisableAecDump)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void AecDumpMessageFilter::OnFilterAdded(IPC::Channel* channel) {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  sender_ = channel;
}

void AecDumpMessageFilter::OnFilterRemoved() {
  DCHECK(io_task_runner_->BelongsToCurrentThread());

  // Once removed, a filter will not be used again.  At this time the
  // observer must be notified so it releases its reference.
  OnChannelClosing();
}

void AecDumpMessageFilter::OnChannelClosing() {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  sender_ = NULL;
  main_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&AecDumpMessageFilter::DoChannelClosingOnDelegates, this));
}

void AecDumpMessageFilter::OnEnableAecDump(
    int id,
    IPC::PlatformFileForTransit file_handle) {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  main_task_runner_->PostTask(
      FROM_HERE, base::Bind(&AecDumpMessageFilter::DoEnableAecDump, this, id,
                            file_handle));
}

void AecDumpMessageFilter::OnDisableAecDump() {
  DCHECK(io_task_runner_->BelongsToCurrentThread());
  main_task_runner_->PostTask(
      FROM_HERE, base::Bind(&AecDumpMessageFilter::DoDisableAecDump, this));
}

void AecDumpMessageFilter::DoEnableAecDump(
    int id,
    IPC::PlatformFileForTransit file_handle) {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  DelegateMap::iterator it = delegates_.find(id);
  if (it != delegates_.end()) {
    it->second->OnAecDumpFile(file_handle);
  } else {
    // Delegate has been removed, we must close the file.
    base::File file = IPC::PlatformFileForTransitToFile(file_handle);
    DCHECK(file.IsValid());
    file.Close();
  }
}

void AecDumpMessageFilter::DoDisableAecDump() {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  for (DelegateMap::iterator it = delegates_.begin();
       it != delegates_.end(); ++it) {
    it->second->OnDisableAecDump();
  }
}

void AecDumpMessageFilter::DoChannelClosingOnDelegates() {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  for (DelegateMap::iterator it = delegates_.begin();
       it != delegates_.end(); ++it) {
    it->second->OnIpcClosing();
  }
  delegates_.clear();
}

int AecDumpMessageFilter::GetIdForDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate) {
  DCHECK(main_task_runner_->BelongsToCurrentThread());
  for (DelegateMap::iterator it = delegates_.begin();
       it != delegates_.end(); ++it) {
    if (it->second == delegate)
      return it->first;
  }
  return kInvalidDelegateId;
}

}  // namespace content
