/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "base/basictypes.h"
#include "BluetoothHfpManager.h"
#include "BluetoothProfileController.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"

using namespace mozilla;
USING_BLUETOOTH_NAMESPACE

namespace {
  StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
  bool sInShutdown = false;
} // namespace

/**
 * nsIObserver function
 */
NS_IMETHODIMP
BluetoothHfpManager::Observe(nsISupports* aSubject,
                             const char* aTopic,
                             const char16_t* aData)
{
  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    HandleShutdown();
  } else {
    MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

/**
 * BluetoothProfileManagerBase functions
 */
void
BluetoothHfpManager::Connect(const BluetoothAddress& aDeviceAddress,
                             BluetoothProfileController* aController)
{
  MOZ_ASSERT(aController);

  aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
}

void
BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
{
  MOZ_ASSERT(aController);

  aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
}

bool
BluetoothHfpManager::IsConnected()
{
  return false;
}

void
BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
{
  MOZ_ASSERT(false);
}

void
BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr)
{
  MOZ_ASSERT(false);
}

void
BluetoothHfpManager::GetAddress(BluetoothAddress& aDeviceAddress)
{
  aDeviceAddress.Clear();
}

void
BluetoothHfpManager::OnGetServiceChannel(
  const BluetoothAddress& aDeviceAddress,
  const BluetoothUuid& aServiceUuid,
  int aChannel)
{
  MOZ_ASSERT(false);
}

void
BluetoothHfpManager::OnUpdateSdpRecords(const BluetoothAddress& aDeviceAddress)
{
  MOZ_ASSERT(false);
}

/**
 * BluetoothHfpManagerBase function
 */
bool
BluetoothHfpManager::IsScoConnected()
{
  return false;
}

bool
BluetoothHfpManager::IsNrecEnabled()
{
  return false;
}

/**
 * Non-inherited functions
 */
// static
BluetoothHfpManager*
BluetoothHfpManager::Get()
{
  MOZ_ASSERT(NS_IsMainThread());

  // If sBluetoothHfpManager already exists, exit early
  if (sBluetoothHfpManager) {
    return sBluetoothHfpManager;
  }

  // If we're in shutdown, don't create a new instance
  NS_ENSURE_FALSE(sInShutdown, nullptr);

  // Create a new instance and return
  BluetoothHfpManager* manager = new BluetoothHfpManager();
  NS_ENSURE_TRUE(manager->Init(), nullptr);

  sBluetoothHfpManager = manager;
  return sBluetoothHfpManager;
}

bool
BluetoothHfpManager::Init()
{
  MOZ_ASSERT(NS_IsMainThread());

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  NS_ENSURE_TRUE(obs, false);

  if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
    BT_WARNING("Failed to add observers!");
    return false;
  }

  return true;
}

// static
void
BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
{
  MOZ_ASSERT(NS_IsMainThread());

  /**
   * TODO:
   *   Implement InitHfpInterface() for applications that want to create SCO
   *   link without a HFP connection (e.g., VoIP).
   */

  if (aRes) {
    aRes->Init();
  }
}

// static
void
BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes)
{
  MOZ_ASSERT(NS_IsMainThread());

  sBluetoothHfpManager = nullptr;

  /**
   * TODO:
   *   Implement DeinitHfpInterface() for applications that want to create SCO
   *   link without a HFP connection (e.g., VoIP).
   */

  if (aRes) {
    aRes->Deinit();
  }
}

void
BluetoothHfpManager::HandleShutdown()
{
  MOZ_ASSERT(NS_IsMainThread());
  sInShutdown = true;
  sBluetoothHfpManager = nullptr;
}

bool
BluetoothHfpManager::ConnectSco()
{
  MOZ_ASSERT(NS_IsMainThread());

  /**
   * TODO:
   *   Implement ConnectSco() for applications that want to create SCO link
   *   without a HFP connection (e.g., VoIP).
   */
  return false;
}

void
BluetoothHfpManager::HandleBackendError()
{
  /**
   * TODO:
   *   Reset connection state and audio state to DISCONNECTED to handle backend
   *   error. The state change triggers UI status bar update as ordinary
   *   bluetooth turn-off sequence.
   */
}

bool
BluetoothHfpManager::DisconnectSco()
{
  MOZ_ASSERT(NS_IsMainThread());

  /**
   * TODO:
   *   Implement DisconnectSco() for applications that want to destroy SCO link
   *   without a HFP connection (e.g., VoIP).
   */
  return false;
}

void
BluetoothHfpManager::Reset()
{
  MOZ_ASSERT(NS_IsMainThread());
}

NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver)
