// Copyright 2013 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/sounds/sounds_manager.h"

#include <vector>

#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "media/audio/audio_manager.h"
#include "media/audio/sounds/audio_stream_handler.h"

namespace media {

namespace {

SoundsManager* g_instance = NULL;
bool g_initialized_for_testing = false;

// SoundsManagerImpl ---------------------------------------------------

class SoundsManagerImpl : public SoundsManager {
 public:
  SoundsManagerImpl() {}
  ~SoundsManagerImpl() override { DCHECK(CalledOnValidThread()); }

  // SoundsManager implementation:
  bool Initialize(SoundKey key, const base::StringPiece& data) override;
  bool Play(SoundKey key) override;
  bool Stop(SoundKey key) override;
  base::TimeDelta GetDuration(SoundKey key) override;

 private:
  AudioStreamHandler* GetHandler(SoundKey key);

  // There's only a handful of sounds, so a vector is sufficient.
  struct StreamEntry {
    SoundKey key;
    std::unique_ptr<AudioStreamHandler> handler;
  };
  std::vector<StreamEntry> handlers_;

  DISALLOW_COPY_AND_ASSIGN(SoundsManagerImpl);
};

bool SoundsManagerImpl::Initialize(SoundKey key,
                                   const base::StringPiece& data) {
  if (AudioStreamHandler* handler = GetHandler(key)) {
    DCHECK(handler->IsInitialized());
    return true;
  }

  std::unique_ptr<AudioStreamHandler> handler(new AudioStreamHandler(data));
  if (!handler->IsInitialized()) {
    LOG(WARNING) << "Can't initialize AudioStreamHandler for key=" << key;
    return false;
  }

  handlers_.push_back({key, std::move(handler)});
  return true;
}

bool SoundsManagerImpl::Play(SoundKey key) {
  DCHECK(CalledOnValidThread());
  AudioStreamHandler* handler = GetHandler(key);
  return handler && handler->Play();
}

bool SoundsManagerImpl::Stop(SoundKey key) {
  DCHECK(CalledOnValidThread());
  AudioStreamHandler* handler = GetHandler(key);
  if (!handler)
    return false;
  handler->Stop();
  return true;
}

base::TimeDelta SoundsManagerImpl::GetDuration(SoundKey key) {
  DCHECK(CalledOnValidThread());
  AudioStreamHandler* handler = GetHandler(key);
  return !handler ? base::TimeDelta() : handler->duration();
}

AudioStreamHandler* SoundsManagerImpl::GetHandler(SoundKey key) {
  for (auto& entry : handlers_) {
    if (entry.key == key)
      return entry.handler.get();
  }
  return nullptr;
}

}  // namespace

SoundsManager::SoundsManager() {}

SoundsManager::~SoundsManager() { DCHECK(CalledOnValidThread()); }

// static
void SoundsManager::Create() {
  CHECK(!g_instance || g_initialized_for_testing)
      << "SoundsManager::Create() is called twice";
  if (g_initialized_for_testing)
    return;
  g_instance = new SoundsManagerImpl();
}

// static
void SoundsManager::Shutdown() {
  CHECK(g_instance) << "SoundsManager::Shutdown() is called "
                    << "without previous call to Create()";
  delete g_instance;
  g_instance = NULL;
}

// static
SoundsManager* SoundsManager::Get() {
  CHECK(g_instance) << "SoundsManager::Get() is called before Create()";
  return g_instance;
}

// static
void SoundsManager::InitializeForTesting(SoundsManager* manager) {
  CHECK(!g_instance) << "SoundsManager is already initialized.";
  CHECK(manager);
  g_instance = manager;
  g_initialized_for_testing = true;
}

}  // namespace media
