/*
    * Hci.hpp
    * Bluetooth HCI (Host Controller Interface) layer
    * HCI transport over USB bulk/interrupt/control endpoints
    * Copyright (c) 2026 Daniel Hammer
*/

#pragma once
#include <cstdint>

namespace Drivers::USB::Bluetooth::Hci {

    // =========================================================================
    // HCI packet types (for USB transport)
    // =========================================================================

    // USB transport uses different endpoints for each packet type:
    // Commands  -> Control EP0 (class request)
    // ACL data  -> Bulk OUT / Bulk IN
    // Events    -> Interrupt IN

    // =========================================================================
    // HCI command opcodes (OGF << 10 | OCF)
    // =========================================================================

    // Link Control (OGF 0x01)
    constexpr uint16_t OP_INQUIRY               = 0x0401;
    constexpr uint16_t OP_INQUIRY_CANCEL         = 0x0402;
    constexpr uint16_t OP_CREATE_CONNECTION      = 0x0405;
    constexpr uint16_t OP_DISCONNECT             = 0x0406;
    constexpr uint16_t OP_ACCEPT_CONN_REQ        = 0x0409;
    constexpr uint16_t OP_REJECT_CONN_REQ        = 0x040A;
    constexpr uint16_t OP_LINK_KEY_REQ_REPLY     = 0x040B;
    constexpr uint16_t OP_LINK_KEY_REQ_NEG_REPLY = 0x040C;
    constexpr uint16_t OP_AUTH_REQUESTED         = 0x0411;
    constexpr uint16_t OP_SET_CONN_ENCRYPT       = 0x0413;
    constexpr uint16_t OP_IO_CAPABILITY_REPLY    = 0x042B;
    constexpr uint16_t OP_USER_CONFIRM_REPLY     = 0x042C;

    // Link Policy (OGF 0x02)
    constexpr uint16_t OP_WRITE_DEFAULT_LP       = 0x080F;
    constexpr uint16_t OP_SNIFF_MODE             = 0x0803;

    // Controller & Baseband (OGF 0x03)
    constexpr uint16_t OP_RESET                  = 0x0C03;
    constexpr uint16_t OP_SET_EVENT_FILTER       = 0x0C05;
    constexpr uint16_t OP_WRITE_LOCAL_NAME       = 0x0C13;
    constexpr uint16_t OP_READ_LOCAL_NAME        = 0x0C14;
    constexpr uint16_t OP_WRITE_SCAN_ENABLE      = 0x0C1A;
    constexpr uint16_t OP_WRITE_CLASS_OF_DEVICE  = 0x0C24;
    constexpr uint16_t OP_WRITE_SSP_MODE         = 0x0C56;
    constexpr uint16_t OP_WRITE_INQUIRY_MODE     = 0x0C45;
    constexpr uint16_t OP_WRITE_PAGE_TIMEOUT     = 0x0C18;
    constexpr uint16_t OP_WRITE_AUTH_ENABLE      = 0x0C20;
    constexpr uint16_t OP_SET_EVENT_MASK         = 0x0C01;

    // Informational Parameters (OGF 0x04)
    constexpr uint16_t OP_READ_BD_ADDR           = 0x1009;
    constexpr uint16_t OP_READ_LOCAL_VERSION     = 0x1001;
    constexpr uint16_t OP_READ_LOCAL_FEATURES    = 0x1003;
    constexpr uint16_t OP_READ_BUFFER_SIZE       = 0x1005;

    // Intel vendor commands (OGF 0x3F)
    constexpr uint16_t OP_INTEL_READ_VERSION     = 0xFC05;
    constexpr uint16_t OP_INTEL_RESET            = 0xFC01;
    constexpr uint16_t OP_INTEL_SET_EVENT_MASK   = 0xFC52;
    constexpr uint16_t OP_INTEL_DDC_CONFIG_WRITE = 0xFC8B;
    constexpr uint16_t OP_INTEL_SECURE_SEND      = 0xFC09;
    constexpr uint16_t OP_INTEL_WRITE_BD_ADDR    = 0xFC31;  // set adapter BD_ADDR

    // =========================================================================
    // HCI event codes
    // =========================================================================

