// 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 "media/audio/audio_device_thread.h"

#include <algorithm>

#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/aligned_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
#include "media/base/audio_bus.h"

using base::PlatformThread;

namespace media {

// The actual worker thread implementation.  It's very bare bones and much
// simpler than SimpleThread (no synchronization in Start, etc) and supports
// joining the thread handle asynchronously via a provided message loop even
// after the Thread object itself has been deleted.
class AudioDeviceThread::Thread
    : public PlatformThread::Delegate,
      public base::RefCountedThreadSafe<AudioDeviceThread::Thread> {
 public:
  Thread(AudioDeviceThread::Callback* callback,
         base::SyncSocket::Handle socket,
         const char* thread_name,
         bool synchronized_buffers);

  void Start();

  // Stops the thread.  If |loop_for_join| is non-NULL, the function posts
  // a task to join (close) the thread handle later instead of waiting for
  // the thread.  If loop_for_join is NULL, then the function waits
  // synchronously for the thread to terminate.
  void Stop(base::MessageLoop* loop_for_join);

 private:
  friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>;
  virtual ~Thread();

  // Overrides from PlatformThread::Delegate.
  virtual void ThreadMain() OVERRIDE;

  // Runs the loop that reads from the socket.
  void Run();

 private:
  base::PlatformThreadHandle thread_;
  AudioDeviceThread::Callback* callback_;
  base::CancelableSyncSocket socket_;
  base::Lock callback_lock_;
  const char* thread_name_;
  const bool synchronized_buffers_;

  DISALLOW_COPY_AND_ASSIGN(Thread);
};

// AudioDeviceThread implementation

AudioDeviceThread::AudioDeviceThread() {
}

AudioDeviceThread::~AudioDeviceThread() { DCHECK(!thread_.get()); }

void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback,
                              base::SyncSocket::Handle socket,
                              const char* thread_name,
                              bool synchronized_buffers) {
  base::AutoLock auto_lock(thread_lock_);
  CHECK(!thread_);
  thread_ = new AudioDeviceThread::Thread(
      callback, socket, thread_name, synchronized_buffers);
  thread_->Start();
}

void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) {
  base::AutoLock auto_lock(thread_lock_);
  if (thread_.get()) {
    thread_->Stop(loop_for_join);
    thread_ = NULL;
  }
}

bool AudioDeviceThread::IsStopped() {
  base::AutoLock auto_lock(thread_lock_);
  return !thread_;
}

// AudioDeviceThread::Thread implementation
AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback,
                                  base::SyncSocket::Handle socket,
                                  const char* thread_name,
                                  bool synchronized_buffers)
    : thread_(),
      callback_(callback),
      socket_(socket),
      thread_name_(thread_name),
      synchronized_buffers_(synchronized_buffers) {
}

AudioDeviceThread::Thread::~Thread() {
  DCHECK(thread_.is_null());
}

void AudioDeviceThread::Thread::Start() {
  base::AutoLock auto_lock(callback_lock_);
  DCHECK(thread_.is_null());
  // This reference will be released when the thread exists.
  AddRef();

  PlatformThread::CreateWithPriority(0, this, &thread_,
                                     base::kThreadPriority_RealtimeAudio);
  CHECK(!thread_.is_null());
}

void AudioDeviceThread::Thread::Stop(base::MessageLoop* loop_for_join) {
  socket_.Shutdown();

  base::PlatformThreadHandle thread = base::PlatformThreadHandle();

  {  // NOLINT
    base::AutoLock auto_lock(callback_lock_);
    callback_ = NULL;
    std::swap(thread, thread_);
  }

  if (!thread.is_null()) {
    if (loop_for_join) {
      loop_for_join->PostTask(FROM_HERE,
          base::Bind(&base::PlatformThread::Join, thread));
    } else {
      base::PlatformThread::Join(thread);
    }
  }
}

void AudioDeviceThread::Thread::ThreadMain() {
  PlatformThread::SetName(thread_name_);

  // Singleton access is safe from this thread as long as callback is non-NULL.
  // The callback is the only point where the thread calls out to 'unknown' code
  // that might touch singletons and the lifetime of the callback is controlled
  // by another thread on which singleton access is OK as well.
  base::ThreadRestrictions::SetSingletonAllowed(true);

  {  // NOLINT
    base::AutoLock auto_lock(callback_lock_);
    if (callback_)
      callback_->InitializeOnAudioThread();
  }

  Run();

  // Release the reference for the thread. Note that after this, the Thread
  // instance will most likely be deleted.
  Release();
}

void AudioDeviceThread::Thread::Run() {
  uint32 buffer_index = 0;
  while (true) {
    int pending_data = 0;
    size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data));
    if (bytes_read != sizeof(pending_data)) {
      DCHECK_EQ(bytes_read, 0U);
      break;
    }

    {
      base::AutoLock auto_lock(callback_lock_);
      if (callback_)
        callback_->Process(pending_data);
    }

    // Let the other end know which buffer we just filled.  The buffer index is
    // used to ensure the other end is getting the buffer it expects.  For more
    // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
    if (synchronized_buffers_) {
      ++buffer_index;
      size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index));
      if (bytes_sent != sizeof(buffer_index))
        break;
    }
  }
}

// AudioDeviceThread::Callback implementation

AudioDeviceThread::Callback::Callback(
    const AudioParameters& audio_parameters,
    base::SharedMemoryHandle memory,
    int memory_length,
    int total_segments)
    : audio_parameters_(audio_parameters),
      samples_per_ms_(audio_parameters.sample_rate() / 1000),
      bytes_per_ms_(audio_parameters.channels() *
                    (audio_parameters_.bits_per_sample() / 8) *
                    samples_per_ms_),
      shared_memory_(memory, false),
      memory_length_(memory_length),
      total_segments_(total_segments) {
  CHECK_NE(bytes_per_ms_, 0);  // Catch division by zero early.
  CHECK_NE(samples_per_ms_, 0);
  CHECK_GT(total_segments_, 0);
  CHECK_EQ(memory_length_ % total_segments_, 0);
  segment_length_ = memory_length_ / total_segments_;
}

AudioDeviceThread::Callback::~Callback() {}

void AudioDeviceThread::Callback::InitializeOnAudioThread() {
  MapSharedMemory();
  CHECK(shared_memory_.memory());
}

}  // namespace media.