//
//  Copyright (C) 2015 Google, Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at:
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

#pragma once

#include <mutex>
#include <unordered_map>

#include <base/macros.h>
#include <base/memory/ref_counted.h>
#include <base/memory/weak_ptr.h>
#include <base/single_thread_task_runner.h>

#include <android/bluetooth/BnBluetoothGattServerCallback.h>
#include <android/bluetooth/IBluetooth.h>

using android::binder::Status;
using android::String16;

namespace heart_rate {

// Implements an example GATT Heart Rate service. This class emulates the
// behavior of a heart rate service by sending fake heart-rate pulses.
class HeartRateServer
    : public android::bluetooth::BnBluetoothGattServerCallback {
 public:
  HeartRateServer(android::sp<android::bluetooth::IBluetooth> bluetooth,
                  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
                  bool advertise);
  ~HeartRateServer() override;

  // Set up the server and register the GATT services with the stack. This
  // initiates a set of asynchronous procedures. Invokes |callback|
  // asynchronously with the result of the operation.
  using RunCallback = std::function<void(bool success)>;
  bool Run(const RunCallback& callback);

 private:
  // Helpers for posting heart rate measurement notifications.
  void ScheduleNextMeasurement();
  void SendHeartRateMeasurement();
  void BuildHeartRateMeasurementValue(std::vector<uint8_t>* out_value);

  // ipc::binder::IBluetoothGattServerCallback override:
  Status OnServerRegistered(int status, int server_id) override;
  Status OnServiceAdded(
      int status,
      const android::bluetooth::BluetoothGattService& service) override;
  Status OnCharacteristicReadRequest(const String16& device_address,
                                     int request_id, int offset, bool is_long,
                                     int handle) override;
  Status OnDescriptorReadRequest(const String16& device_address, int request_id,
                                 int offset, bool is_long, int handle) override;
  Status OnCharacteristicWriteRequest(const String16& device_address,
                                      int request_id, int offset,
                                      bool is_prepare_write, bool need_response,
                                      const std::vector<uint8_t>& value,
                                      int handle) override;
  Status OnDescriptorWriteRequest(const String16& device_address,
                                  int request_id, int offset,
                                  bool is_prepare_write, bool need_response,
                                  const std::vector<uint8_t>& value,
                                  int handle) override;
  Status OnExecuteWriteRequest(const String16& device_address, int request_id,
                               bool is_execute) override;
  Status OnNotificationSent(const String16& device_address,
                            int status) override;
  Status OnConnectionStateChanged(const String16& device_address,
                                  bool connected) override;

  // Single mutex to protect all variables below.
  std::mutex mutex_;

  // This stores whether or not at least one remote device has written to the
  // CCC descriptor.
  bool simulation_started_;

  // The IBluetooth and IBluetoothGattServer binders that we use to communicate
  // with the Bluetooth daemon's GATT server features.
  android::sp<android::bluetooth::IBluetooth> bluetooth_;
  android::sp<android::bluetooth::IBluetoothGattServer> gatt_;

  // ID assigned to us by the daemon to operate on our dedicated GATT server
  // instance.
  int server_if_;

  // Callback passed to Run(). We use this to tell main that all attributes have
  // been registered with the daemon.
  RunCallback pending_run_cb_;

  // Stores whether or not an outgoing notification is still pending. We use
  // this to throttle notifications so that we don't accidentally congest the
  // connection.
  std::unordered_map<std::string, bool> pending_notification_map_;

  // The current HR notification count.
  int hr_notification_count_;

  // The Energy Expended value we use in our notifications.
  uint16_t energy_expended_;

  // Handles that refer to Heart Rate Service GATT objects.
  // These returned to us from the Bluetooth daemon as we populate the database.
  uint16_t hr_service_handle_;
  uint16_t hr_measurement_handle_;
  uint16_t hr_measurement_cccd_handle_;
  uint16_t body_sensor_loc_handle_;
  uint16_t hr_control_point_handle_;

  // The daemon itself doesn't maintain a Client Characteristic Configuration
  // mapping, so we do it ourselves here.
  std::unordered_map<std::string, uint8_t> device_ccc_map_;

  // Wether we should also start advertising
  bool advertise_;

  // libchrome task runner that we use to post heart rate measurement
  // notifications on the main thread.
  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;

  // We use this to pass weak_ptr's to base::Bind, which won't execute if the
  // HeartRateServer object gets deleted. This is a convenience utility from
  // libchrome and we use it here since base::TaskRunner uses base::Callback.
  // Note: This should remain the last member so that it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<HeartRateServer> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(HeartRateServer);
};

}  // namespace heart_rate
