/*
 *  Copyright (c) 2016-2019, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file includes definitions for MAC types.
 */

#ifndef OT_CORE_MAC_MAC_TYPES_HPP_
#define OT_CORE_MAC_MAC_TYPES_HPP_

#include "openthread-core-config.h"

#include <stdint.h>
#include <string.h>

#include <openthread/link.h>
#include <openthread/thread.h>
#include <openthread/provisional/link.h>

#include "common/as_core_type.hpp"
#include "common/clearable.hpp"
#include "common/data.hpp"
#include "common/equatable.hpp"
#include "common/string.hpp"
#include "crypto/storage.hpp"

namespace ot {

#if OPENTHREAD_FTD || OPENTHREAD_MTD
namespace Ip6 {
class InterfaceIdentifier;
}
#endif

namespace Mac {

/**
 * @addtogroup core-mac
 *
 * @{
 */

/**
 * Represents the IEEE 802.15.4 PAN ID.
 */
typedef otPanId PanId;

constexpr PanId kPanIdBroadcast = 0xffff; ///< Broadcast PAN ID.

/**
 * Represents the IEEE 802.15.4 Short Address.
 */
typedef otShortAddress ShortAddress;

constexpr ShortAddress kShortAddrBroadcast = OT_RADIO_BROADCAST_SHORT_ADDR; ///< Broadcast Short Address.
constexpr ShortAddress kShortAddrInvalid   = OT_RADIO_INVALID_SHORT_ADDR;   ///< Invalid Short Address.

/**
 * Represents the wake-up identifier.
 */
typedef otWakeupId WakeupId;

/**
 * Represents the MAC layer counters.
 */
typedef otMacCounters Counters;

/**
 * Generates a random IEEE 802.15.4 PAN ID.
 *
 * @returns A randomly generated IEEE 802.15.4 PAN ID (excluding `kPanIdBroadcast`).
 */
PanId GenerateRandomPanId(void);

/**
 * Represents an IEEE 802.15.4 Extended Address.
 */
OT_TOOL_PACKED_BEGIN
class ExtAddress : public otExtAddress, public Equatable<ExtAddress>, public Clearable<ExtAddress>
{
public:
    static constexpr uint16_t kInfoStringSize = 17; ///< Max chars for the info string (`ToString()`).

    /**
     * Defines the fixed-length `String` object returned from `ToString()`.
     */
    typedef String<kInfoStringSize> InfoString;

    /**
     * Type specifies the copy byte order when Extended Address is being copied to/from a buffer.
     */
    enum CopyByteOrder : uint8_t
    {
        kNormalByteOrder,  ///< Copy address bytes in normal order (as provided in array buffer).
        kReverseByteOrder, ///< Copy address bytes in reverse byte order.
    };

    /**
     * Fills all bytes of address with a given byte value.
     *
     * @param[in] aByte A byte value to fill address with.
     */
    void Fill(uint8_t aByte) { memset(this, aByte, sizeof(*this)); }

#if OPENTHREAD_FTD || OPENTHREAD_MTD
    /**
     * Generates a random IEEE 802.15.4 Extended Address.
     */
    void GenerateRandom(void);

    /**
     * Sets the Extended Address from a given IPv6 Address Interface Identifier.
     *
     * @param[in] aIid   The IPv6 Interface Identifier to convert to Extended Address.
     */
    void SetFromIid(const Ip6::InterfaceIdentifier &aIid);
#endif

    /**
     * Sets the Extended Address from a given byte array.
     *
     * @param[in] aBuffer    Pointer to an array containing the Extended Address. `OT_EXT_ADDRESS_SIZE` bytes from
     *                       buffer are copied to form the Extended Address.
     * @param[in] aByteOrder The byte order to use when copying the address.
     */
    void Set(const uint8_t *aBuffer, CopyByteOrder aByteOrder = kNormalByteOrder)
    {
        CopyAddress(m8, aBuffer, aByteOrder);
    }

