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

#include <cstdint>
#include <functional>
#include <memory>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>

#include "hci/address.h"
#include "model/controller/controller_properties.h"
#include "model/controller/link_layer_controller.h"
#include "model/controller/vendor_commands/csr.h"
#include "model/devices/device.h"
#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"
#include "phy.h"

namespace rootcanal {

using ::bluetooth::hci::Address;
using ::bluetooth::hci::CommandView;

// List of reject reasons for invalid packets.
enum InvalidPacketReason {
  kUnknown = 0,
  kParseError = 1,
  kUnsupported = 2,
};

// Emulates a dual mode BR/EDR + LE controller by maintaining the link layer
// state machine detailed in the Bluetooth Core Specification Version 4.2,
// Volume 6, Part B, Section 1.1 (page 30). Provides methods corresponding to
// commands sent by the HCI. These methods will be registered as callbacks from
// a controller instance with the HciHandler. To implement a new Bluetooth
// command, simply add the method declaration below, with return type void and a
// single const std::vector<uint8_t>& argument. After implementing the
// method, simply register it with the HciHandler using the SET_HANDLER macro in
// the controller's default constructor. Be sure to name your method after the
// corresponding Bluetooth command in the Core Specification with the prefix
// "Hci" to distinguish it as a controller command.
class DualModeController : public Device {
public:
  DualModeController(ControllerProperties properties = ControllerProperties());
  DualModeController(DualModeController&&) = delete;
  DualModeController(const DualModeController&) = delete;
  ~DualModeController() = default;

  DualModeController& operator=(const DualModeController&) = delete;

  // Overwrite the configuration.
  void SetProperties(ControllerProperties properties);

  // Device methods.
  std::string GetTypeString() const override;

  void ReceiveLinkLayerPacket(model::packets::LinkLayerPacketView incoming, Phy::Type type,
                              int8_t rssi) override;

  void Tick() override;
  void Close() override;

  // Route commands and data from the stack.
  void HandleAcl(std::shared_ptr<std::vector<uint8_t>> acl_packet);
  void HandleCommand(std::shared_ptr<std::vector<uint8_t>> command_packet);
  void HandleSco(std::shared_ptr<std::vector<uint8_t>> sco_packet);
  void HandleIso(std::shared_ptr<std::vector<uint8_t>> iso_packet);

  /// Report invalid packets received for this controller instance
  /// to an external tracker. Packets are rejected if they failed to
  /// be parsed, or run into an unimplemented part of the controller.
  void RegisterInvalidPacketHandler(
          const std::function<void(uint32_t, InvalidPacketReason, std::string,
                                   std::vector<uint8_t> const&)>& handler);

  // Set the callbacks for sending packets to the HCI.
  void RegisterEventChannel(
          const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_event);

  void RegisterAclChannel(
          const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_acl);

  void RegisterScoChannel(
          const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);

  void RegisterIsoChannel(
          const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_iso);

  // Controller commands. For error codes, see the Bluetooth Core Specification,
  // Version 4.2, Volume 2, Part D (page 370).

  // Link Control Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1

  // 7.1.1
  void Inquiry(CommandView command);

  // 7.1.2
  void InquiryCancel(CommandView command);

  // 7.1.5
  void CreateConnection(CommandView command);

  // 7.1.6
  void Disconnect(CommandView command);

  // Deprecated
  void AddScoConnection(CommandView command);

  // 7.1.7
  void CreateConnectionCancel(CommandView command);

  // 7.1.8
  void AcceptConnectionRequest(CommandView command);

  // 7.1.9
  void RejectConnectionRequest(CommandView command);

  // 7.1.14
  void ChangeConnectionPacketType(CommandView command);

  // 7.1.17
  void ChangeConnectionLinkKey(CommandView command);

  // 7.1.18
  void CentralLinkKey(CommandView command);

  // 7.1.19
  void RemoteNameRequest(CommandView command);

  // 7.1.21
  void ReadRemoteSupportedFeatures(CommandView command);

  // 7.1.22
  void ReadRemoteExtendedFeatures(CommandView command);

  // 7.1.23
  void ReadRemoteVersionInformation(CommandView command);

  // 7.1.24
  void ReadClockOffset(CommandView command);

  // 7.1.26
  void SetupSynchronousConnection(CommandView command);

  // 7.1.27
  void AcceptSynchronousConnection(CommandView command);

  // 7.1.28
  void RejectSynchronousConnection(CommandView command);

  // 7.1.45
  void EnhancedSetupSynchronousConnection(CommandView command);

  // 7.1.46
  void EnhancedAcceptSynchronousConnection(CommandView command);

  // Link Policy Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.2

  // 7.2.1
  void HoldMode(CommandView command);

  // 7.2.2
  void SniffMode(CommandView command);

