/*
 * Copyright 2018 The Android Open Source Project
 *
 * 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 <base/bind.h>
#include <base/memory/weak_ptr.h>
#include <map>
#include <memory>

#include "avrcp_internal.h"
#include "packet/avrcp/avrcp_packet.h"
#include "packet/base/packet.h"
#include "profile/avrcp/device.h"
#include "raw_address.h"

namespace bluetooth {
namespace avrcp {

// TODO: Remove the singleton design structure for this class.
// AvrcpTargetService is already a singleton and can manage the lifetime of this
// object. multiple singleton objects can lead to code that is hard to test and
// have hard to debug lifetimes.

// TODO (apanicke): Use a device factory instead of just the constructor in
// order to create device objects. This will allow us to create specific device
// classes that can provide interop fixes for certain badly behaving devices.

/**
 * ConnectionHandler handles SDP, connecting to remote AVRCP devices
 * and multiplexing/delivering messages to devices.
 */
class ConnectionHandler {
 public:
  /**
   * This callback is used to return a new device after a connection attempt.
   * A reference to the new Avrcp device is located in the shared_ptr.
   * If there was an issue during connection the pointer value will be null.
   */
  using ConnectionCallback = base::Callback<void(std::shared_ptr<Device>)>;

  /**
   * Initializes the singleton instance and sets up SDP. Also Opens the
   * AVRCP Acceptor to receive connection requests from a remote device.
   *
   * Params:
   * callback - A callback that gets called any time a new AVRCP Device
   *            is connected. Will return nullpointer if a device fails
   *            to connect via ConnectDevice();
   *
   * TODO: Add message loop to determine which thread events are posted to
   */
  static bool Initialize(const ConnectionCallback& callback,
                         AvrcpInterface* avrcp, SdpInterface* sdp,
                         VolumeInterface* vol);

  /**
   * Clears the singleton and tears down SDP
   */
  static bool CleanUp();

  /**
   * Get the singleton instance of Connection Handler
   */
  static ConnectionHandler* Get();

  /**
   * Attempt to connect AVRCP on a device. The callback will be called with
   * either a smart pointer pointing to the connected AVRCP device or null
   * if the connection failed.
   *
   * The order of operations for this function is as follows.
   *   1. Perform SDP on remote device
   *   2. Connect the AVCTP Channel
   *   2. (Optional) If supported connect the AVCTP Browse channel
   *   4. Call the provided callback with the new
   *
   * Params:
   * bdaddr - Bluetooth address of device to connect to
   * callback - The function that gets called when a connection succeeds or
   *            fails. The pointer being cleared implies that the connection
   *            failed.
   *
   * Returns:
   * true if the connection attempt starts, false if there are no resources to
   * connect AVRCP
   */
  virtual bool ConnectDevice(const RawAddress& bdaddr);

  /**
   * Disconnects AVRCP from a device that was successfully connected too using
   * ConnectionHandler::ConnectDevice
   *
   * Returns:
   * true if the AVRCP was successfully disconnected for the device or false
   * if the device was already disconnected or in an invalid state
   */
  virtual bool DisconnectDevice(const RawAddress& bdaddr);

  virtual std::vector<std::shared_ptr<Device>> GetListOfDevices() const;

  /**
   * Provide a custom ConnectionHandler that will be returned by Get().
   * Initialize and CleanUp should not be called as the owner of the handler
   * determines its lifetime.
   */
  static void InitForTesting(ConnectionHandler* handler);

 private:
  AvrcpInterface* avrc_;
  SdpInterface* sdp_;
  VolumeInterface* vol_;

  ConnectionCallback connection_cb_;

  std::map<uint8_t, std::shared_ptr<Device>> device_map_;
  // TODO (apanicke): Replace the features with a class that has individual
  // fields.
  std::map<RawAddress, uint16_t> feature_map_;

  static ConnectionHandler* instance_;

  using SdpCallback = base::Callback<void(uint16_t status, uint16_t version,
                                          uint16_t features)>;
  virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb);
  void SdpCb(const RawAddress& bdaddr, SdpCallback cb,
             tSDP_DISCOVERY_DB* disc_db, uint16_t status);

  virtual bool AvrcpConnect(bool initiator, const RawAddress& bdaddr);

  // Callbacks when connecting to a device
  void InitiatorControlCb(uint8_t handle, uint8_t event, uint16_t result,
                          const RawAddress* peer_addr);
  void AcceptorControlCb(uint8_t handle, uint8_t event, uint16_t result,
                         const RawAddress* peer_addr);
  void MessageCb(uint8_t handle, uint8_t label, uint8_t opcode,
                 tAVRC_MSG* p_msg);

  ConnectionHandler() : weak_ptr_factory_(this){};
  virtual ~ConnectionHandler() = default;

  // Callback for when sending a response to a device
  void SendMessage(uint8_t handle, uint8_t label, bool browse,
                   std::unique_ptr<::bluetooth::PacketBuilder> message);

  base::WeakPtrFactory<ConnectionHandler> weak_ptr_factory_;
  DISALLOW_COPY_AND_ASSIGN(ConnectionHandler);
};

}  // namespace avrcp
}  // namespace bluetooth