    /**
     * Indicates whether or not the Group bit is set.
     *
     * @retval TRUE   If the group bit is set.
     * @retval FALSE  If the group bit is not set.
     */
    bool IsGroup(void) const { return (m8[0] & kGroupFlag) != 0; }

    /**
     * Sets the Group bit.
     *
     * @param[in]  aGroup  TRUE if group address, FALSE otherwise.
     */
    void SetGroup(bool aGroup)
    {
        if (aGroup)
        {
            m8[0] |= kGroupFlag;
        }
        else
        {
            m8[0] &= ~kGroupFlag;
        }
    }

    /**
     * Toggles the Group bit.
     */
    void ToggleGroup(void) { m8[0] ^= kGroupFlag; }

    /**
     * Indicates whether or not the Local bit is set.
     *
     * @retval TRUE   If the local bit is set.
     * @retval FALSE  If the local bit is not set.
     */
    bool IsLocal(void) const { return (m8[0] & kLocalFlag) != 0; }

    /**
     * Sets the Local bit.
     *
     * @param[in]  aLocal  TRUE if locally administered, FALSE otherwise.
     */
    void SetLocal(bool aLocal)
    {
        if (aLocal)
        {
            m8[0] |= kLocalFlag;
        }
        else
        {
            m8[0] &= ~kLocalFlag;
        }
    }

    /**
     * Toggles the Local bit.
     */
    void ToggleLocal(void) { m8[0] ^= kLocalFlag; }

    /**
     * Copies the Extended Address into a given buffer.
     *
     * @param[out] aBuffer     A pointer to a buffer to copy the Extended Address into.
     * @param[in]  aByteOrder  The byte order to copy the address.
     */
    void CopyTo(uint8_t *aBuffer, CopyByteOrder aByteOrder = kNormalByteOrder) const
    {
        CopyAddress(aBuffer, m8, aByteOrder);
    }

    /**
     * Converts an address to a string.
     *
     * @returns An `InfoString` containing the string representation of the Extended Address.
     */
    InfoString ToString(void) const;

    /**
     * Parses an Extended Address from a string.
     *
     * The string must be a hex representation of the address (e.g., "0123456789abcdef").
     * The parsing is case-insensitive.
     *
     * @param[in]  aString  A pointer to the string to parse.
     *
     * @retval kErrorNone          Successfully parsed the Extended Address.
     * @retval kErrorInvalidArgs   @p aString is `nullptr`.
     * @retval kErrorParse         @p aString is not a valid hex string representation of an Extended Address.
     */
    Error FromString(const char *aString);

private:
    static constexpr uint8_t kGroupFlag = (1 << 0);
    static constexpr uint8_t kLocalFlag = (1 << 1);

    static void CopyAddress(uint8_t *aDst, const uint8_t *aSrc, CopyByteOrder aByteOrder);
} OT_TOOL_PACKED_END;

/**
 * Represents an IEEE 802.15.4 Short or Extended Address.
 */
class Address
{
public:
    /**
     * Defines the fixed-length `String` object returned from `ToString()`.
     */
    typedef ExtAddress::InfoString InfoString;

    /**
     * Specifies the IEEE 802.15.4 Address type.
     */
    enum Type : uint8_t
    {
        kTypeNone,     ///< No address.
        kTypeShort,    ///< IEEE 802.15.4 Short Address.
        kTypeExtended, ///< IEEE 802.15.4 Extended Address.
    };

    /**
     * Initializes an Address.
     */
    Address(void)
        : mType(kTypeNone)
    {
    }

    /**
     * Gets the address type (Short Address, Extended Address, or none).
     *
     * @returns The address type.
     */
    Type GetType(void) const { return mType; }

    /**
     * Indicates whether or not there is an address.
     *
     * @returns TRUE if there is no address (i.e. address type is `kTypeNone`), FALSE otherwise.
     */
    bool IsNone(void) const { return (mType == kTypeNone); }