    constexpr uint8_t EVT_INQUIRY_COMPLETE       = 0x01;
    constexpr uint8_t EVT_INQUIRY_RESULT         = 0x02;
    constexpr uint8_t EVT_CONNECTION_COMPLETE     = 0x03;
    constexpr uint8_t EVT_CONNECTION_REQUEST      = 0x04;
    constexpr uint8_t EVT_DISCONNECTION_COMPLETE  = 0x05;
    constexpr uint8_t EVT_AUTH_COMPLETE           = 0x06;
    constexpr uint8_t EVT_ENCRYPT_CHANGE          = 0x08;
    constexpr uint8_t EVT_LINK_KEY_REQUEST        = 0x17;
    constexpr uint8_t EVT_LINK_KEY_NOTIFICATION   = 0x18;
    constexpr uint8_t EVT_COMMAND_COMPLETE        = 0x0E;
    constexpr uint8_t EVT_COMMAND_STATUS          = 0x0F;
    constexpr uint8_t EVT_NUM_COMPLETED_PACKETS   = 0x13;
    constexpr uint8_t EVT_IO_CAPABILITY_REQUEST   = 0x31;
    constexpr uint8_t EVT_IO_CAPABILITY_RESPONSE  = 0x32;
    constexpr uint8_t EVT_USER_CONFIRM_REQUEST    = 0x33;
    constexpr uint8_t EVT_SIMPLE_PAIRING_COMPLETE = 0x36;
    constexpr uint8_t EVT_INQUIRY_RESULT_RSSI     = 0x22;
    constexpr uint8_t EVT_EXTENDED_INQUIRY_RESULT = 0x2F;
    constexpr uint8_t EVT_VENDOR_SPECIFIC         = 0xFF;

    // =========================================================================
    // Inquiry result storage
    // =========================================================================

    struct InquiryDevice {
        uint8_t  BdAddr[6];
        uint8_t  _pad[2];
        uint32_t ClassOfDevice;
        int8_t   Rssi;
        uint8_t  _pad2[3];
        char     Name[64];       // From Extended Inquiry Result or Remote Name Request
    };

    constexpr int MAX_INQUIRY_RESULTS = 16;

    // =========================================================================
    // HCI packet headers
    // =========================================================================

    struct CommandHeader {
        uint16_t Opcode;
        uint8_t  ParamLength;
    } __attribute__((packed));

    struct EventHeader {
        uint8_t  EventCode;
        uint8_t  ParamLength;
    } __attribute__((packed));

    struct AclHeader {
        uint16_t HandleFlags;   // bits 11:0 = handle, 13:12 = PB flag, 15:14 = BC flag
        uint16_t DataLength;
    } __attribute__((packed));

    // ACL PB (Packet Boundary) flag values
    constexpr uint16_t ACL_PB_FIRST_NON_FLUSH = 0x0000;  // First non-auto-flushable
    constexpr uint16_t ACL_PB_CONTINUING      = 0x1000;  // Continuing fragment
    constexpr uint16_t ACL_PB_FIRST_FLUSH     = 0x2000;  // First auto-flushable

    // =========================================================================
    // HCI connection info
    // =========================================================================

    struct ConnectionInfo {
        bool     Active;
        uint16_t Handle;
        uint8_t  BdAddr[6];
        uint8_t  LinkType;      // 0x01 = ACL
        bool     Encrypted;
    };

    constexpr int MAX_CONNECTIONS = 4;

    // A persisted (bonded/paired) device, as stored in the link-key store.
    struct BondInfo {
        uint8_t Addr[6];
    };

    // =========================================================================
    // Intel Bluetooth version info
    // =========================================================================

    struct IntelVersion {
        uint8_t  Status;
        uint8_t  HwPlatform;
        uint8_t  HwVariant;
        uint8_t  HwRevision;
        uint8_t  FwVariant;     // 0x06 = bootloader, 0x23 = operational
        uint8_t  FwRevision;
        uint8_t  FwBuildNum;
        uint8_t  FwBuildWw;
        uint8_t  FwBuildYy;
        uint8_t  FwPatchNum;
    } __attribute__((packed));

    // =========================================================================
    // Public API
    // =========================================================================

    // Initialize HCI transport over USB for the given slot
    void Initialize(uint8_t slotId);