  // 7.2.3
  void ExitSniffMode(CommandView command);

  // 7.2.6
  void QosSetup(CommandView command);

  // 7.2.7
  void RoleDiscovery(CommandView command);

  // 7.2.8
  void SwitchRole(CommandView command);

  // 7.2.9
  void ReadLinkPolicySettings(CommandView command);

  // 7.2.10
  void WriteLinkPolicySettings(CommandView command);

  // 7.2.11
  void ReadDefaultLinkPolicySettings(CommandView command);

  // 7.2.12
  void WriteDefaultLinkPolicySettings(CommandView command);

  // 7.2.13
  void FlowSpecification(CommandView command);

  // 7.2.14
  void SniffSubrating(CommandView command);

  // Link Controller Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.3

  // 7.3.1
  void SetEventMask(CommandView command);

  // 7.3.2
  void Reset(CommandView command);

  // 7.3.3
  void SetEventFilter(CommandView command);

  // 7.3.10
  void DeleteStoredLinkKey(CommandView command);

  // 7.3.11
  void WriteLocalName(CommandView command);

  // 7.3.12
  void ReadLocalName(CommandView command);

  // 7.3.13 - 7.3.14
  void ReadConnectionAcceptTimeout(CommandView command);
  void WriteConnectionAcceptTimeout(CommandView command);

  // 7.3.15 - 7.3.16
  void ReadPageTimeout(CommandView command);
  void WritePageTimeout(CommandView command);

  // 7.3.17 - 7.3.18
  void ReadScanEnable(CommandView command);
  void WriteScanEnable(CommandView command);

  // 7.3.19 - 7.3.20
  void ReadPageScanActivity(CommandView command);
  void WritePageScanActivity(CommandView command);

  // 7.3.21 - 7.3.22
  void ReadInquiryScanActivity(CommandView command);
  void WriteInquiryScanActivity(CommandView command);

  // 7.3.23 - 7.3.24
  void ReadAuthenticationEnable(CommandView command);
  void WriteAuthenticationEnable(CommandView command);

  // 7.3.25 - 7.3.26
  void ReadClassOfDevice(CommandView command);
  void WriteClassOfDevice(CommandView command);

  // 7.3.27 - 7.3.28
  void ReadVoiceSetting(CommandView command);
  void WriteVoiceSetting(CommandView command);

  // 7.3.35
  void ReadTransmitPowerLevel(CommandView command);

  // 7.3.36 - 7.3.37
  void ReadSynchronousFlowControlEnable(CommandView command);
  void WriteSynchronousFlowControlEnable(CommandView command);

  // 7.3.39
  void HostBufferSize(CommandView command);

  // 7.3.42
  void WriteLinkSupervisionTimeout(CommandView command);

  // 7.3.43
  void ReadNumberOfSupportedIac(CommandView command);

  // 7.3.44 - 7.3.45
  void ReadCurrentIacLap(CommandView command);
  void WriteCurrentIacLap(CommandView command);

  // 7.3.47
  void ReadInquiryScanType(CommandView command);

  // 7.3.48
  void WriteInquiryScanType(CommandView command);

  // 7.3.49
  void ReadInquiryMode(CommandView command);

  // 7.3.50
  void WriteInquiryMode(CommandView command);

  // 7.3.52
  void ReadPageScanType(CommandView command);

  // 7.3.52
  void WritePageScanType(CommandView command);

  // 7.3.56
  void WriteExtendedInquiryResponse(CommandView command);

  // 7.3.57
  void RefreshEncryptionKey(CommandView command);

  // 7.3.59
  void WriteSimplePairingMode(CommandView command);

  // 7.3.60
  void ReadLocalOobData(CommandView command);

  // 7.3.61
  void ReadInquiryResponseTransmitPowerLevel(CommandView command);

  // 7.3.66
  void EnhancedFlush(CommandView command);

  // 7.3.69
  void SetEventMaskPage2(CommandView command);

  // 7.3.74
  void ReadEnhancedTransmitPowerLevel(CommandView command);

  // 7.3.79
  void WriteLeHostSupport(CommandView command);

  // 7.3.92
  void WriteSecureConnectionsHostSupport(CommandView command);

  // 7.3.95
  void ReadLocalOobExtendedData(CommandView command);

  // Informational Parameters Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.4

  // 7.4.5
  void ReadBufferSize(CommandView command);

  // 7.4.1
  void ReadLocalVersionInformation(CommandView command);

  // 7.4.6
  void ReadBdAddr(CommandView command);

  // 7.4.2
  void ReadLocalSupportedCommands(CommandView command);

  // 7.4.3
  void ReadLocalSupportedFeatures(CommandView command);

  // 7.4.4
  void ReadLocalExtendedFeatures(CommandView command);