    /**
     * Indicates whether or not the Address is a Short Address.
     *
     * @returns TRUE if it is a Short Address, FALSE otherwise.
     */
    bool IsShort(void) const { return (mType == kTypeShort); }

    /**
     * Indicates whether or not the Address is an Extended Address.
     *
     * @returns TRUE if it is an Extended Address, FALSE otherwise.
     */
    bool IsExtended(void) const { return (mType == kTypeExtended); }

    /**
     * Gets the address as a Short Address.
     *
     * MUST be used only if the address type is Short Address.
     *
     * @returns The Short Address.
     */
    ShortAddress GetShort(void) const { return mShared.mShortAddress; }

    /**
     * Gets the address as an Extended Address.
     *
     * MUST be used only if the address type is Extended Address.
     *
     * @returns A constant reference to the Extended Address.
     */
    const ExtAddress &GetExtended(void) const { return mShared.mExtAddress; }

    /**
     * Gets the address as an Extended Address.
     *
     * MUST be used only if the address type is Extended Address.
     *
     * @returns A reference to the Extended Address.
     */
    ExtAddress &GetExtended(void) { return mShared.mExtAddress; }

    /**
     * Sets the address to none (i.e., clears the address).
     *
     * Address type will be updated to `kTypeNone`.
     */
    void SetNone(void) { mType = kTypeNone; }

    /**
     * Sets the address with a Short Address.
     *
     * The type is also updated to indicate that address is Short.
     *
     * @param[in]  aShortAddress  A Short Address
     */
    void SetShort(ShortAddress aShortAddress)
    {
        mShared.mShortAddress = aShortAddress;
        mType                 = kTypeShort;
    }

    /**
     * Sets the address with an Extended Address.
     *
     * The type is also updated to indicate that the address is Extended.
     *
     * @param[in]  aExtAddress  An Extended Address
     */
    void SetExtended(const ExtAddress &aExtAddress)
    {
        mShared.mExtAddress = aExtAddress;
        mType               = kTypeExtended;
    }

    /**
     * Sets the address with an Extended Address given as a byte array.
     *
     * The type is also updated to indicate that the address is Extended.
     *
     * @param[in] aBuffer    Pointer to an array containing the Extended Address. `OT_EXT_ADDRESS_SIZE` bytes from
     *                       buffer are copied to form the Extended Address.
     * @param[in] aByteOrder The byte order to copy the address from @p aBuffer.
     */
    void SetExtended(const uint8_t *aBuffer, ExtAddress::CopyByteOrder aByteOrder = ExtAddress::kNormalByteOrder)
    {
        mShared.mExtAddress.Set(aBuffer, aByteOrder);
        mType = kTypeExtended;
    }

#if OPENTHREAD_FTD || OPENTHREAD_MTD
    /**
     * Sets the address as an Extended Address from a given IPv6 Address Interface Identifier.
     *
     * @param[in] aIid   The IPv6 Interface Identifier to convert to Extended Address.
     */
    void SetExtendedFromIid(const Ip6::InterfaceIdentifier &aIid);
#endif

    /**
     * Indicates whether or not the address is a Short Broadcast Address.
     *
     * @returns TRUE if address is Short Broadcast Address, FALSE otherwise.
     */
    bool IsBroadcast(void) const { return ((mType == kTypeShort) && (GetShort() == kShortAddrBroadcast)); }

    /**
     * Indicates whether or not the address is a Short Invalid Address.
     *
     * @returns TRUE if address is Short Invalid Address, FALSE otherwise.
     */
    bool IsShortAddrInvalid(void) const { return ((mType == kTypeShort) && (GetShort() == kShortAddrInvalid)); }

    /**
     * Overloads operator `==` to evaluate whether or not two `Address` instances are equal.
     *
     * @param[in]  aOther  The other `Address` instance to compare with.
     *
     * @retval TRUE   If the two `Address` instances are equal.
     * @retval FALSE  If the two `Address` instances are not equal.
     */
    bool operator==(const Address &aOther) const;

