/*
 * Copyright (c) 2005,2006,2007 INRIA
 * Copyright (c) 2020 Orange Labs
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 *          Rediet <getachew.redieteab@orange.com>
 */

#ifndef WIFI_PHY_COMMON_H
#define WIFI_PHY_COMMON_H

#include "wifi-spectrum-value-helper.h"
#include "wifi-standards.h"
#include "wifi-types.h"

#include "ns3/fatal-error.h"
#include "ns3/ptr.h"

#include <ostream>
#include <set>
#include <vector>

/**
 * @file
 * @ingroup wifi
 * Declaration of the following enums:
 * - ns3::WifiPreamble
 * - ns3::WifiModulationClass
 * - ns3::WifiPpduField
 * - ns3::WifiPpduType
 * - ns3::WifiPhyRxfailureReason
 */

namespace ns3
{

class WifiNetDevice;
class WifiMode;
class Time;

/**
 * typedef for a pair of start and stop frequencies to represent a band
 */
using WifiSpectrumBandFrequencies = std::pair<Hz_u, Hz_u>;

/// WifiSpectrumBandInfo structure containing info about a spectrum band
struct WifiSpectrumBandInfo
{
    std::vector<WifiSpectrumBandIndices>
        indices; //!< the start and stop indices for each segment of the band
    std::vector<WifiSpectrumBandFrequencies>
        frequencies; //!< the start and stop frequencies for each segment of the band
};

/// vector of spectrum bands
using WifiSpectrumBands = std::vector<WifiSpectrumBandInfo>;

/// A map of the received power for each band
using RxPowerWattPerChannelBand = std::map<WifiSpectrumBandInfo, Watt_u>;

/**
 * @ingroup wifi
 * Compare two bands.
 *
 * @param lhs the band on the left of operator<
 * @param rhs the band on the right of operator<
 * @return true if the start/stop frequencies of the first segment of left are lower than the
 * start/stop frequencies of the first segment of right. If the first segment is the same for left
 * and right, it return true if the start/stop frequencies of the second segment of left are lower
 * than the start/stop frequencies of the second segment of right. Otherwise, the function return
 * false.
 */
inline bool
operator<(const WifiSpectrumBandInfo& lhs, const WifiSpectrumBandInfo& rhs)
{
    if (lhs.frequencies.front() == rhs.frequencies.front())
    {
        return lhs.frequencies.back() < rhs.frequencies.back();
    }
    return lhs.frequencies.front() < rhs.frequencies.front();
}

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param band the band
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiSpectrumBandInfo& band)
{
    NS_ASSERT(band.indices.size() == band.frequencies.size());
    for (std::size_t segmentIndex = 0; segmentIndex < band.indices.size(); ++segmentIndex)
    {
        os << "indices segment" << segmentIndex << ": [" << band.indices.at(segmentIndex).first
           << "-" << band.indices.at(segmentIndex).second << "], frequencies segment"
           << segmentIndex << ": [" << band.frequencies.at(segmentIndex).first << "Hz-"
           << band.frequencies.at(segmentIndex).second << "Hz] ";
    }
    return os;
}

/**
 * These constants define the various convolutional coding rates
 * used for the OFDM transmission modes in the IEEE 802.11
 * standard. DSSS (for example) rates which do not have an explicit
 * coding stage in their generation should have this parameter set to
 * WIFI_CODE_RATE_UNDEFINED.
 */
