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

#ifndef DEVICE_BLUETOOTH_BLUETOOTH_TASK_MANAGER_WIN_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_TASK_MANAGER_WIN_H_

#include <stdint.h>

#include <memory>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/win/scoped_handle.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/bluetooth_low_energy_win.h"

namespace base {

class SequencedTaskRunner;
class SequencedWorkerPool;

}  // namespace base

namespace device {

// Manages the blocking Bluetooth tasks using |SequencedWorkerPool|. It runs
// bluetooth tasks using |SequencedWorkerPool| and informs its observers of
// bluetooth adapter state changes and any other bluetooth device inquiry
// result.
//
// It delegates the blocking Windows API calls to |bluetooth_task_runner_|'s
// message loop, and receives responses via methods like OnAdapterStateChanged
// posted to UI thread.
class DEVICE_BLUETOOTH_EXPORT BluetoothTaskManagerWin
    : public base::RefCountedThreadSafe<BluetoothTaskManagerWin> {
 public:
  struct DEVICE_BLUETOOTH_EXPORT AdapterState {
    AdapterState();
    ~AdapterState();
    std::string name;
    std::string address;
    bool powered;
  };

  struct DEVICE_BLUETOOTH_EXPORT ServiceRecordState {
    ServiceRecordState();
    ~ServiceRecordState();
    // Properties common to Bluetooth Classic and LE devices.
    std::string name;
    // Properties specific to Bluetooth Classic devices.
    std::vector<uint8_t> sdp_bytes;
    // Properties specific to Bluetooth LE devices.
    BluetoothUUID gatt_uuid;
    uint16_t attribute_handle;
    // GATT service device path.
    // Note: Operation of the included characteristics and descriptors of this
    // service must use service device path instead of resident device device
    // path.
    base::FilePath path;
  };

  struct DEVICE_BLUETOOTH_EXPORT DeviceState {
    DeviceState();
    ~DeviceState();

    bool is_bluetooth_classic() const { return path.empty(); }

    // Properties common to Bluetooth Classic and LE devices.
    std::string address;  // This uniquely identifies the device.
    base::Optional<std::string> name;  // Friendly name
    bool visible;
    bool connected;
    bool authenticated;
    ScopedVector<ServiceRecordState> service_record_states;
    // Properties specific to Bluetooth Classic devices.
    uint32_t bluetooth_class;
    // Properties specific to Bluetooth LE devices.
    base::FilePath path;
  };

  class DEVICE_BLUETOOTH_EXPORT Observer {
   public:
     virtual ~Observer() {}

     virtual void AdapterStateChanged(const AdapterState& state) {}
     virtual void DiscoveryStarted(bool success) {}
     virtual void DiscoveryStopped() {}
     // Called when the adapter has just been polled for the list of *all* known
     // devices. This includes devices previously paired, devices paired using
     // the underlying Operating System UI, and devices discovered recently due
     // to an active discovery session. Note that for a given device (address),
     // the associated state can change over time. For example, during a
     // discovery session, the "friendly" name may initially be "unknown" before
     // the actual name is retrieved in subsequent poll events.
     virtual void DevicesPolled(const ScopedVector<DeviceState>& devices) {}
  };

  explicit BluetoothTaskManagerWin(
      scoped_refptr<base::SequencedTaskRunner> ui_task_runner);

  static BluetoothUUID BluetoothLowEnergyUuidToBluetoothUuid(
      const BTH_LE_UUID& bth_le_uuid);

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  void Initialize();
  void InitializeWithBluetoothTaskRunner(
      scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner);
  void Shutdown();

  void PostSetPoweredBluetoothTask(
      bool powered,
      const base::Closure& callback,
      const BluetoothAdapter::ErrorCallback& error_callback);
  void PostStartDiscoveryTask();
  void PostStopDiscoveryTask();

  // Callbacks of asynchronous operations of GATT service.
  typedef base::Callback<void(HRESULT)> HResultCallback;
  typedef base::Callback<
      void(std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC>, uint16_t, HRESULT)>
      GetGattIncludedCharacteristicsCallback;
  typedef base::Callback<
      void(std::unique_ptr<BTH_LE_GATT_DESCRIPTOR>, uint16_t, HRESULT)>
      GetGattIncludedDescriptorsCallback;
  typedef base::Callback<void(std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC_VALUE>,
                              HRESULT)>
      ReadGattCharacteristicValueCallback;
  typedef base::Callback<void(std::unique_ptr<std::vector<uint8_t>>)>
      GattCharacteristicValueChangedCallback;
  typedef base::Callback<void(PVOID, HRESULT)> GattEventRegistrationCallback;

  // Get all included characteristics of a given service. The service is
  // uniquely identified by its |uuid| and |attribute_handle| with service
  // device |service_path|. The result is returned asynchronously through
  // |callback|.
  void PostGetGattIncludedCharacteristics(
      const base::FilePath& service_path,
      const BluetoothUUID& uuid,
      uint16_t attribute_handle,
      const GetGattIncludedCharacteristicsCallback& callback);

  // Get all included descriptors of a given |characterisitc| in service
  // with |service_path|. The result is returned asynchronously through
  // |callback|.
  void PostGetGattIncludedDescriptors(
      const base::FilePath& service_path,
      const PBTH_LE_GATT_CHARACTERISTIC characteristic,
      const GetGattIncludedDescriptorsCallback& callback);

  // Post read the value of a given |characteristic| in service with
  // |service_path|. The result is returned asynchronously through |callback|.
  void PostReadGattCharacteristicValue(
      const base::FilePath& device_path,
      const PBTH_LE_GATT_CHARACTERISTIC characteristic,
      const ReadGattCharacteristicValueCallback& callback);

  // Post write the value of a given |characteristic| in service with
  // |service_path| to |new_value|. The operation result is returned
  // asynchronously through |callback|.
  void PostWriteGattCharacteristicValue(
      const base::FilePath& service_path,
      const PBTH_LE_GATT_CHARACTERISTIC characteristic,
      const std::vector<uint8_t>& new_value,
      const HResultCallback& callback);

  // Post a task to register to receive value changed notifications from
  // |characteristic| in service with |service_path|. |ccc_descriptor| is the
  // Client Characteristic Configuration descriptor. |registered_callback| is
  // the function to be invoked if the event occured. The operation result is
  // returned asynchronously through |callback|.
  void PostRegisterGattCharacteristicValueChangedEvent(
      const base::FilePath& service_path,
      const PBTH_LE_GATT_CHARACTERISTIC characteristic,
      const PBTH_LE_GATT_DESCRIPTOR ccc_descriptor,
      const GattEventRegistrationCallback& callback,
      const GattCharacteristicValueChangedCallback& registered_callback);

  // Post a task to unregister from value change notifications. |event_handle|
  // was returned by PostRegisterGattCharacteristicValueChangedEvent.
  void PostUnregisterGattCharacteristicValueChangedEvent(PVOID event_handle);

 private:
  friend class base::RefCountedThreadSafe<BluetoothTaskManagerWin>;
  friend class BluetoothTaskManagerWinTest;

  static const int kPollIntervalMs;

  virtual ~BluetoothTaskManagerWin();

  // Logs Win32 errors occurring during polling on the worker thread. The method
  // may discard messages to avoid logging being too verbose.
  void LogPollingError(const char* message, int win32_error);

  // Notify all Observers of updated AdapterState. Should only be called on the
  // UI thread.
  void OnAdapterStateChanged(const AdapterState* state);
  void OnDiscoveryStarted(bool success);
  void OnDiscoveryStopped();
  void OnDevicesPolled(const ScopedVector<DeviceState>* devices);

  // Called on BluetoothTaskRunner.
  void StartPolling();
  void PollAdapter();
  void PostAdapterStateToUi();
  void SetPowered(bool powered,
                  const base::Closure& callback,
                  const BluetoothAdapter::ErrorCallback& error_callback);

  // Starts discovery. Once the discovery starts, it issues a discovery inquiry
  // with a short timeout, then issues more inquiries with greater timeout
  // values. The discovery finishes when StopDiscovery() is called or timeout
  // has reached its maximum value.
  void StartDiscovery();
  void StopDiscovery();

  // Issues a device inquiry that runs for |timeout_multiplier| * 1.28 seconds.
  // This posts itself again with |timeout_multiplier| + 1 until
  // |timeout_multiplier| reaches the maximum value or stop discovery call is
  // received.
  void DiscoverDevices(int timeout_multiplier);

  // Fetch already known device information. Similar to |StartDiscovery|, except
  // this function does not issue a discovery inquiry. Instead it gets the
  // device info cached in the adapter.
  void GetKnownDevices();

  // Looks for Bluetooth Classic and Low Energy devices, as well as the services
  // exposed by those devices.
  bool SearchDevices(int timeout_multiplier,
                     bool search_cached_devices_only,
                     ScopedVector<DeviceState>* device_list);

  // Sends a device search API call to the adapter to look for Bluetooth Classic
  // devices.
  bool SearchClassicDevices(int timeout_multiplier,
                            bool search_cached_devices_only,
                            ScopedVector<DeviceState>* device_list);

  // Enumerate Bluetooth Low Energy devices.
  bool SearchLowEnergyDevices(ScopedVector<DeviceState>* device_list);

  // Discover services for the devices in |device_list|.
  bool DiscoverServices(ScopedVector<DeviceState>* device_list,
                        bool search_cached_services_only);

  // Discover Bluetooth Classic services for the given |device_address|.
  bool DiscoverClassicDeviceServices(
      const std::string& device_address,
      const GUID& protocol_uuid,
      bool search_cached_services_only,
      ScopedVector<ServiceRecordState>* service_record_states);

  // Discover Bluetooth Classic services for the given |device_address|.
  // Returns a Win32 error code.
  int DiscoverClassicDeviceServicesWorker(
      const std::string& device_address,
      const GUID& protocol_uuid,
      bool search_cached_services_only,
      ScopedVector<ServiceRecordState>* service_record_states);

  // Discover Bluetooth Low Energy services for the given |device_path|.
  bool DiscoverLowEnergyDeviceServices(
      const base::FilePath& device_path,
      ScopedVector<ServiceRecordState>* service_record_states);

  // Search for device paths of the GATT services in |*service_record_states|
  // from |device_address|.
  bool SearchForGattServiceDevicePaths(
      const std::string device_address,
      ScopedVector<ServiceRecordState>* service_record_states);

  // GATT service related functions.
  void GetGattIncludedCharacteristics(
      base::FilePath device_path,
      BluetoothUUID uuid,
      uint16_t attribute_handle,
      const GetGattIncludedCharacteristicsCallback& callback);
  void GetGattIncludedDescriptors(
      base::FilePath service_path,
      BTH_LE_GATT_CHARACTERISTIC characteristic,
      const GetGattIncludedDescriptorsCallback& callback);
  void ReadGattCharacteristicValue(
      base::FilePath device_path,
      BTH_LE_GATT_CHARACTERISTIC characteristic,
      const ReadGattCharacteristicValueCallback& callback);
  void WriteGattCharacteristicValue(base::FilePath service_path,
                                    BTH_LE_GATT_CHARACTERISTIC characteristic,
                                    std::vector<uint8_t> new_value,
                                    const HResultCallback& callback);
  void RegisterGattCharacteristicValueChangedEvent(
      base::FilePath service_path,
      BTH_LE_GATT_CHARACTERISTIC characteristic,
      BTH_LE_GATT_DESCRIPTOR ccc_descriptor,
      const GattEventRegistrationCallback& callback,
      const GattCharacteristicValueChangedCallback& registered_callback);
  void UnregisterGattCharacteristicValueChangedEvent(PVOID event_handle);

  // UI task runner reference.
  scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;

  scoped_refptr<base::SequencedWorkerPool> worker_pool_;
  scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner_;

  // List of observers interested in event notifications.
  base::ObserverList<Observer> observers_;

  // Weak reference of the adapter handle, let BluetoothClassicWrapper handle
  // the close of |adapter_handle_|.
  HANDLE adapter_handle_;

  // indicates whether the adapter is in discovery mode or not.
  bool discovering_;

  // Use for discarding too many log messages.
  base::TimeTicks current_logging_batch_ticks_;
  int current_logging_batch_count_;

  DISALLOW_COPY_AND_ASSIGN(BluetoothTaskManagerWin);
};

}  // namespace device

#endif  // DEVICE_BLUETOOTH_BLUETOOTH_TASK_MANAGER_WIN_H_