    /**
     * Converts an address to a null-terminated string
     *
     * @returns A `String` representing the address.
     */
    InfoString ToString(void) const;

private:
    union
    {
        ShortAddress mShortAddress; ///< The IEEE 802.15.4 Short Address.
        ExtAddress   mExtAddress;   ///< The IEEE 802.15.4 Extended Address.
    } mShared;

    Type mType; ///< The address type (Short, Extended, or none).
};

/**
 * Represents two MAC addresses corresponding to source and destination.
 */
struct Addresses
{
    Address mSource;      ///< Source address.
    Address mDestination; ///< Destination address.
};

/**
 * Represents two PAN IDs corresponding to source and destination.
 */
class PanIds : public Clearable<PanIds>
{
public:
    /**
     * Initializes PAN IDs as empty (no source or destination PAN ID).
     */
    PanIds(void) { Clear(); }

    /**
     * Indicates whether or not source PAN ID is present.
     *
     * @retval TRUE   The source PAN ID is present.
     * @retval FALSE  The source PAN ID is not present.
     */
    bool IsSourcePresent(void) const { return mIsSourcePresent; }

    /**
     * Gets the source PAN ID when it is present.
     *
     * @returns The source PAN ID.
     */
    PanId GetSource(void) const { return mSource; }

    /**
     * Indicates whether or not destination PAN ID is present.
     *
     * @retval TRUE   The destination PAN ID is present.
     * @retval FALSE  The destination PAN ID is not present.
     */
    bool IsDestinationPresent(void) const { return mIsDestinationPresent; }

    /**
     * Gets the destination PAN ID when it is present.
     *
     * @returns The destination PAN ID.
     */
    PanId GetDestination(void) const { return mDestination; }

    /**
     * Sets the source PAN ID.
     *
     * @param[in] aPanId  The source PAN ID.
     */
    void SetSource(PanId aPanId);

    /**
     * Sets the destination PAN ID.
     *
     * @param[in] aPanId  The source PAN ID.
     */
    void SetDestination(PanId aPanId);

    /**
     * Sets both source and destination PAN IDs to the same value.
     *
     * @param[in] aPanId  The PAN ID.
     */
    void SetBothSourceDestination(PanId aPanId);

private:
    PanId mSource;
    PanId mDestination;
    bool  mIsSourcePresent;
    bool  mIsDestinationPresent;
};

/**
 * Represents a MAC key.
 */
OT_TOOL_PACKED_BEGIN
class Key : public otMacKey, public Equatable<Key>, public Clearable<Key>
{
public:
    static constexpr uint16_t kSize = OT_MAC_KEY_SIZE; ///< Key size in bytes.

    /**
     * Gets a pointer to the bytes array containing the key
     *
     * @returns A pointer to the byte array containing the key.
     */
    const uint8_t *GetBytes(void) const { return m8; }

} OT_TOOL_PACKED_END;

/**
 * Represents a MAC Key Ref used by PSA.
 */
typedef otMacKeyRef KeyRef;

/**
 * Represents a MAC Key Material.
 */
class KeyMaterial : public otMacKeyMaterial, public Unequatable<KeyMaterial>
{
public:
    /**
     * Initializes a `KeyMaterial`.
     */
    KeyMaterial(void)
    {
        GetKey().Clear();
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
        SetKeyRef(kInvalidKeyRef);
#endif
    }

#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    /**
     * Overload `=` operator to assign the `KeyMaterial` from another one.
     *
     * If the `KeyMaterial` currently stores a valid and different `KeyRef`, the assignment of new value will ensure to
     * delete the previous one before using the new `KeyRef` from @p aOther.
     *
     * @param[in] aOther  aOther  The other `KeyMaterial` instance to assign from.
     *
     * @returns A reference to the current `KeyMaterial`
     */
    KeyMaterial &operator=(const KeyMaterial &aOther);

    KeyMaterial(const KeyMaterial &) = delete;
#endif

