# Copyright 2021-2025 Google LLC
#
# 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
#
#      https://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.

# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
from __future__ import annotations

import dataclasses
from typing import ClassVar

from bumble import hci


# -----------------------------------------------------------------------------
# Advertising PDU
# -----------------------------------------------------------------------------
class AdvertisingPdu:
    """Base Advertising Physical Channel PDU class.

    See Core Spec 6.0, Volume 6, Part B, 2.3. Advertising physical channel PDU.

    Currently these messages don't really follow the LL spec, because LL protocol is
    context-aware and we don't have real physical transport.
    """


@dataclasses.dataclass
class ConnectInd(AdvertisingPdu):
    initiator_address: hci.Address
    advertiser_address: hci.Address
    interval: int
    latency: int
    timeout: int


@dataclasses.dataclass
class AdvInd(AdvertisingPdu):
    advertiser_address: hci.Address
    data: bytes


@dataclasses.dataclass
class AdvDirectInd(AdvertisingPdu):
    advertiser_address: hci.Address
    target_address: hci.Address


@dataclasses.dataclass
class AdvNonConnInd(AdvertisingPdu):
    advertiser_address: hci.Address
    data: bytes


@dataclasses.dataclass
class AdvExtInd(AdvertisingPdu):
    advertiser_address: hci.Address
    data: bytes

    target_address: hci.Address | None = None
    adi: int | None = None
    tx_power: int | None = None


# -----------------------------------------------------------------------------
# LL Control PDU
# -----------------------------------------------------------------------------
class ControlPdu:
    """Base LL Control PDU Class.

    See Core Spec 6.0, Volume 6, Part B, 2.4.2. LL Control PDU.

    Currently these messages don't really follow the LL spec, because LL protocol is
    context-aware and we don't have real physical transport.
    """

    class Opcode(hci.SpecableEnum):
        LL_CONNECTION_UPDATE_IND = 0x00
        LL_CHANNEL_MAP_IND = 0x01
        LL_TERMINATE_IND = 0x02
        LL_ENC_REQ = 0x03
        LL_ENC_RSP = 0x04
        LL_START_ENC_REQ = 0x05
        LL_START_ENC_RSP = 0x06
        LL_UNKNOWN_RSP = 0x07
        LL_FEATURE_REQ = 0x08
        LL_FEATURE_RSP = 0x09
        LL_PAUSE_ENC_REQ = 0x0A
        LL_PAUSE_ENC_RSP = 0x0B
        LL_VERSION_IND = 0x0C
        LL_REJECT_IND = 0x0D
        LL_PERIPHERAL_FEATURE_REQ = 0x0E
        LL_CONNECTION_PARAM_REQ = 0x0F
        LL_CONNECTION_PARAM_RSP = 0x10
        LL_REJECT_EXT_IND = 0x11
        LL_PING_REQ = 0x12
        LL_PING_RSP = 0x13
        LL_LENGTH_REQ = 0x14
        LL_LENGTH_RSP = 0x15
        LL_PHY_REQ = 0x16
        LL_PHY_RSP = 0x17
        LL_PHY_UPDATE_IND = 0x18
        LL_MIN_USED_CHANNELS_IND = 0x19
        LL_CTE_REQ = 0x1A
        LL_CTE_RSP = 0x1B
        LL_PERIODIC_SYNC_IND = 0x1C
        LL_CLOCK_ACCURACY_REQ = 0x1D
        LL_CLOCK_ACCURACY_RSP = 0x1E
        LL_CIS_REQ = 0x1F
        LL_CIS_RSP = 0x20
        LL_CIS_IND = 0x21
        LL_CIS_TERMINATE_IND = 0x22
        LL_POWER_CONTROL_REQ = 0x23
        LL_POWER_CONTROL_RSP = 0x24
        LL_POWER_CHANGE_IND = 0x25
        LL_SUBRATE_REQ = 0x26
        LL_SUBRATE_IND = 0x27
        LL_CHANNEL_REPORTING_IND = 0x28
        LL_CHANNEL_STATUS_IND = 0x29
        LL_PERIODIC_SYNC_WR_IND = 0x2A
        LL_FEATURE_EXT_REQ = 0x2B
        LL_FEATURE_EXT_RSP = 0x2C
        LL_CS_SEC_RSP = 0x2D
        LL_CS_CAPABILITIES_REQ = 0x2E
        LL_CS_CAPABILITIES_RSP = 0x2F
        LL_CS_CONFIG_REQ = 0x30
        LL_CS_CONFIG_RSP = 0x31
        LL_CS_REQ = 0x32
        LL_CS_RSP = 0x33
        LL_CS_IND = 0x34
        LL_CS_TERMINATE_REQ = 0x35
        LL_CS_FAE_REQ = 0x36
        LL_CS_FAE_RSP = 0x37
        LL_CS_CHANNEL_MAP_IND = 0x38
        LL_CS_SEC_REQ = 0x39
        LL_CS_TERMINATE_RSP = 0x3A
        LL_FRAME_SPACE_REQ = 0x3B
        LL_FRAME_SPACE_RSP = 0x3C

    opcode: ClassVar[Opcode]


@dataclasses.dataclass
class TerminateInd(ControlPdu):
    opcode = ControlPdu.Opcode.LL_TERMINATE_IND

    error_code: int


@dataclasses.dataclass
class EncReq(ControlPdu):
    opcode = ControlPdu.Opcode.LL_ENC_REQ

    rand: bytes
    ediv: int
    ltk: bytes


@dataclasses.dataclass
class CisReq(ControlPdu):
    opcode = ControlPdu.Opcode.LL_CIS_REQ

    cig_id: int
    cis_id: int


@dataclasses.dataclass
class CisRsp(ControlPdu):
    opcode = ControlPdu.Opcode.LL_CIS_REQ

    cig_id: int
    cis_id: int


@dataclasses.dataclass
class CisInd(ControlPdu):
    opcode = ControlPdu.Opcode.LL_CIS_REQ

    cig_id: int
    cis_id: int


@dataclasses.dataclass
class CisTerminateInd(ControlPdu):
    opcode = ControlPdu.Opcode.LL_CIS_TERMINATE_IND

    cig_id: int
    cis_id: int
    error_code: int


@dataclasses.dataclass
class FeatureReq(ControlPdu):
    opcode = ControlPdu.Opcode.LL_FEATURE_REQ

    feature_set: bytes


@dataclasses.dataclass
class FeatureRsp(ControlPdu):
    opcode = ControlPdu.Opcode.LL_FEATURE_RSP

    feature_set: bytes


@dataclasses.dataclass
class PeripheralFeatureReq(ControlPdu):
    opcode = ControlPdu.Opcode.LL_PERIPHERAL_FEATURE_REQ

    feature_set: bytes