    // Start receiving HCI events and ACL data (call after HCI init sequence).
    // Arms both the interrupt IN and the bulk IN; the bulk IN must stay armed
    // through the firmware download (it absorbs the device's ~635 KB cc=4 glitch
    // and keeps the event pipe alive -- see the definition).
    void StartEventPipe();

    // Send an HCI command via USB control transfer (EP0)
    bool SendCommand(uint16_t opcode, const uint8_t* params, uint8_t paramLen);

    // Wait for a Command Complete event matching the given opcode
    // Returns true if received within timeout, fills outParams (excluding status byte)
    bool WaitCommandComplete(uint16_t opcode, uint8_t* outParams = nullptr,
                             uint8_t maxLen = 0, uint32_t timeoutMs = 2000);

    // Wait for a Command Status event matching the given opcode
    bool WaitCommandStatus(uint16_t opcode, uint32_t timeoutMs = 2000);

    // Send ACL data via USB bulk OUT
    bool SendAcl(uint16_t handle, uint16_t pbFlag, const uint8_t* data, uint16_t len);

    // Process an HCI event received on the interrupt IN endpoint
    void ProcessEvent(const uint8_t* data, uint32_t len);

    // Process ACL data received on the bulk IN endpoint
    void ProcessAcl(const uint8_t* data, uint32_t len);

    // Get connection info
    ConnectionInfo* GetConnection(uint16_t handle);
    ConnectionInfo* GetActiveConnection();
    ConnectionInfo* GetConnectionByIndex(int index);  // 0..MAX_CONNECTIONS-1

    // HCI Reset command
    bool Reset();

    // Read local BD_ADDR
    bool ReadBdAddr(uint8_t* addr);

    // Read standard HCI local version info
    struct LocalVersion {
        uint8_t  Status;
        uint8_t  HciVersion;
        uint16_t HciRevision;
        uint8_t  LmpVersion;
        uint16_t Manufacturer;
        uint16_t LmpSubversion;
    } __attribute__((packed));

    bool ReadLocalVersion(LocalVersion* ver);

    // Read Intel-specific version info
    bool ReadIntelVersion(IntelVersion* ver);

    // =========================================================================
    // Intel firmware download primitives (bootloader mode)
    // =========================================================================

    // Read the Intel version response in TLV format (0xFC05 with parameter
    // 0xFF).  Copies the raw return parameters (byte 0 = status, followed by
    // the TLV stream) into outBuf, bounded by the number of bytes actually
    // received from the controller.  Returns that length, or -1 on failure.
    int ReadIntelVersionTlv(uint8_t* outBuf, int maxLen);

    // Intel "Secure Send" (0xFC09): pushes one logical fragment to the
    // bootloader, split into <=252-byte chunks each prefixed with the
    // fragment type (0x00 CSS init, 0x01 firmware data, 0x02 signature,
    // 0x03 public key).  The bootloader does not Command-Complete these; pacing
    // is by USB transfer completion and the result arrives asynchronously as a
    // 0xFF/0x06 secure-send result event (see WaitSecureSendResult).
    bool IntelSecureSend(uint8_t fragmentType, const uint8_t* data, uint32_t len);

    // Pipelined bulk-OUT variant for the firmware payload (btusb bootloader
    // path: 0xFC09 packets on the bulk endpoint, several in flight).  Call
    // DrainBulkTx() after the last fragment to ensure everything reached the
    // controller before waiting for the download-complete result.
    bool IntelSecureSendBulk(uint8_t fragmentType, const uint8_t* data, uint32_t len);
    bool DrainBulkTx(uint32_t timeoutMs);

    // Reset / await the Intel "secure send result" vendor event (0xFF/0x06).
    // Call ClearSecureSendResult() before a download phase, then
    // WaitSecureSendResult() to read the outcome (result/status, 0 = success).
    void ClearSecureSendResult();
    bool WaitSecureSendResult(uint32_t timeoutMs, uint8_t* outResult, uint8_t* outStatus);

    // Bonded-device link key persistence (so pairings survive reboots).
    // LoadLinkKeys(): read the on-disk store once VFS is up.
    // FlushLinkKeys(): write the store to disk if it changed -- call from
    // process context (NOT an event handler), since it does blocking disk I/O.
    void LoadLinkKeys();
    void FlushLinkKeys();

    // Enumerate the stored bonds (paired devices) into buf; returns the count
    // written (<= maxCount).  Used to list paired-but-disconnected devices.
    int ListBonds(BondInfo* buf, int maxCount);