    /**
     *  This method clears the `KeyMaterial`.
     *
     * Under `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE`, if the `KeyMaterial` currently stores a valid previous
     * `KeyRef`, the `Clear()` call will ensure to delete the previous `KeyRef` and set it to `kInvalidKeyRef`.
     */
    void Clear(void);

#if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    /**
     * Gets the literal `Key`.
     *
     * @returns The literal `Key`
     */
    const Key &GetKey(void) const { return static_cast<const Key &>(mKeyMaterial.mKey); }

#else
    /**
     * Gets the stored `KeyRef`
     *
     * @returns The `KeyRef`
     */
    KeyRef GetKeyRef(void) const { return mKeyMaterial.mKeyRef; }
#endif

    /**
     * Sets the `KeyMaterial` from a given Key.
     *
     * If the `KeyMaterial` currently stores a valid `KeyRef`, the `SetFrom()` call will ensure to delete the previous
     * one before creating and using a new `KeyRef` associated with the new `Key`.
     *
     * @param[in] aKey           A reference to the new key.
     * @param[in] aIsExportable  Boolean indicating if the key is exportable (this is only applicable under
     *                           `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` config).
     */
    void SetFrom(const Key &aKey, bool aIsExportable = false);

    /**
     * Extracts the literal key from `KeyMaterial`
     *
     * @param[out] aKey  A reference to the output the key.
     */
    void ExtractKey(Key &aKey) const;

    /**
     * Converts `KeyMaterial` to a `Crypto::Key`.
     *
     * @param[out]  aCryptoKey  A reference to a `Crypto::Key` to populate.
     */
    void ConvertToCryptoKey(Crypto::Key &aCryptoKey) const;

    /**
     * Overloads operator `==` to evaluate whether or not two `KeyMaterial` instances are equal.
     *
     * @param[in]  aOther  The other `KeyMaterial` instance to compare with.
     *
     * @retval TRUE   If the two `KeyMaterial` instances are equal.
     * @retval FALSE  If the two `KeyMaterial` instances are not equal.
     */
    bool operator==(const KeyMaterial &aOther) const;

private:
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    static constexpr KeyRef kInvalidKeyRef = Crypto::Storage::kInvalidKeyRef;

    void DestroyKey(void);
    void SetKeyRef(KeyRef aKeyRef) { mKeyMaterial.mKeyRef = aKeyRef; }
#endif
    Key &GetKey(void) { return static_cast<Key &>(mKeyMaterial.mKey); }
    void SetKey(const Key &aKey) { mKeyMaterial.mKey = aKey; }
};

#if OPENTHREAD_CONFIG_MULTI_RADIO

/**
 * Defines the radio link types.
 */
enum RadioType : uint8_t
{
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
    kRadioTypeIeee802154, ///< IEEE 802.15.4 (2.4GHz) link type.
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    kRadioTypeTrel, ///< Thread Radio Encapsulation link type.
#endif
};

/**
 * This constant specifies the number of supported radio link types.
 */
constexpr uint8_t kNumRadioTypes = (((OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE) ? 1 : 0) +
                                    ((OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE) ? 1 : 0));

/**
 * Represents a set of radio links.
 */
class RadioTypes
{
public:
    static constexpr uint16_t kInfoStringSize = 32; ///< Max chars for the info string (`ToString()`).

    /**
     * Defines the fixed-length `String` object returned from `ToString()`.
     */
    typedef String<kInfoStringSize> InfoString;

    /**
     * This static class variable defines an array containing all supported radio link types.
     */
    static const RadioType kAllRadioTypes[kNumRadioTypes];

    /**
     * Initializes a `RadioTypes` object as empty set
     */
    RadioTypes(void)
        : mBitMask(0)
    {
    }

    /**
     * Initializes a `RadioTypes` object with a given bit-mask.
     *
     * @param[in] aMask   A bit-mask representing the radio types (the first bit corresponds to radio type 0, and so on)
     */
    explicit RadioTypes(uint8_t aMask)
        : mBitMask(aMask)
    {
    }