enum WifiCodeRate : uint16_t
{
    WIFI_CODE_RATE_UNDEFINED, //!< undefined coding rate
    WIFI_CODE_RATE_1_2,       //!< 1/2 coding rate
    WIFI_CODE_RATE_2_3,       //!< 2/3 coding rate
    WIFI_CODE_RATE_3_4,       //!< 3/4 coding rate
    WIFI_CODE_RATE_5_6,       //!< 5/6 coding rate
    WIFI_CODE_RATE_5_8,       //!< 5/8 coding rate
    WIFI_CODE_RATE_13_16,     //!< 13/16 coding rate
    WIFI_CODE_RATE_1_4,       //!< 1/4 coding rate
    WIFI_CODE_RATE_13_28,     //!< 13/28 coding rate
    WIFI_CODE_RATE_13_21,     //!< 13/21 coding rate
    WIFI_CODE_RATE_52_63,     //!< 52/63 coding rate
    WIFI_CODE_RATE_13_14,     //!< 13/14 coding rate
    WIFI_CODE_RATE_7_8,       //!< 7/8 coding rate
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param codeRate the code rate
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiCodeRate& codeRate)
{
    switch (codeRate)
    {
    case WIFI_CODE_RATE_UNDEFINED:
        return (os << "Code rate undefined");
    case WIFI_CODE_RATE_1_2:
        return (os << "Code rate 1/2");
    case WIFI_CODE_RATE_2_3:
        return (os << "Code rate 2/3");
    case WIFI_CODE_RATE_3_4:
        return (os << "Code rate 3/4");
    case WIFI_CODE_RATE_5_6:
        return (os << "Code rate 5/6");
    case WIFI_CODE_RATE_5_8:
        return (os << "Code rate 5/8");
    case WIFI_CODE_RATE_13_16:
        return (os << "Code rate 13/16");
    case WIFI_CODE_RATE_1_4:
        return (os << "Code rate 1/4");
    case WIFI_CODE_RATE_13_28:
        return (os << "Code rate 13/28");
    case WIFI_CODE_RATE_13_21:
        return (os << "Code rate 13/21");
    case WIFI_CODE_RATE_52_63:
        return (os << "Code rate 52/63");
    case WIFI_CODE_RATE_13_14:
        return (os << "Code rate 13/14");
    case WIFI_CODE_RATE_7_8:
        return (os << "Code rate 7/8");
    default:
        NS_FATAL_ERROR("Unknown code rate");
        return (os << "Unknown");
    }
}

/**
 * @ingroup wifi
 * The type of preamble to be used by an IEEE 802.11 transmission
 */
enum WifiPreamble
{
    WIFI_PREAMBLE_LONG,
    WIFI_PREAMBLE_SHORT,
    WIFI_PREAMBLE_HT_MF,
    WIFI_PREAMBLE_VHT_SU,
    WIFI_PREAMBLE_VHT_MU,
    WIFI_PREAMBLE_DMG_CTRL,
    WIFI_PREAMBLE_DMG_SC,
    WIFI_PREAMBLE_DMG_OFDM,
    WIFI_PREAMBLE_HE_SU,
    WIFI_PREAMBLE_HE_ER_SU,
    WIFI_PREAMBLE_HE_MU,
    WIFI_PREAMBLE_HE_TB,
    WIFI_PREAMBLE_EHT_MU,
    WIFI_PREAMBLE_EHT_TB
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param preamble the preamble
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiPreamble& preamble)
{
    switch (preamble)
    {
    case WIFI_PREAMBLE_LONG:
        return (os << "LONG");
    case WIFI_PREAMBLE_SHORT:
        return (os << "SHORT");
    case WIFI_PREAMBLE_HT_MF:
        return (os << "HT_MF");
    case WIFI_PREAMBLE_VHT_SU:
        return (os << "VHT_SU");
    case WIFI_PREAMBLE_VHT_MU:
        return (os << "VHT_MU");
    case WIFI_PREAMBLE_DMG_CTRL:
        return (os << "DMG_CTRL");
    case WIFI_PREAMBLE_DMG_SC:
        return (os << "DMG_SC");
    case WIFI_PREAMBLE_DMG_OFDM:
        return (os << "DMG_OFDM");
    case WIFI_PREAMBLE_HE_SU:
        return (os << "HE_SU");
    case WIFI_PREAMBLE_HE_ER_SU:
        return (os << "HE_ER_SU");
    case WIFI_PREAMBLE_HE_MU:
        return (os << "HE_MU");
    case WIFI_PREAMBLE_HE_TB:
        return (os << "HE_TB");
    case WIFI_PREAMBLE_EHT_MU:
        return (os << "EHT_MU");
    case WIFI_PREAMBLE_EHT_TB:
        return (os << "EHT_TB");
    default:
        NS_FATAL_ERROR("Invalid preamble");
        return (os << "INVALID");
    }
}

/**
 * @ingroup wifi
 * This enumeration defines the modulation classes per
 * (Table 10-6 "Modulation classes"; IEEE 802.11-2016, with
 * updated in 802.11ax/D6.0 as Table 10-9).
 */
