// Copyright 2014 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 "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"

#include "base/bind.h"
#include "base/logging.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"

namespace chromeos {

BluetoothGattNotifySessionChromeOS::BluetoothGattNotifySessionChromeOS(
    scoped_refptr<device::BluetoothAdapter> adapter,
    const std::string& device_address,
    const std::string& service_identifier,
    const std::string& characteristic_identifier,
    const dbus::ObjectPath& characteristic_path)
    : active_(true),
      adapter_(adapter),
      device_address_(device_address),
      service_id_(service_identifier),
      characteristic_id_(characteristic_identifier),
      object_path_(characteristic_path) {
  DCHECK(adapter_.get());
  DCHECK(!device_address_.empty());
  DCHECK(!service_id_.empty());
  DCHECK(!characteristic_id_.empty());
  DCHECK(object_path_.IsValid());

  DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->AddObserver(
      this);
}

BluetoothGattNotifySessionChromeOS::~BluetoothGattNotifySessionChromeOS() {
  DBusThreadManager::Get()
      ->GetBluetoothGattCharacteristicClient()
      ->RemoveObserver(this);
  Stop(base::Bind(&base::DoNothing));
}

std::string BluetoothGattNotifySessionChromeOS::GetCharacteristicIdentifier()
    const {
  return characteristic_id_;
}

bool BluetoothGattNotifySessionChromeOS::IsActive() {
  // Determine if the session is active. If |active_| is false, then it's
  // been explicitly marked, so return false.
  if (!active_)
    return false;

  // The fact that |active_| is true doesn't mean that the session is
  // actually active, since the characteristic might have stopped sending
  // notifications yet this method was called before we processed the
  // observer event (e.g. because somebody else called this method in their
  // BluetoothGattCharacteristicClient::Observer implementation, which was
  // called before ours). Check the client to see if notifications are still
  // being sent.
  BluetoothGattCharacteristicClient::Properties* properties =
      DBusThreadManager::Get()
          ->GetBluetoothGattCharacteristicClient()
          ->GetProperties(object_path_);
  if (!properties || !properties->notifying.value())
    active_ = false;

  return active_;
}

void BluetoothGattNotifySessionChromeOS::Stop(const base::Closure& callback) {
  if (!active_) {
    VLOG(1) << "Notify session already inactive.";
    callback.Run();
    return;
  }

  // Mark this session as inactive no matter what.
  active_ = false;

  device::BluetoothDevice* device = adapter_->GetDevice(device_address_);
  if (!device)
    return;

  device::BluetoothGattService* service = device->GetGattService(service_id_);
  if (!service)
    return;

  BluetoothRemoteGattCharacteristicChromeOS* chrc =
      static_cast<BluetoothRemoteGattCharacteristicChromeOS*>(
          service->GetCharacteristic(characteristic_id_));
  if (!chrc)
    return;

  chrc->RemoveNotifySession(callback);
}

void BluetoothGattNotifySessionChromeOS::GattCharacteristicRemoved(
    const dbus::ObjectPath& object_path) {
  if (object_path != object_path_)
    return;

  active_ = false;
}

void BluetoothGattNotifySessionChromeOS::GattCharacteristicPropertyChanged(
    const dbus::ObjectPath& object_path,
    const std::string& property_name) {
  if (object_path != object_path_)
    return;

  if (!active_)
    return;

  BluetoothGattCharacteristicClient::Properties* properties =
      DBusThreadManager::Get()
          ->GetBluetoothGattCharacteristicClient()
          ->GetProperties(object_path_);
  if (!properties) {
    active_ = false;
    return;
  }

  if (property_name == properties->notifying.name() &&
      !properties->notifying.value())
    active_ = false;
}

}  // namespace chromeos