/*
    * Avrcp.cpp
    * Bluetooth AVRCP -- minimal Target role over AVCTP (PSM 0x0017)
    * Copyright (c) 2026 Daniel Hammer
*/

#include "Avrcp.hpp"
#include "A2dp.hpp"
#include "L2cap.hpp"
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Libraries/Memory.hpp>

using namespace Kt;

namespace Drivers::USB::Bluetooth::Avrcp {

    // =========================================================================
    // Constants
    // =========================================================================

    // AV/C command types (commands) / response codes (responses)
    constexpr uint8_t AVC_CTYPE_CONTROL   = 0x0;
    constexpr uint8_t AVC_CTYPE_STATUS    = 0x1;
    constexpr uint8_t AVC_CTYPE_NOTIFY    = 0x3;
    constexpr uint8_t AVC_RSP_NOT_IMPL    = 0x8;
    constexpr uint8_t AVC_RSP_ACCEPTED    = 0x9;
    constexpr uint8_t AVC_RSP_REJECTED    = 0xA;
    constexpr uint8_t AVC_RSP_STABLE      = 0xC;
    constexpr uint8_t AVC_RSP_INTERIM     = 0xF;

    // AV/C opcodes
    constexpr uint8_t AVC_OP_VENDOR       = 0x00;
    constexpr uint8_t AVC_OP_UNIT_INFO    = 0x30;
    constexpr uint8_t AVC_OP_SUBUNIT_INFO = 0x31;
    constexpr uint8_t AVC_OP_PASSTHROUGH  = 0x7C;

    // AVRCP vendor-dependent PDUs (BT SIG company id 00 19 58)
    constexpr uint8_t PDU_GET_CAPABILITIES = 0x10;
    constexpr uint8_t PDU_GET_PLAY_STATUS  = 0x30;
    constexpr uint8_t PDU_REGISTER_NOTIFY  = 0x31;
    constexpr uint8_t PDU_SET_ABS_VOLUME   = 0x50;

    // AVRCP notification events
    constexpr uint8_t EVT_PLAYBACK_STATUS  = 0x01;
    constexpr uint8_t EVT_TRACK_CHANGED    = 0x02;
    constexpr uint8_t EVT_VOLUME_CHANGED   = 0x0D;

    // =========================================================================
    // Send helpers
    // =========================================================================

    // AVCTP single-packet response: byte0 = transaction<<4 | pktType<<2 |
    // C/R(1=response)<<1 | IPID; bytes 1-2 = PID big-endian.
    static void SendAvctp(uint16_t cid, uint8_t transaction, uint16_t pid,
                          bool invalidPid, const uint8_t* avc, uint16_t avcLen) {
        uint8_t buf[96] = {};
        if (3u + avcLen > sizeof(buf)) return;
        buf[0] = (uint8_t)((transaction << 4) | 0x02 | (invalidPid ? 0x01 : 0));
        buf[1] = (uint8_t)(pid >> 8);
        buf[2] = (uint8_t)(pid & 0xFF);
        if (avc && avcLen) memcpy(&buf[3], avc, avcLen);
        L2cap::SendData(cid, buf, (uint16_t)(3 + avcLen));
    }

    // AVRCP vendor-dependent response frame:
    // [rsp][subunit 0x48 panel][opcode 0x00][company 00 19 58][pdu][pkt 0][len BE][params]
    static void SendVendorRsp(uint16_t cid, uint8_t transaction, uint8_t rspCode,
                              uint8_t pdu, const uint8_t* p, uint16_t n) {
        uint8_t avc[64] = {};
        if (10u + n > sizeof(avc)) return;
        avc[0] = rspCode;
        avc[1] = 0x48;                       // PANEL subunit
        avc[2] = AVC_OP_VENDOR;
        avc[3] = 0x00; avc[4] = 0x19; avc[5] = 0x58;   // BT SIG company id
        avc[6] = pdu;
        avc[7] = 0x00;                       // packet type: single
        avc[8] = (uint8_t)(n >> 8);
        avc[9] = (uint8_t)(n & 0xFF);
        if (p && n) memcpy(&avc[10], p, n);
        SendAvctp(cid, transaction, 0x110E, false, avc, (uint16_t)(10 + n));
    }