enum WifiModulationClass
{
    /** Modulation class unknown or unspecified. A WifiMode with this
    WifiModulationClass has not been properly initialized. */
    WIFI_MOD_CLASS_UNKNOWN = 0,
    WIFI_MOD_CLASS_DSSS,      //!< DSSS (Clause 15)
    WIFI_MOD_CLASS_HR_DSSS,   //!< HR/DSSS (Clause 16)
    WIFI_MOD_CLASS_ERP_OFDM,  //!< ERP-OFDM (18.4)
    WIFI_MOD_CLASS_OFDM,      //!< OFDM (Clause 17)
    WIFI_MOD_CLASS_HT,        //!< HT (Clause 19)
    WIFI_MOD_CLASS_VHT,       //!< VHT (Clause 22)
    WIFI_MOD_CLASS_DMG_CTRL,  //!< DMG (Clause 21)
    WIFI_MOD_CLASS_DMG_OFDM,  //!< DMG (Clause 21)
    WIFI_MOD_CLASS_DMG_SC,    //!< DMG (Clause 21)
    WIFI_MOD_CLASS_DMG_LP_SC, //!< DMG (Clause 21)
    WIFI_MOD_CLASS_HE,        //!< HE (Clause 27)
    WIFI_MOD_CLASS_EHT        //!< EHT (Clause 36)
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param modulation the WifiModulationClass
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiModulationClass& modulation)
{
    switch (modulation)
    {
    case WIFI_MOD_CLASS_DSSS:
        return (os << "DSSS");
    case WIFI_MOD_CLASS_HR_DSSS:
        return (os << "HR/DSSS");
    case WIFI_MOD_CLASS_ERP_OFDM:
        return (os << "ERP-OFDM");
    case WIFI_MOD_CLASS_OFDM:
        return (os << "OFDM");
    case WIFI_MOD_CLASS_HT:
        return (os << "HT");
    case WIFI_MOD_CLASS_VHT:
        return (os << "VHT");
    case WIFI_MOD_CLASS_DMG_CTRL:
        return (os << "DMG_CTRL");
    case WIFI_MOD_CLASS_DMG_OFDM:
        return (os << "DMG_OFDM");
    case WIFI_MOD_CLASS_DMG_SC:
        return (os << "DMG_SC");
    case WIFI_MOD_CLASS_DMG_LP_SC:
        return (os << "DMG_LP_SC");
    case WIFI_MOD_CLASS_HE:
        return (os << "HE");
    case WIFI_MOD_CLASS_EHT:
        return (os << "EHT");
    default:
        NS_FATAL_ERROR("Unknown modulation");
        return (os << "unknown");
    }
}

/**
 * @ingroup wifi
 * The type of PPDU field (grouped for convenience)
 */
enum WifiPpduField
{
    /**
     * SYNC + SFD fields for DSSS or ERP,
     * shortSYNC + shortSFD fields for HR/DSSS or ERP,
     * HT-GF-STF + HT-GF-LTF1 fields for HT-GF,
     * L-STF + L-LTF fields otherwise.
     */
    WIFI_PPDU_FIELD_PREAMBLE = 0,
    /**
     * PHY header field for DSSS or ERP,
     * short PHY header field for HR/DSSS or ERP,
     * field not present for HT-GF,
     * L-SIG field or L-SIG + RL-SIG fields otherwise.
     */
    WIFI_PPDU_FIELD_NON_HT_HEADER,
    WIFI_PPDU_FIELD_HT_SIG,   //!< HT-SIG field
    WIFI_PPDU_FIELD_TRAINING, //!< STF + LTF fields (excluding those in preamble for HT-GF)
    WIFI_PPDU_FIELD_SIG_A,    //!< SIG-A field
    WIFI_PPDU_FIELD_SIG_B,    //!< SIG-B field
    WIFI_PPDU_FIELD_U_SIG,    //!< U-SIG field
    WIFI_PPDU_FIELD_EHT_SIG,  //!< EHT-SIG field
    WIFI_PPDU_FIELD_DATA      //!< data field
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param field the PPDU field
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiPpduField& field)
{
    switch (field)
    {
    case WIFI_PPDU_FIELD_PREAMBLE:
        return (os << "preamble");
    case WIFI_PPDU_FIELD_NON_HT_HEADER:
        return (os << "non-HT header");
    case WIFI_PPDU_FIELD_HT_SIG:
        return (os << "HT-SIG");
    case WIFI_PPDU_FIELD_TRAINING:
        return (os << "training");
    case WIFI_PPDU_FIELD_SIG_A:
        return (os << "SIG-A");
    case WIFI_PPDU_FIELD_SIG_B:
        return (os << "SIG-B");
    case WIFI_PPDU_FIELD_U_SIG:
        return (os << "U-SIG");
    case WIFI_PPDU_FIELD_EHT_SIG:
        return (os << "EHT-SIG");
    case WIFI_PPDU_FIELD_DATA:
        return (os << "data");
    default:
        NS_FATAL_ERROR("Unknown field");
        return (os << "unknown");
    }
}