    /**
     * Clears the set.
     */
    void Clear(void) { mBitMask = 0; }

    /**
     * Indicates whether the set is empty or not
     *
     * @returns TRUE if the set is empty, FALSE otherwise.
     */
    bool IsEmpty(void) const { return (mBitMask == 0); }

    /**
     *  This method indicates whether the set contains only a single radio type.
     *
     * @returns TRUE if the set contains a single radio type, FALSE otherwise.
     */
    bool ContainsSingleRadio(void) const { return !IsEmpty() && ((mBitMask & (mBitMask - 1)) == 0); }

    /**
     * Indicates whether or not the set contains a given radio type.
     *
     * @param[in] aType  A radio link type.
     *
     * @returns TRUE if the set contains @p aType, FALSE otherwise.
     */
    bool Contains(RadioType aType) const { return ((mBitMask & BitFlag(aType)) != 0); }

    /**
     * Adds a radio type to the set.
     *
     * @param[in] aType  A radio link type.
     */
    void Add(RadioType aType) { mBitMask |= BitFlag(aType); }

    /**
     * Adds another radio types set to the current one.
     *
     * @param[in] aTypes   A radio link type set to add.
     */
    void Add(RadioTypes aTypes) { mBitMask |= aTypes.mBitMask; }

    /**
     * Adds all radio types supported by device to the set.
     */
    void AddAll(void);

    /**
     * Removes a given radio type from the set.
     *
     * @param[in] aType  A radio link type.
     */
    void Remove(RadioType aType) { mBitMask &= ~BitFlag(aType); }

    /**
     * Gets the radio type set as a bitmask.
     *
     * The first bit in the mask corresponds to first radio type (radio type with value zero), and so on.
     *
     * @returns A bitmask representing the set of radio types.
     */
    uint8_t GetAsBitMask(void) const { return mBitMask; }

    /**
     * Overloads operator `-` to return a new set which is the set difference between current set and
     * a given set.
     *
     * @param[in] aOther  Another radio type set.
     *
     * @returns A new set which is set difference between current one and @p aOther.
     */
    RadioTypes operator-(const RadioTypes &aOther) const { return RadioTypes(mBitMask & ~aOther.mBitMask); }

    /**
     * Converts the radio set to human-readable string.
     *
     * @return A string representation of the set of radio types.
     */
    InfoString ToString(void) const;

private:
    static uint8_t BitFlag(RadioType aType) { return static_cast<uint8_t>(1U << static_cast<uint8_t>(aType)); }

    uint8_t mBitMask;
};

/**
 * Converts a link type to a string
 *
 * @param[in] aRadioType  A link type value.
 *
 * @returns A string representation of the link type.
 */
const char *RadioTypeToString(RadioType aRadioType);

#endif // OPENTHREAD_CONFIG_MULTI_RADIO

/**
 * Represents Link Frame Counters for all supported radio links.
 */
class LinkFrameCounters
{
public:
    /**
     * Resets all counters (set them all to zero).
     */
    void Reset(void) { SetAll(0); }

#if OPENTHREAD_CONFIG_MULTI_RADIO

    /**
     * Gets the link Frame Counter for a given radio link.
     *
     * @param[in] aRadioType  A radio link type.
     *
     * @returns The Link Frame Counter for radio link @p aRadioType.
     */
    uint32_t Get(RadioType aRadioType) const;

    /**
     * Sets the Link Frame Counter for a given radio link.
     *
     * @param[in] aRadioType  A radio link type.
     * @param[in] aCounter    The new counter value.
     */
    void Set(RadioType aRadioType, uint32_t aCounter);

#else

    /**
     * Gets the Link Frame Counter value.
     *
     * @return The Link Frame Counter value.
     */
    uint32_t Get(void) const
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
    {
        return m154Counter;
    }
#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    {
        return mTrelCounter;
    }
#endif

