// Copyright 2015 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.

#ifndef DEVICE_BLUETOOTH_BLUETOOTH_AUDIO_SINK_BLUEZ_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_AUDIO_SINK_BLUEZ_H_

#include <stdint.h>
#include <string>
#include <vector>

#include "base/files/file.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/observer_list.h"
#include "dbus/file_descriptor.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_audio_sink.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/dbus/bluetooth_media_client.h"
#include "device/bluetooth/dbus/bluetooth_media_endpoint_service_provider.h"
#include "device/bluetooth/dbus/bluetooth_media_transport_client.h"

namespace bluez {

class BluetoothAudioSinkBlueZTest;

class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkBlueZ
    : public device::BluetoothAudioSink,
      public device::BluetoothAdapter::Observer,
      public bluez::BluetoothMediaClient::Observer,
      public bluez::BluetoothMediaTransportClient::Observer,
      public bluez::BluetoothMediaEndpointServiceProvider::Delegate,
      public base::MessageLoopForIO::Watcher {
 public:
  explicit BluetoothAudioSinkBlueZ(
      scoped_refptr<device::BluetoothAdapter> adapter);

  // device::BluetoothAudioSink overrides.
  // Unregisters a BluetoothAudioSink. |callback| should handle
  // the clean-up after the audio sink is deleted successfully, otherwise
  // |error_callback| will be called.
  void Unregister(
      const base::Closure& callback,
      const device::BluetoothAudioSink::ErrorCallback& error_callback) override;
  void AddObserver(BluetoothAudioSink::Observer* observer) override;
  void RemoveObserver(BluetoothAudioSink::Observer* observer) override;
  device::BluetoothAudioSink::State GetState() const override;
  uint16_t GetVolume() const override;

  // Registers a BluetoothAudioSink. User applications can use |options| to
  // configure the audio sink. |callback| will be executed if the audio sink is
  // successfully registered, otherwise |error_callback| will be called. Called
  // by BluetoothAdapterBlueZ.
  void Register(
      const device::BluetoothAudioSink::Options& options,
      const base::Closure& callback,
      const device::BluetoothAudioSink::ErrorCallback& error_callback);

  // Returns a pointer to the media endpoint object. This function should be
  // used for testing purpose only.
  bluez::BluetoothMediaEndpointServiceProvider* GetEndpointServiceProvider();

 private:
  ~BluetoothAudioSinkBlueZ() override;

  // device::BluetoothAdapter::Observer overrides.
  void AdapterPresentChanged(device::BluetoothAdapter* adapter,
                             bool present) override;
  void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
                             bool powered) override;

  // bluez::BluetoothMediaClient::Observer overrides.
  void MediaRemoved(const dbus::ObjectPath& object_path) override;

  // bluez::BluetoothMediaTransportClient::Observer overrides.
  void MediaTransportRemoved(const dbus::ObjectPath& object_path) override;
  void MediaTransportPropertyChanged(const dbus::ObjectPath& object_path,
                                     const std::string& property_name) override;

  // bluez::BluetoothMediaEndpointServiceProvider::Delegate overrides.
  void SetConfiguration(const dbus::ObjectPath& transport_path,
                        const TransportProperties& properties) override;
  void SelectConfiguration(
      const std::vector<uint8_t>& capabilities,
      const SelectConfigurationCallback& callback) override;
  void ClearConfiguration(const dbus::ObjectPath& transport_path) override;
  void Released() override;

  // base::MessageLoopForIO::Watcher overrides.
  void OnFileCanReadWithoutBlocking(int fd) override;
  void OnFileCanWriteWithoutBlocking(int fd) override;

  // Acquires file descriptor via current transport object when the state change
  // is triggered by MediaTransportPropertyChanged.
  void AcquireFD();

  // Watches if there is any available data from |fd_|.
  void WatchFD();

  // Stops watching |fd_| and resets |fd_|.
  void StopWatchingFD();

  // Reads from the file descriptor acquired via Media Transport object and
  // notify |observer_| while the audio data is available.
  void ReadFromFile();