    // Remove a stored bond by BD_ADDR and persist the removal (process context).
    // Returns true if a matching bond was found and forgotten.
    bool ForgetBond(const uint8_t* addr);

    // Toggle the bounded firmware-phase interrupt-IN trace (one ring record
    // per completion, capped).  Recording is IRQ-safe (lock-free ring, no
    // logging); DumpFwTrace() prints pending records and MUST only be called
    // from process context (terminal lock is a non-IRQ-safe Mutex).
    void SetFwTrace(bool on);
    void DumpFwTrace();

    // Non-blocking peek at the most recent 0xFF/0x06 secure-send result without
    // consuming it.  Returns true if one has arrived since the last
    // ClearSecureSendResult().  The payload loop uses this to catch a mid-stream
    // rejection -- a healthy bootloader stays silent until the final fragment.
    bool PeekSecureSendResult(uint8_t* outResult, uint8_t* outStatus);

    // Reset the controller into operational firmware at bootAddr (0xFC01) and
    // wait for the Intel "bootup" vendor event.  Returns true once booted.
    bool IntelBootFirmware(uint32_t bootAddr, uint32_t timeoutMs = 5000);

    // Apply one DDC parameter record (record[0] = payload length) via 0xFC8B.
    bool IntelWriteDdcRecord(const uint8_t* record, uint8_t recordLen);

    // Configure the Intel vendor event mask (0xFC52).
    bool IntelSetEventMask();

    // Set local name
    bool WriteLocalName(const char* name);

    // Set class of device
    bool WriteClassOfDevice(uint32_t cod);

    // Enable scan (inquiry + page)
    bool WriteScanEnable(uint8_t mode);

    // Write Simple Secure Pairing mode
    bool WriteSSPMode(uint8_t mode);

    // Accept an incoming connection
    bool AcceptConnection(const uint8_t* bdAddr, uint8_t role);

    // Request authentication on an ACL link (we are the connection initiator).
    // Drives Link Key Request -> pairing; needed for bonded-device reconnects.
    bool AuthenticateLink(uint16_t handle);

    // Set the adapter's BD_ADDR (Intel vendor command 0xFC31).  Used to dodge a
    // remote that holds a stale, un-clearable bond to our real address.
    bool SetBdAddr(const uint8_t* addr);

    // Send any queued pairing replies (IO-cap / user-confirm / link-key) with
    // real confirmed transfers.  Call from top-level (e.g. the connect loop),
    // NOT from an event handler -- event handlers only enqueue.
    void ProcessPendingCommands();

    // ACL TX flow control: outstanding (un-acked) ACL packets, and the
    // controller's ACL buffer count (Number-Of-Completed-Packets credits).  The
    // media writer throttles on these so it never overruns the controller.
    uint16_t AclPendingCount();
    uint16_t AclMaxPackets();

    // ACL packets handed to the xHCI whose bulk OUT completion has not been
    // reaped yet (each one still owns a TX DMA ring slot).
    uint32_t AclTxInFlight();

    // True when SendAcl can be called without overrunning either the
    // controller's ACL buffers (NOCP credits) or the TX DMA ring.  The media
    // writer polls events until this is true before sending each frame.
    bool AclTxReady();

    // Lost-NOCP recovery: zero the credit count after a prolonged stall with
    // the USB side drained (completions presumed lost).
    void AclResetCredits();

    // Disconnect a connection
    bool Disconnect(uint16_t handle, uint8_t reason);

    // Read ACL buffer size from controller
    bool ReadBufferSize(uint16_t* aclLen, uint8_t* scoLen,
                        uint16_t* aclNum, uint16_t* scoNum);

    // Inquiry (device discovery)
    bool StartInquiry(uint8_t durationUnits);   // duration in 1.28s units (e.g., 8 = ~10s)
    bool CancelInquiry();
    int  GetInquiryResults(InquiryDevice* buf, int maxCount);
    void ClearInquiryResults();
    bool IsInquiryActive();

    // Create ACL connection to a remote device
    bool CreateConnection(const uint8_t* bdAddr);

    // Drain any pending HCI events (call in poll loops that aren't inside
    // WaitCommandComplete/WaitCommandStatus)
    void DrainEvents();

}