    /**
     * Sets the Link Frame Counter for a given radio link.
     *
     * @param[in] aCounter    The new counter value.
     */
    void Set(uint32_t aCounter)
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
    {
        m154Counter = aCounter;
    }
#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    {
        mTrelCounter = aCounter;
    }
#endif

#endif // OPENTHREAD_CONFIG_MULTI_RADIO

#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
    /**
     * Gets the Link Frame Counter for 802.15.4 radio link.
     *
     * @returns The Link Frame Counter for 802.15.4 radio link.
     */
    uint32_t Get154(void) const { return m154Counter; }

    /**
     * Sets the Link Frame Counter for 802.15.4 radio link.
     *
     * @param[in] aCounter   The new counter value.
     */
    void Set154(uint32_t aCounter) { m154Counter = aCounter; }
#endif

#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    /**
     * Gets the Link Frame Counter for TREL radio link.
     *
     * @returns The Link Frame Counter for TREL radio link.
     */
    uint32_t GetTrel(void) const { return mTrelCounter; }

    /**
     * Increments the Link Frame Counter for TREL radio link.
     */
    void IncrementTrel(void) { mTrelCounter++; }
#endif

    /**
     * Gets the maximum Link Frame Counter among all supported radio links.
     *
     * @return The maximum Link frame Counter among all supported radio links.
     */
    uint32_t GetMaximum(void) const;

    /**
     * Sets the Link Frame Counter value for all radio links.
     *
     * @param[in]  aCounter  The Link Frame Counter value.
     */
    void SetAll(uint32_t aCounter);

private:
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
    uint32_t m154Counter;
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    uint32_t mTrelCounter;
#endif
};

/**
 * Represents CSL accuracy.
 */
class CslAccuracy
{
public:
    static constexpr uint8_t kWorstClockAccuracy = 255; ///< Worst possible crystal accuracy, in units of ± ppm.
    static constexpr uint8_t kWorstUncertainty   = 255; ///< Worst possible uncertainty, in units of 10 microseconds.

    /**
     * Initializes the CSL accuracy using `kWorstClockAccuracy` and `kWorstUncertainty` values.
     */
    void Init(void)
    {
        mClockAccuracy = kWorstClockAccuracy;
        mUncertainty   = kWorstUncertainty;
    }

    /**
     * Returns the CSL clock accuracy.
     *
     * @returns The CSL clock accuracy in ± ppm.
     */
    uint8_t GetClockAccuracy(void) const { return mClockAccuracy; }

    /**
     * Sets the CSL clock accuracy.
     *
     * @param[in]  aClockAccuracy  The CSL clock accuracy in ± ppm.
     */
    void SetClockAccuracy(uint8_t aClockAccuracy) { mClockAccuracy = aClockAccuracy; }

    /**
     * Returns the CSL uncertainty.
     *
     * @returns The uncertainty in units 10 microseconds.
     */
    uint8_t GetUncertainty(void) const { return mUncertainty; }

    /**
     * Gets the CLS uncertainty in microseconds.
     *
     * @returns the CLS uncertainty in microseconds.
     */
    uint16_t GetUncertaintyInMicrosec(void) const { return static_cast<uint16_t>(mUncertainty) * kUsPerUncertUnit; }

    /**
     * Sets the CSL uncertainty.
     *
     * @param[in]  aUncertainty  The CSL uncertainty in units 10 microseconds.
     */
    void SetUncertainty(uint8_t aUncertainty) { mUncertainty = aUncertainty; }

private:
    static constexpr uint8_t kUsPerUncertUnit = 10;

    uint8_t mClockAccuracy;
    uint8_t mUncertainty;
};

#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
/**
 * Gets the length of the wake-up identifier.
 *
 * The length is the number of bytes remaining after removing the most significant zero bytes.
 *
 * @param[in]  aWakeupId  The wake-up identifier.
 *
 * @returns The length of the @p aWakeupId.
 */
uint8_t GetWakeupIdLength(WakeupId aWakeupId);
#endif

#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
/**
 * Represents a wake-up request.
 */