/**
 * @ingroup wifi
 * The type of PPDU (SU, DL MU, or UL MU)
 */
enum WifiPpduType
{
    WIFI_PPDU_TYPE_SU = 0,
    WIFI_PPDU_TYPE_DL_MU,
    WIFI_PPDU_TYPE_UL_MU
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param type the PPDU type
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiPpduType& type)
{
    switch (type)
    {
    case WIFI_PPDU_TYPE_SU:
        return (os << "SU");
    case WIFI_PPDU_TYPE_DL_MU:
        return (os << "DL MU");
    case WIFI_PPDU_TYPE_UL_MU:
        return (os << "UL MU");
    default:
        NS_FATAL_ERROR("Unknown type");
        return (os << "unknown");
    }
}

/**
 * @ingroup wifi
 * Enumeration of the possible reception failure reasons.
 */
enum WifiPhyRxfailureReason
{
    UNKNOWN = 0,
    UNSUPPORTED_SETTINGS,
    CHANNEL_SWITCHING,
    RXING,
    TXING,
    SLEEPING,
    POWERED_OFF,
    TRUNCATED_TX,
    BUSY_DECODING_PREAMBLE,
    PREAMBLE_DETECT_FAILURE,
    RECEPTION_ABORTED_BY_TX,
    L_SIG_FAILURE,
    HT_SIG_FAILURE,
    SIG_A_FAILURE,
    SIG_B_FAILURE,
    U_SIG_FAILURE,
    EHT_SIG_FAILURE,
    PREAMBLE_DETECTION_PACKET_SWITCH,
    FRAME_CAPTURE_PACKET_SWITCH,
    OBSS_PD_CCA_RESET,
    PPDU_TOO_LATE,
    FILTERED,
    DMG_HEADER_FAILURE,
    DMG_ALLOCATION_ENDED,
    SIGNAL_DETECTION_ABORTED_BY_TX
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param reason the failure reason
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, const WifiPhyRxfailureReason& reason)
{
    switch (reason)
    {
    case UNSUPPORTED_SETTINGS:
        return (os << "UNSUPPORTED_SETTINGS");
    case CHANNEL_SWITCHING:
        return (os << "CHANNEL_SWITCHING");
    case RXING:
        return (os << "RXING");
    case TXING:
        return (os << "TXING");
    case SLEEPING:
        return (os << "SLEEPING");
    case POWERED_OFF:
        return (os << "OFF");
    case TRUNCATED_TX:
        return (os << "TRUNCATED_TX");
    case BUSY_DECODING_PREAMBLE:
        return (os << "BUSY_DECODING_PREAMBLE");
    case PREAMBLE_DETECT_FAILURE:
        return (os << "PREAMBLE_DETECT_FAILURE");
    case RECEPTION_ABORTED_BY_TX:
        return (os << "RECEPTION_ABORTED_BY_TX");
    case L_SIG_FAILURE:
        return (os << "L_SIG_FAILURE");
    case HT_SIG_FAILURE:
        return (os << "HT_SIG_FAILURE");
    case SIG_A_FAILURE:
        return (os << "SIG_A_FAILURE");
    case SIG_B_FAILURE:
        return (os << "SIG_B_FAILURE");
    case U_SIG_FAILURE:
        return (os << "U_SIG_FAILURE");
    case EHT_SIG_FAILURE:
        return (os << "EHT_SIG_FAILURE");
    case PREAMBLE_DETECTION_PACKET_SWITCH:
        return (os << "PREAMBLE_DETECTION_PACKET_SWITCH");
    case FRAME_CAPTURE_PACKET_SWITCH:
        return (os << "FRAME_CAPTURE_PACKET_SWITCH");
    case OBSS_PD_CCA_RESET:
        return (os << "OBSS_PD_CCA_RESET");
    case PPDU_TOO_LATE:
        return (os << "PPDU_TOO_LATE");
    case FILTERED:
        return (os << "FILTERED");
    case DMG_HEADER_FAILURE:
        return (os << "DMG_HEADER_FAILURE");
    case DMG_ALLOCATION_ENDED:
        return (os << "DMG_ALLOCATION_ENDED");
    case SIGNAL_DETECTION_ABORTED_BY_TX:
        return (os << "SIGNAL_DETECTION_ABORTED_BY_TX");
    case UNKNOWN:
    default:
        NS_FATAL_ERROR("Unknown reason");
        return (os << "UNKNOWN");
    }
}