  // 7.4.8
  void ReadLocalSupportedCodecsV1(CommandView command);

  // Status Parameters Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.5

  // 7.5.1 - 7.5.2
  void ReadFailedContactCounter(CommandView command);
  void ResetFailedContactCounter(CommandView command);

  // 7.5.4
  void ReadRssi(CommandView command);

  // 7.5.7
  void ReadEncryptionKeySize(CommandView command);

  // Test Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.7

  // 7.7.1
  void ReadLoopbackMode(CommandView command);

  // 7.7.2
  void WriteLoopbackMode(CommandView command);

  // LE Controller Commands
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.8

  // 7.8.1
  void LeSetEventMask(CommandView command);

  // 7.8.2 - 7.8.93
  void LeReadBufferSizeV1(CommandView command);
  void LeReadBufferSizeV2(CommandView command);

  // 7.8.3
  void LeReadLocalSupportedFeaturesPage0(CommandView command);

  // 7.8.4
  void LeSetRandomAddress(CommandView command);

  // 7.8.5 - 7.8.9
  void LeSetAdvertisingParameters(CommandView command);
  void LeReadAdvertisingPhysicalChannelTxPower(CommandView command);
  void LeSetAdvertisingData(CommandView command);
  void LeSetScanResponseData(CommandView command);
  void LeSetAdvertisingEnable(CommandView command);

  // 7.8.10 - 7.8.11
  void LeSetScanParameters(CommandView command);
  void LeSetScanEnable(CommandView command);

  // 7.8.12 - 7.8.13
  void LeCreateConnection(CommandView command);
  void LeCreateConnectionCancel(CommandView command);

  // 7.8.14 - 7.8.17
  void LeReadFilterAcceptListSize(CommandView command);
  void LeClearFilterAcceptList(CommandView command);
  void LeAddDeviceToFilterAcceptList(CommandView command);
  void LeRemoveDeviceFromFilterAcceptList(CommandView command);

  // 7.8.18
  void LeConnectionUpdate(CommandView command);

  // 7.8.21
  void LeReadRemoteFeaturesPage0(CommandView command);

  // 7.8.22
  void LeEncrypt(CommandView command);

  // 7.8.23
  void LeRand(CommandView command);

  // 7.8.24
  void LeStartEncryption(CommandView command);

  // 7.8.25 - 7.8.26
  void LeLongTermKeyRequestReply(CommandView command);
  void LeLongTermKeyRequestNegativeReply(CommandView command);

  // 7.8.27
  void LeReadSupportedStates(CommandView command);

  // 7.8.31 - 7.8.32
  void LeRemoteConnectionParameterRequestReply(CommandView command);
  void LeRemoteConnectionParameterRequestNegativeReply(CommandView command);

  // 7.8.34 - 7.8.35
  void LeReadSuggestedDefaultDataLength(CommandView command);
  void LeWriteSuggestedDefaultDataLength(CommandView command);

  // 7.8.36
  void LeReadLocalP256PublicKey(CommandView command);

  // 7.8.38 - 7.8.41
  void LeAddDeviceToResolvingList(CommandView command);
  void LeRemoveDeviceFromResolvingList(CommandView command);
  void LeClearResolvingList(CommandView command);
  void LeReadResolvingListSize(CommandView command);

  // 7.8.42 - 7.8.43
  void LeReadPeerResolvableAddress(CommandView command);
  void LeReadLocalResolvableAddress(CommandView command);

  // 7.8.44 - 7.8.45
  void LeSetAddressResolutionEnable(CommandView command);
  void LeSetResolvablePrivateAddressTimeout(CommandView command);

  // 7.8.46
  void LeReadMaximumDataLength(CommandView command);

  // 7.8.47 - 7.8.49
  void LeReadPhy(CommandView command);
  void LeSetDefaultPhy(CommandView command);
  void LeSetPhy(CommandView command);

  // 7.8.52 - 7.8.60
  void LeSetAdvertisingSetRandomAddress(CommandView command);
  void LeSetExtendedAdvertisingParametersV1(CommandView command);
  void LeSetExtendedAdvertisingData(CommandView command);
  void LeSetExtendedScanResponseData(CommandView command);
  void LeSetExtendedAdvertisingEnable(CommandView command);
  void LeReadMaximumAdvertisingDataLength(CommandView command);
  void LeReadNumberOfSupportedAdvertisingSets(CommandView command);
  void LeRemoveAdvertisingSet(CommandView command);
  void LeClearAdvertisingSets(CommandView command);

  // 7.8.61 - 7.8.63
  void LeSetPeriodicAdvertisingParametersV1(CommandView command);
  void LeSetPeriodicAdvertisingData(CommandView command);
  void LeSetPeriodicAdvertisingEnable(CommandView command);