  // Called when the state property of BluetoothMediaTransport has been updated.
  void StateChanged(device::BluetoothAudioSink::State state);

  // Called when the volume property of BluetoothMediaTransport has been
  // updated.
  void VolumeChanged(uint16_t volume);

  // Called when the registration of Media Endpoint has succeeded.
  void OnRegisterSucceeded(const base::Closure& callback);

  // Called when the registration of Media Endpoint failed.
  void OnRegisterFailed(
      const device::BluetoothAudioSink::ErrorCallback& error_callback,
      const std::string& error_name,
      const std::string& error_message);

  // Called when the unregistration of Media Endpoint has succeeded. The
  // clean-up of media, media transport and media endpoint will be handled here.
  void OnUnregisterSucceeded(const base::Closure& callback);

  // Called when the unregistration of Media Endpoint failed.
  void OnUnregisterFailed(
      const device::BluetoothAudioSink::ErrorCallback& error_callback,
      const std::string& error_name,
      const std::string& error_message);

  // Called when the file descriptor, read MTU and write MTU are retrieved
  // successfully using |transport_path_|.
  void OnAcquireSucceeded(dbus::FileDescriptor* fd,
                          const uint16_t read_mtu,
                          const uint16_t write_mtu);

  // Called when acquiring the file descriptor, read MTU and write MTU failed.
  void OnAcquireFailed(const std::string& error_name,
                       const std::string& error_message);

  // Called when the file descriptor is released successfully.
  void OnReleaseFDSucceeded();

  // Called when it failed to release file descriptor.
  void OnReleaseFDFailed(const std::string& error_name,
                         const std::string& error_message);

  // Helper functions to clean up media, media transport and media endpoint.
  // Called when the |state_| changes to either STATE_INVALID or
  // STATE_DISCONNECTED.
  void ResetMedia();
  void ResetTransport();
  void ResetEndpoint();

  // The connection state between the BluetoothAudioSinkBlueZ and the remote
  // device.
  device::BluetoothAudioSink::State state_;

  // The volume control by the remote device during the streaming. The valid
  // range of volume is 0-127, and 128 is used to represent invalid volume.
  uint16_t volume_;

  // Read MTU of the file descriptor acquired via Media Transport object.
  uint16_t read_mtu_;

  // Write MTU of the file descriptor acquired via Media Transport object.
  uint16_t write_mtu_;

  // Flag for logging the read failure in ReadFromFD.
  bool read_has_failed_;

  // The file which takes ownership of the file descriptor acquired via Media
  // Transport object.
  scoped_ptr<base::File> file_;

  // To avoid reallocation of memory, data will be updated only when |read_mtu_|
  // changes.
  scoped_ptr<char[]> data_;

  // File descriptor watcher for the file descriptor acquired via Media
  // Transport object.
  base::MessageLoopForIO::FileDescriptorWatcher fd_read_watcher_;

  // Object path of the media object being used.
  dbus::ObjectPath media_path_;

  // Object path of the transport object being used.
  dbus::ObjectPath transport_path_;

  // Object path of the media endpoint object being used.
  dbus::ObjectPath endpoint_path_;

  // BT adapter which the audio sink binds to. |adapter_| should outlive
  // a BluetoothAudioSinkBlueZ object.
  scoped_refptr<device::BluetoothAdapter> adapter_;

  // Options used to initiate Media Endpoint and select configuration for the
  // transport.
  device::BluetoothAudioSink::Options options_;

  // Media Endpoint object owned by the audio sink object.
  scoped_ptr<bluez::BluetoothMediaEndpointServiceProvider> media_endpoint_;

  // List of observers interested in event notifications from us. Objects in
  // |observers_| are expected to outlive a BluetoothAudioSinkBlueZ object.
  base::ObserverList<BluetoothAudioSink::Observer> observers_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<BluetoothAudioSinkBlueZ> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(BluetoothAudioSinkBlueZ);
};

}  // namespace bluez

#endif  // DEVICE_BLUETOOTH_BLUETOOTH_AUDIO_SINK_BLUEZ_H_