/**
 * @ingroup wifi
 * Enumeration of the possible channel-list parameter elements
 * defined in Table 8-5 of IEEE 802.11-2016.
 */
enum WifiChannelListType : uint8_t
{
    WIFI_CHANLIST_PRIMARY = 0,
    WIFI_CHANLIST_SECONDARY,
    WIFI_CHANLIST_SECONDARY40,
    WIFI_CHANLIST_SECONDARY80,
    WIFI_CHANLIST_SECONDARY160
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param type the wifi channel list type
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, WifiChannelListType type)
{
    switch (type)
    {
    case WIFI_CHANLIST_PRIMARY:
        return (os << "PRIMARY");
    case WIFI_CHANLIST_SECONDARY:
        return (os << "SECONDARY");
    case WIFI_CHANLIST_SECONDARY40:
        return (os << "SECONDARY40");
    case WIFI_CHANLIST_SECONDARY80:
        return (os << "SECONDARY80");
    case WIFI_CHANLIST_SECONDARY160:
        return (os << "SECONDARY160");
    default:
        NS_FATAL_ERROR("Unknown wifi channel type " << +type);
        return (os << "UNKNOWN");
    }
}

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param width the wifi channel width type
 * @returns a reference to the stream
 */
inline std::ostream&
operator<<(std::ostream& os, WifiChannelWidthType width)
{
    switch (width)
    {
    case WifiChannelWidthType::CW_20MHZ:
        return (os << "20MHz");
    case WifiChannelWidthType::CW_22MHZ:
        return (os << "22MHz");
    case WifiChannelWidthType::CW_5MHZ:
        return (os << "5MHz");
    case WifiChannelWidthType::CW_10MHZ:
        return (os << "10MHz");
    case WifiChannelWidthType::CW_40MHZ:
        return (os << "40MHz");
    case WifiChannelWidthType::CW_80MHZ:
        return (os << "80MHz");
    case WifiChannelWidthType::CW_160MHZ:
        return (os << "160MHz");
    case WifiChannelWidthType::CW_80_PLUS_80MHZ:
        return (os << "80+80MHz");
    case WifiChannelWidthType::CW_320MHZ:
        return (os << "320MHz");
    case WifiChannelWidthType::CW_2160MHZ:
        return (os << "2160MHz");
    default:
    case WifiChannelWidthType::MAX:
        return (os << "UNKNOWN");
    }
}

/**
 * Get the guard interval for a given WifiMode.
 *
 * @param mode the WifiMode
 * @param device pointer to the WifiNetDevice object
 *
 * @return the guard interval duration to use for the mode
 */
Time GetGuardIntervalForMode(WifiMode mode, const Ptr<WifiNetDevice> device);

/**
 * Get the guard interval for a given WifiMode.
 *
 * @param mode the WifiMode
 * @param htShortGuardInterval whether HT/VHT short guard interval is enabled
 * @param heGuardInterval the HE guard interval duration
 *
 * @return the guard interval duration to use for the mode
 */
Time GetGuardIntervalForMode(WifiMode mode, bool htShortGuardInterval, Time heGuardInterval);

/**
 * Return the preamble to be used for the transmission.
 *
 * @param modulation the modulation selected for the transmission
 * @param useShortPreamble whether short preamble should be used
 *
 * @return the preamble to be used for the transmission
 */
WifiPreamble GetPreambleForTransmission(WifiModulationClass modulation,
                                        bool useShortPreamble = false);

/**
 * Return the modulation class corresponding to the given preamble type.
 * Only preamble types used by HT/VHT/HE/EHT can be passed to this function.
 *
 * @param preamble the given preamble type (must be one defined by HT standard or later)
 * @return the modulation class corresponding to the given preamble type
 */
WifiModulationClass GetModulationClassForPreamble(WifiPreamble preamble);

/**
 * Return whether the modulation class of the selected mode for the
 * control answer frame is allowed.
 *
 * @param modClassReq modulation class of the request frame
 * @param modClassAnswer modulation class of the answer frame
 *
 * @return true if the modulation class of the selected mode for the
 * control answer frame is allowed, false otherwise
 */