  // 7.8.67 - 7.8.69
  void LePeriodicAdvertisingCreateSync(CommandView command);
  void LePeriodicAdvertisingCreateSyncCancel(CommandView command);
  void LePeriodicAdvertisingTerminateSync(CommandView command);

  // 7.8.70 - 7.8.73
  void LeAddDeviceToPeriodicAdvertiserList(CommandView command);
  void LeRemoveDeviceFromPeriodicAdvertiserList(CommandView command);
  void LeClearPeriodicAdvertiserList(CommandView command);
  void LeReadPeriodicAdvertiserListSize(CommandView command);

  // 7.8.64 - 7.8.65
  void LeSetExtendedScanParameters(CommandView command);
  void LeSetExtendedScanEnable(CommandView command);

  // 7.8.66
  void LeExtendedCreateConnectionV1(CommandView command);

  // 7.8.77
  void LeSetPrivacyMode(CommandView command);

  // 7.8.108
  void LeRequestPeerSca(CommandView command);

  // 7.8.115
  void LeSetHostFeatureV1(CommandView command);

  // Vendor-specific Commands
  void LeGetVendorCapabilities(CommandView command);
  void LeBatchScan(CommandView command);
  void LeApcf(CommandView command);
  void LeGetControllerActivityEnergyInfo(CommandView command);
  void LeExSetScanParameters(CommandView command);
  void GetControllerDebugInfo(CommandView command);

  // CSR vendor command.
  // Implement the command specific to the CSR controller
  // used specifically by the PTS tool to pass certification tests.
  void CsrVendorCommand(CommandView command);
  void CsrReadVarid(CsrVarid varid, std::vector<uint8_t>& value) const;
  void CsrWriteVarid(CsrVarid varid, std::vector<uint8_t> const& value) const;
  void CsrReadPskey(CsrPskey pskey, std::vector<uint8_t>& value) const;
  void CsrWritePskey(CsrPskey pskey, std::vector<uint8_t> const& value);

  // Command pass-through.
  void ForwardToLm(CommandView command);
  void ForwardToLl(CommandView command);

protected:
  // Controller configuration.
  ControllerProperties properties_;

  // Link Layer state.
  LinkLayerController link_layer_controller_{address_, properties_, id_};

private:
  // Send a HCI_Command_Complete event for the specified op_code with
  // the error code UNKNOWN_OPCODE.
  void SendCommandCompleteUnknownOpCodeEvent(bluetooth::hci::OpCode op_code) const;

  // Validate that a received packet is correctly formatted.
  // If the packet failed to be parsed, the function sends a
  // HCI Hardware Error event to the host and logs the packet to
  // the configured handler.
  template <typename T>
  bool CheckPacketView(T const& view, std::string reason) {
    if (view.IsValid()) {
      return true;
    }

    // Send a hardware error to reset the host, and report the packet
    // for tracing.
    send_event_(bluetooth::hci::HardwareErrorBuilder::Create(0x43));
    invalid_packet_handler_(id_, InvalidPacketReason::kParseError, reason, view.bytes().bytes());
    return false;
  }

  // Callbacks to send packets back to the HCI.
  std::function<void(std::shared_ptr<bluetooth::hci::AclBuilder>)> send_acl_;
  std::function<void(std::shared_ptr<bluetooth::hci::EventBuilder>)> send_event_;
  std::function<void(std::shared_ptr<bluetooth::hci::ScoBuilder>)> send_sco_;
  std::function<void(std::shared_ptr<bluetooth::hci::IsoBuilder>)> send_iso_;

  // Report invalid packets received on this controller instance.
  std::function<void(uint32_t, InvalidPacketReason, std::string, std::vector<uint8_t> const&)>
          invalid_packet_handler_;

  // Loopback mode (Vol 4, Part E § 7.6.1).
  // The local loopback mode is used to pass the android Vendor Test Suite
  // with RootCanal.
  bluetooth::hci::LoopbackMode loopback_mode_{LoopbackMode::NO_LOOPBACK};

  // Random value generator, always seeded with 0 to be deterministic.
  std::mt19937_64 random_generator_{};

  // Flag set to true after the HCI Reset command has been received
  // the first time.
  bool controller_reset_{false};

  // Map command opcodes to the corresponding bit index in the
  // supported command mask.
  static const std::unordered_map<OpCode, OpCodeIndex> hci_command_op_code_to_index_;

  // Map all implemented opcodes to the function implementing the handler
  // for the associated command. The map should be a subset of the
  // supported_command field in the properties_ object. Commands
  // that are supported but not implemented will raise a fatal assert.
  using CommandHandler = std::function<void(DualModeController*, bluetooth::hci::CommandView)>;
  static const std::unordered_map<OpCode, CommandHandler> hci_command_handlers_;
};

}  // namespace rootcanal