    // =========================================================================
    // ProcessAvctp
    // =========================================================================

    void ProcessAvctp(uint16_t localCid, const uint8_t* d, uint16_t len) {
        if (len < 3) return;

        uint8_t transaction = (d[0] >> 4) & 0x0F;
        uint8_t pktType = (d[0] >> 2) & 0x03;
        bool isCommand = ((d[0] >> 1) & 0x01) == 0;
        uint16_t pid = ((uint16_t)d[1] << 8) | d[2];

        if (!isCommand) return;          // a response to something we sent
        if (pktType != 0) return;        // only single-packet AVCTP supported

        if (pid != 0x110E) {
            // Unsupported PID: respond with the IPID bit set, no message body.
            SendAvctp(localCid, transaction, pid, true, nullptr, 0);
            return;
        }

        const uint8_t* avc = d + 3;
        uint16_t alen = (uint16_t)(len - 3);
        if (alen < 3) return;
        uint8_t ctype = avc[0] & 0x0F;
        uint8_t opcode = avc[2];

        switch (opcode) {
            case AVC_OP_UNIT_INFO: {
                // Fixed-format reply: PANEL subunit, BT SIG company id.
                uint8_t rsp[8] = {AVC_RSP_STABLE, 0xFF, AVC_OP_UNIT_INFO,
                                  0x07, 0x48, 0x00, 0x19, 0x58};
                SendAvctp(localCid, transaction, pid, false, rsp, sizeof(rsp));
                break;
            }

            case AVC_OP_SUBUNIT_INFO: {
                uint8_t rsp[8] = {AVC_RSP_STABLE, 0xFF, AVC_OP_SUBUNIT_INFO,
                                  0x07, 0x48, 0xFF, 0xFF, 0xFF};
                SendAvctp(localCid, transaction, pid, false, rsp, sizeof(rsp));
                break;
            }

            case AVC_OP_PASSTHROUGH: {
                // Accept every pass-through (play/pause/etc.).  Echo the frame
                // with the response code swapped in.  Wiring the operation ids
                // to the Music app is a later feature; ACCEPTED keeps the
                // controller-side state machine happy.
                uint8_t rsp[16] = {};
                uint16_t cp = (alen > sizeof(rsp)) ? (uint16_t)sizeof(rsp) : alen;
                memcpy(rsp, avc, cp);
                rsp[0] = AVC_RSP_ACCEPTED;
                SendAvctp(localCid, transaction, pid, false, rsp, cp);
                if (alen >= 4) {
                    KernelLogStream(INFO, "BT-AVRCP") << "pass-through op="
                        << base::hex << (uint64_t)(avc[3] & 0x7F)
                        << ((avc[3] & 0x80) ? " release" : " press") << base::dec;
                }
                break;
            }

            case AVC_OP_VENDOR: {
                // [ctype][subunit][00][company 3][pdu][pkt][len BE][params]
                if (alen < 10) break;
                if (avc[3] != 0x00 || avc[4] != 0x19 || avc[5] != 0x58) {
                    uint8_t rsp[8] = {};
                    memcpy(rsp, avc, 8);
                    rsp[0] = AVC_RSP_NOT_IMPL;
                    SendAvctp(localCid, transaction, pid, false, rsp, 8);
                    break;
                }
                uint8_t pdu = avc[6];
                uint16_t plen = ((uint16_t)avc[8] << 8) | avc[9];
                const uint8_t* p = &avc[10];
                if (10u + plen > alen) plen = (uint16_t)(alen - 10);

                if (pdu == PDU_GET_CAPABILITIES && ctype == AVC_CTYPE_STATUS && plen >= 1) {
                    if (p[0] == 0x02) {          // CompanyID list
                        uint8_t rp[5] = {0x02, 0x01, 0x00, 0x19, 0x58};
                        SendVendorRsp(localCid, transaction, AVC_RSP_STABLE,
                                      pdu, rp, sizeof(rp));
                    } else if (p[0] == 0x03) {   // EventsSupported
                        uint8_t rp[5] = {0x03, 0x03, EVT_PLAYBACK_STATUS,
                                         EVT_TRACK_CHANGED, EVT_VOLUME_CHANGED};
                        SendVendorRsp(localCid, transaction, AVC_RSP_STABLE,
                                      pdu, rp, sizeof(rp));
                    } else {
                        uint8_t err = 0x01;      // invalid parameter
                        SendVendorRsp(localCid, transaction, AVC_RSP_REJECTED,
                                      pdu, &err, 1);
                    }
                } else if (pdu == PDU_GET_PLAY_STATUS && ctype == AVC_CTYPE_STATUS) {
                    // song length / position unknown (0xFFFFFFFF), status by state
                    uint8_t rp[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                     (uint8_t)(A2dp::IsStreaming() ? 0x01 : 0x00)};
                    SendVendorRsp(localCid, transaction, AVC_RSP_STABLE,
                                  pdu, rp, sizeof(rp));
                } else if (pdu == PDU_REGISTER_NOTIFY && ctype == AVC_CTYPE_NOTIFY && plen >= 1) {
                    // INTERIM response with the current value.  (A CHANGED
                    // follow-up on actual change is a later feature.)
                    if (p[0] == EVT_VOLUME_CHANGED) {
                        uint8_t rp[2] = {EVT_VOLUME_CHANGED,
                                         (uint8_t)((A2dp::GetVolume() * 127) / 100)};
                        SendVendorRsp(localCid, transaction, AVC_RSP_INTERIM,
                                      pdu, rp, sizeof(rp));
                    } else if (p[0] == EVT_PLAYBACK_STATUS) {
                        uint8_t rp[2] = {EVT_PLAYBACK_STATUS,
                                         (uint8_t)(A2dp::IsStreaming() ? 0x01 : 0x00)};
                        SendVendorRsp(localCid, transaction, AVC_RSP_INTERIM,
                                      pdu, rp, sizeof(rp));
                    } else if (p[0] == EVT_TRACK_CHANGED) {
                        uint8_t rp[9] = {EVT_TRACK_CHANGED,
                                         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
                        SendVendorRsp(localCid, transaction, AVC_RSP_INTERIM,
                                      pdu, rp, sizeof(rp));
                    } else {
                        uint8_t err = 0x01;
                        SendVendorRsp(localCid, transaction, AVC_RSP_REJECTED,
                                      pdu, &err, 1);
                    }
                } else if (pdu == PDU_SET_ABS_VOLUME && ctype == AVC_CTYPE_CONTROL && plen >= 1) {
                    uint8_t vol = p[0] & 0x7F;
                    A2dp::SetVolume(((int)vol * 100) / 127);
                    SendVendorRsp(localCid, transaction, AVC_RSP_ACCEPTED,
                                  pdu, &vol, 1);
                    KernelLogStream(INFO, "BT-AVRCP") << "absolute volume -> "
                        << (uint64_t)(((int)vol * 100) / 127) << "%";
                } else {
                    uint8_t err = 0x00;          // invalid command
                    SendVendorRsp(localCid, transaction, AVC_RSP_REJECTED,
                                  pdu, &err, 1);
                }
                break;
            }

            default: {
                uint8_t rsp[8] = {};
                uint16_t cp = (alen > sizeof(rsp)) ? (uint16_t)sizeof(rsp) : alen;
                memcpy(rsp, avc, cp);
                rsp[0] = AVC_RSP_NOT_IMPL;
                SendAvctp(localCid, transaction, pid, false, rsp, cp);
                break;
            }
        }
    }

}