bool IsAllowedControlAnswerModulationClass(WifiModulationClass modClassReq,
                                           WifiModulationClass modClassAnswer);

/**
 * Get the maximum PPDU duration (see Section 10.14 of 802.11-2016) for
 * the PHY layers defining the aPPDUMaxTime characteristic (HT, VHT and HE).
 * Return zero otherwise.
 *
 * @param preamble the preamble type
 *
 * @return the maximum PPDU duration, if defined, and zero otherwise
 */
Time GetPpduMaxTime(WifiPreamble preamble);

/**
 * Return true if a preamble corresponds to a multi-user transmission.
 *
 * @param preamble the preamble
 * @return true if the provided preamble corresponds to a multi-user transmission
 */
bool IsMu(WifiPreamble preamble);

/**
 * Return true if a preamble corresponds to a downlink multi-user transmission.
 *
 * @param preamble the preamble
 * @return true if the provided preamble corresponds to a downlink multi-user transmission
 */
bool IsDlMu(WifiPreamble preamble);

/**
 * Return true if a preamble corresponds to a uplink multi-user transmission.
 *
 * @param preamble the preamble
 * @return true if the provided preamble corresponds to a uplink multi-user transmission
 */
bool IsUlMu(WifiPreamble preamble);

/**
 * Return the modulation class corresponding to a given standard.
 *
 * In the case of WIFI_STANDARD_80211b, two modulation classes are supported
 * (WIFI_MOD_CLASS_DSSS and WIFI_MOD_CLASS_HR_DSSS); this method will return
 * the latter.
 *
 * @param standard the standard
 * @return the modulation class corresponding to the standard
 */
WifiModulationClass GetModulationClassForStandard(WifiStandard standard);

/**
 * Get the supported channel width set that can be advertised in PHY capabilities.
 *
 * @param standard the standard
 * @param band the PHY band
 * @return the supported channel width set that can be advertised for the given standard and band
 */
std::set<MHz_u> GetSupportedChannelWidthSet(WifiStandard standard, WifiPhyBand band);

/**
 * Get the maximum channel width allowed for the given modulation class.
 *
 * @param modulation the modulation class
 * @return the maximum channel width allowed for the given modulation class
 */
MHz_u GetMaximumChannelWidth(WifiModulationClass modulation);

/**
 * Get the total channel width for the channel width type.
 *
 * @param width the channel width type
 * @return the total channel width for the channel width type
 */
MHz_u GetChannelWidthInMhz(WifiChannelWidthType width);

/**
 * Return true if a preamble corresponds to an EHT transmission.
 *
 * @param preamble the preamble
 * @return true if the provided preamble corresponds to an EHT transmission
 */
bool IsEht(WifiPreamble preamble);

/**
 * @brief map a given channel list type to the corresponding scaling factor
 */
const std::map<WifiChannelListType, dBm_u> channelTypeToScalingFactor{
    {WIFI_CHANLIST_PRIMARY, 0.0},
    {WIFI_CHANLIST_SECONDARY, 0.0},
    {WIFI_CHANLIST_SECONDARY40, 3.0},
    {WIFI_CHANLIST_SECONDARY80, 6.0},
    {WIFI_CHANLIST_SECONDARY160, 12.0},
};

/**
 * A struct for both SNR and PER
 */
struct SnrPer
{
    double snr{0.0}; ///< SNR in linear scale
    double per{1.0}; ///< PER

    /**
     * Default constructor.
     */
    SnrPer()
    {
    }

    /**
     * Constructor for SnrPer.
     *
     * @param s the SNR in linear scale
     * @param p the PER
     */
    SnrPer(double s, double p)
        : snr(s),
          per(p)
    {
    }
};

/**
 * A pair containing information on the PHY header chunk, namely
 * the start and stop times of the chunk and the WifiMode used.
 */
typedef std::pair<std::pair<Time /* start */, Time /* stop */>, WifiMode> PhyHeaderChunkInfo;
/**
 * A map of PhyHeaderChunkInfo elements per PPDU field.
 * @see PhyHeaderChunkInfo
 */
typedef std::map<WifiPpduField, PhyHeaderChunkInfo> PhyHeaderSections;

} // namespace ns3

#endif /* WIFI_PHY_COMMON_H */