class WakeupRequest : public otWakeupRequest
{
public:
    /**
     * Represents a wake-up request type.
     */
    enum Type : uint8_t
    {
        kTypeExtAddress    = OT_WAKEUP_TYPE_EXT_ADDRESS,
        kTypeWakeupId      = OT_WAKEUP_TYPE_IDENTIFIER,
        kTypeGroupWakeupId = OT_WAKEUP_TYPE_GROUP_IDENTIFIER,
    };

    /**
     * Sets the wake-up request with an Extended Address.
     *
     * The type is also updated to indicate that the wake-up request type is `kTypeExtAddress`.
     *
     * @param[in]  aExtAddress  An Extended Address.
     */
    void SetExtAddress(const ExtAddress &aExtAddress);

    /**
     * Gets the Extended Address of the wake-up request.
     *
     * MUST be used only if the wake-up request type is `kTypeExtAddress`.
     *
     * @returns A constant reference to the Extended Address.
     */
    const ExtAddress &GetExtAddress(void) const;

    /**
     * Gets the Extended Address of the wake-up request.
     *
     * MUST be used only if the wake-up request type is `kTypeExtAddress`.
     *
     * @returns A reference to the Extended Address.
     */
    ExtAddress &GetExtAddress(void);

    /**
     * Gets the Wake-up Identifier of the wake-up request.
     *
     * MUST be used only if the wake-up request type is `kTypeWakeupId` or `kTypeGroupWakeupId`.
     *
     * @returns The Wake-up Identifier.
     */
    WakeupId GetWakeupId(void) const { return mShared.mWakeupId; }

    /**
     * Sets the wake-up request with the Wake-up Identifier.
     *
     * The type is also updated to indicate that the wake-up request type is `kTypeWakeupId`.
     *
     * @param[in]  aWakeupId  A Wake-up Identifier.
     */
    void SetWakeupId(WakeupId aWakeupId)
    {
        SetType(kTypeWakeupId);
        mShared.mWakeupId = aWakeupId;
    }

    /**
     * Sets the wake-up request type.
     *
     * @param[in]  aType  The wake-up request type.
     */
    void SetType(Type aType);

    /**
     * Indicates whether the peer is set to be woken up by the extended address.
     *
     * @retval TRUE   If the peer is set to be woken up by the extended address.
     * @retval FALSE  If the peer is not not set to be woken up by the extended address.
     */
    bool IsWakeupByExtAddress(void) const;

    /**
     * Indicates whether the peer is set to be woken up by the wake-up identifier.
     *
     * @retval TRUE   If the peer is set to be woken up by the wake-up identifier.
     * @retval FALSE  If the peer is not set to be woken up by the wake-up identifier.
     */
    bool IsWakeupById(void) const;

    /**
     * Indicates whether the peer is set to be woken up by the group wake-up identifier.
     *
     * @retval TRUE   If the peer is set to be woken up by the group wake-up identifier.
     * @retval FALSE  If the peer is not set to be woken up by the group wake-up identifier.
     */
    bool IsWakeupByGroupId(void) const;
};
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE

#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
/**
 * Represents the information of the received wake-up frame.
 */
struct WakeupInfo
{
    ExtAddress mExtAddress;        ///< The extended address of the Wake-up Coordinator.
    uint32_t   mAttachDelayMs;     ///< The delay before linking to the peer.
    uint8_t    mRetryInterval : 2; ///< The interval of the periodic connection windows.
    uint8_t    mRetryCount : 4;    ///< The maximum number of retries the action by the Wake-up Listener.
};
#endif

/**
 * @}
 */

} // namespace Mac

DefineCoreType(otExtAddress, Mac::ExtAddress);
DefineCoreType(otMacKey, Mac::Key);
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
DefineCoreType(otWakeupRequest, Mac::WakeupRequest);
DefineMapEnum(otWakeupType, Mac::WakeupRequest::Type);
#endif

} // namespace ot

#endif // OT_CORE_MAC_MAC_TYPES_HPP_
