/*
 * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Marco Miozzo  <marco.miozzo@cttc.es>
 *         Nicola Baldo  <nbaldo@cttc.es>
 * Modified by:
 *          Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015)
 *          Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
 */

#ifndef LTE_ENB_MAC_H
#define LTE_ENB_MAC_H

#include "ff-mac-csched-sap.h"
#include "ff-mac-sched-sap.h"
#include "lte-ccm-mac-sap.h"
#include "lte-common.h"
#include "lte-enb-cmac-sap.h"
#include "lte-enb-phy-sap.h"
#include "lte-mac-sap.h"

#include <ns3/nstime.h>
#include <ns3/packet-burst.h>
#include <ns3/packet.h>
#include <ns3/trace-source-accessor.h>
#include <ns3/traced-value.h>

#include <map>
#include <vector>

namespace ns3
{

class DlCqiLteControlMessage;
class UlCqiLteControlMessage;
class PdcchMapLteControlMessage;

/// DlHarqProcessesBuffer_t typedef
typedef std::vector<std::vector<Ptr<PacketBurst>>> DlHarqProcessesBuffer_t;

/**
 * This class implements the MAC layer of the eNodeB device
 */
class LteEnbMac : public Object
{
    /// allow EnbMacMemberLteEnbCmacSapProvider class friend access
    friend class EnbMacMemberLteEnbCmacSapProvider;
    /// allow EnbMacMemberLteMacSapProvider<LteEnbMac> class friend access
    friend class EnbMacMemberLteMacSapProvider<LteEnbMac>;
    /// allow EnbMacMemberFfMacSchedSapUser class friend access
    friend class EnbMacMemberFfMacSchedSapUser;
    /// allow EnbMacMemberFfMacCschedSapUser class friend access
    friend class EnbMacMemberFfMacCschedSapUser;
    /// allow EnbMacMemberLteEnbPhySapUser class friend access
    friend class EnbMacMemberLteEnbPhySapUser;
    /// allow MemberLteCcmMacSapProvider<LteEnbMac> class friend access
    friend class MemberLteCcmMacSapProvider<LteEnbMac>;

  public:
    /**
     * \brief Get the type ID.
     * \return the object TypeId
     */
    static TypeId GetTypeId();

    LteEnbMac();
    ~LteEnbMac() override;
    void DoDispose() override;

    /**
     * \brief Set the component carrier ID
     * \param index the component carrier ID
     */
    void SetComponentCarrierId(uint8_t index);
    /**
     * \brief Set the scheduler SAP provider
     * \param s a pointer SAP provider of the FF packet scheduler
     */
    void SetFfMacSchedSapProvider(FfMacSchedSapProvider* s);
    /**
     * \brief Get the scheduler SAP user
     * \return a pointer to the SAP user of the scheduler
     */
    FfMacSchedSapUser* GetFfMacSchedSapUser();
    /**
     * \brief Set the control scheduler SAP provider
     * \param s a pointer to the control scheduler SAP provider
     */
    void SetFfMacCschedSapProvider(FfMacCschedSapProvider* s);
    /**
     * \brief Get the control scheduler SAP user
     * \return a pointer to the control scheduler SAP user
     */
    FfMacCschedSapUser* GetFfMacCschedSapUser();

    /**
     * \brief Set the MAC SAP user
     * \param s a pointer to the MAC SAP user
     */
    void SetLteMacSapUser(LteMacSapUser* s);
    /**
     * \brief Get the MAC SAP provider
     * \return a pointer to the SAP provider of the MAC
     */
    LteMacSapProvider* GetLteMacSapProvider();
    /**
     * \brief Set the control MAC SAP user
     * \param s a pointer to the control MAC SAP user
     */
    void SetLteEnbCmacSapUser(LteEnbCmacSapUser* s);
    /**
     * \brief Get the control MAC SAP provider
     * \return a pointer to the control MAC SAP provider
     */
    LteEnbCmacSapProvider* GetLteEnbCmacSapProvider();

    /**
     * \brief Get the eNB-PHY SAP User
     * \return a pointer to the SAP User of the PHY
     */
    LteEnbPhySapUser* GetLteEnbPhySapUser();

    /**
     * \brief Set the PHY SAP Provider
     * \param s a pointer to the PHY SAP provider
     */
    void SetLteEnbPhySapProvider(LteEnbPhySapProvider* s);

    /**
     * \brief Get the eNB-ComponentCarrierManager SAP User
     * \return a pointer to the SAP User of the ComponentCarrierManager
     */
    LteCcmMacSapProvider* GetLteCcmMacSapProvider();

    /**
     * \brief Set the ComponentCarrierManager SAP user
     * \param s a pointer to the ComponentCarrierManager provider
     */
    void SetLteCcmMacSapUser(LteCcmMacSapUser* s);

    /**
     * TracedCallback signature for DL scheduling events.
     *
     * \param [in] frame Frame number.
     * \param [in] subframe Subframe number.
     * \param [in] rnti The C-RNTI identifying the UE.
     * \param [in] mcs0 The MCS for transport block..
     * \param [in] tbs0Size
     * \param [in] mcs1 The MCS for transport block.
     * \param [in] tbs1Size
     * \param [in] component carrier id
     */
    typedef void (*DlSchedulingTracedCallback)(const uint32_t frame,
                                               const uint32_t subframe,
                                               const uint16_t rnti,
                                               const uint8_t mcs0,
                                               const uint16_t tbs0Size,
                                               const uint8_t mcs1,
                                               const uint16_t tbs1Size,
                                               const uint8_t ccId);

    /**
     *  TracedCallback signature for UL scheduling events.
     *
     * \param [in] frame Frame number.
     * \param [in] subframe Subframe number.
     * \param [in] rnti The C-RNTI identifying the UE.
     * \param [in] mcs  The MCS for transport block
     * \param [in] tbsSize
     */
    typedef void (*UlSchedulingTracedCallback)(const uint32_t frame,
                                               const uint32_t subframe,
                                               const uint16_t rnti,
                                               const uint8_t mcs,
                                               const uint16_t tbsSize);

  private:
    /**
     * \brief Receive a DL CQI ideal control message
     * \param msg the DL CQI message
     */
    void ReceiveDlCqiLteControlMessage(Ptr<DlCqiLteControlMessage> msg);

    /**
     * \brief Receive a DL CQI ideal control message
     * \param msg the DL CQI message
     */
    void DoReceiveLteControlMessage(Ptr<LteControlMessage> msg);

    /**
     * \brief Receive a CE element containing the buffer status report
     * \param bsr the BSR message
     */
    void ReceiveBsrMessage(MacCeListElement_s bsr);

    /**
     * \brief UL CQI report
     * \param ulcqi FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
     */
    void DoUlCqiReport(FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi);

    // forwarded from LteEnbCmacSapProvider
    /**
     * \brief Configure MAC function
     * \param ulBandwidth the UL bandwidth
     * \param dlBandwidth the DL bandwidth
     */
    void DoConfigureMac(uint16_t ulBandwidth, uint16_t dlBandwidth);
    /**
     * \brief Add UE function
     * \param rnti the RNTI
     */
    void DoAddUe(uint16_t rnti);
    /**
     * \brief Remove UE function
     * \param rnti the RNTI
     */
    void DoRemoveUe(uint16_t rnti);
    /**
     * \brief Add LC function
     * \param lcinfo the LC info
     * \param msu the LTE MAC SAP user
     */
    void DoAddLc(LteEnbCmacSapProvider::LcInfo lcinfo, LteMacSapUser* msu);
    /**
     * \brief Reconfigure LC function
     * \param lcinfo the LC info
     */
    void DoReconfigureLc(LteEnbCmacSapProvider::LcInfo lcinfo);
    /**
     * \brief Release LC function
     * \param rnti the RNTI
     * \param lcid the LCID
     */
    void DoReleaseLc(uint16_t rnti, uint8_t lcid);
    /**
     * \brief UE Update configuration request function
     * \param params LteEnbCmacSapProvider::UeConfig
     */
    void DoUeUpdateConfigurationReq(LteEnbCmacSapProvider::UeConfig params);
    /**
     * \brief Get RACH configuration function
     * \returns LteEnbCmacSapProvider::RachConfig
     */
    LteEnbCmacSapProvider::RachConfig DoGetRachConfig() const;
    /**
     * \brief Allocate NC RA preamble function
     * \param rnti the RNTI
     * \returns LteEnbCmacSapProvider::AllocateNcRaPreambleReturnValue
     */
    LteEnbCmacSapProvider::AllocateNcRaPreambleReturnValue DoAllocateNcRaPreamble(uint16_t rnti);

    // forwarded from LteMacSapProvider
    /**
     * \brief Transmit PDU function
     * \param params LteMacSapProvider::TransmitPduParameters
     */
    void DoTransmitPdu(LteMacSapProvider::TransmitPduParameters params);
    /**
     * \brief Report Buffer Status function
     * \param params LteMacSapProvider::ReportBufferStatusParameters
     */
    void DoReportBufferStatus(LteMacSapProvider::ReportBufferStatusParameters params);

    // forwarded from FfMacCchedSapUser
    /**
     * \brief CSched Cell Config configure function
     * \param params FfMacCschedSapUser::CschedCellConfigCnfParameters
     */
    void DoCschedCellConfigCnf(FfMacCschedSapUser::CschedCellConfigCnfParameters params);
    /**
     * \brief CSched UE Config configure function
     * \param params FfMacCschedSapUser::CschedUeConfigCnfParameters
     */
    void DoCschedUeConfigCnf(FfMacCschedSapUser::CschedUeConfigCnfParameters params);
    /**
     * \brief CSched LC Config configure function
     * \param params FfMacCschedSapUser::CschedLcConfigCnfParameters
     */
    void DoCschedLcConfigCnf(FfMacCschedSapUser::CschedLcConfigCnfParameters params);
    /**
     * \brief CSched LC Release configure function
     * \param params FfMacCschedSapUser::CschedLcReleaseCnfParameters
     */
    void DoCschedLcReleaseCnf(FfMacCschedSapUser::CschedLcReleaseCnfParameters params);
    /**
     * \brief CSched UE Release configure function
     * \param params FfMacCschedSapUser::CschedUeReleaseCnfParameters
     */
    void DoCschedUeReleaseCnf(FfMacCschedSapUser::CschedUeReleaseCnfParameters params);
    /**
     * \brief CSched UE Config Update Indication function
     * \param params FfMacCschedSapUser::CschedUeConfigUpdateIndParameters
     */
    void DoCschedUeConfigUpdateInd(FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params);
    /**
     * \brief CSched Cell Config Update Indication function
     * \param params FfMacCschedSapUser::CschedCellConfigUpdateIndParameters
     */
    void DoCschedCellConfigUpdateInd(
        FfMacCschedSapUser::CschedCellConfigUpdateIndParameters params);

    // forwarded from FfMacSchedSapUser
    /**
     * \brief Sched DL Config Indication function
     * \param ind FfMacSchedSapUser::SchedDlConfigIndParameters
     */
    void DoSchedDlConfigInd(FfMacSchedSapUser::SchedDlConfigIndParameters ind);
    /**
     * \brief Sched UL Config Indication function
     * \param params FfMacSchedSapUser::SchedUlConfigIndParameters
     */
    void DoSchedUlConfigInd(FfMacSchedSapUser::SchedUlConfigIndParameters params);

    // forwarded from LteEnbPhySapUser
    /**
     * \brief Subrame Indication function
     * \param frameNo frame number
     * \param subframeNo subframe number
     */
    void DoSubframeIndication(uint32_t frameNo, uint32_t subframeNo);
    /**
     * \brief Receive RACH Preamble function
     * \param prachId PRACH ID number
     */
    void DoReceiveRachPreamble(uint8_t prachId);

    // forwarded by LteCcmMacSapProvider
    /**
     * Report MAC CE to scheduler
     * \param bsr the BSR
     */
    void DoReportMacCeToScheduler(MacCeListElement_s bsr);

    /**
     * \brief Report SR to scheduler
     * \param rnti RNTI of the UE that requested the SR
     *
     * Since SR is not implemented in LTE, this method does nothing.
     */
    void DoReportSrToScheduler(uint16_t rnti [[maybe_unused]])
    {
    }

  public:
    /**
     * legacy public for use the Phy callback
     * \param p packet
     */
    void DoReceivePhyPdu(Ptr<Packet> p);

  private:
    /**
     * \brief UL Info List Elements HARQ Feedback function
     * \param params UlInfoListElement_s
     */
    void DoUlInfoListElementHarqFeedback(UlInfoListElement_s params);
    /**
     * \brief DL Info List Elements HARQ Feedback function
     * \param params DlInfoListElement_s
     */
    void DoDlInfoListElementHarqFeedback(DlInfoListElement_s params);

    /// RNTI, LC ID, SAP of the RLC instance
    std::map<uint16_t, std::map<uint8_t, LteMacSapUser*>> m_rlcAttached;

    std::vector<CqiListElement_s> m_dlCqiReceived; ///< DL-CQI received
    std::vector<FfMacSchedSapProvider::SchedUlCqiInfoReqParameters>
        m_ulCqiReceived;                            ///< UL-CQI received
    std::vector<MacCeListElement_s> m_ulCeReceived; ///< CE received (BSR up to now)

    std::vector<DlInfoListElement_s> m_dlInfoListReceived; ///< DL HARQ feedback received

    std::vector<UlInfoListElement_s> m_ulInfoListReceived; ///< UL HARQ feedback received

    /*
     * Map of UE's info element (see 4.3.12 of FF MAC Scheduler API)
     */
    // std::map <uint16_t,UlInfoListElement_s> m_ulInfoListElements;

    LteMacSapProvider* m_macSapProvider;      ///< the MAC SAP provider
    LteEnbCmacSapProvider* m_cmacSapProvider; ///< the CMAC SAP provider
    LteMacSapUser* m_macSapUser;              ///< the MAC SAP user
    LteEnbCmacSapUser* m_cmacSapUser;         ///< the CMAC SAP user

    FfMacSchedSapProvider* m_schedSapProvider;   ///< the Sched SAP provider
    FfMacCschedSapProvider* m_cschedSapProvider; ///< the Csched SAP provider
    FfMacSchedSapUser* m_schedSapUser;           ///< the Sched SAP user
    FfMacCschedSapUser* m_cschedSapUser;         ///< the CSched SAP user

    // PHY-SAP
    LteEnbPhySapProvider* m_enbPhySapProvider; ///< the ENB Phy SAP provider
    LteEnbPhySapUser* m_enbPhySapUser;         ///< the ENB Phy SAP user

    // Sap For ComponentCarrierManager 'Uplink case'
    LteCcmMacSapProvider* m_ccmMacSapProvider; ///< CCM MAC SAP provider
    LteCcmMacSapUser* m_ccmMacSapUser;         ///< CCM MAC SAP user
    /**
     * frame number of current subframe indication
     */
    uint32_t m_frameNo;
    /**
     * subframe number of current subframe indication
     */
    uint32_t m_subframeNo;
    /**
     * Trace information regarding DL scheduling
     * Frame number, Subframe number, RNTI, MCS of TB1, size of TB1,
     * MCS of TB2 (0 if not present), size of TB2 (0 if not present)
     */
    TracedCallback<DlSchedulingCallbackInfo> m_dlScheduling;

    /**
     * Trace information regarding UL scheduling
     * Frame number, Subframe number, RNTI, MCS of TB, size of TB, component carrier id
     */
    TracedCallback<uint32_t, uint32_t, uint16_t, uint8_t, uint16_t, uint8_t> m_ulScheduling;

    uint8_t m_macChTtiDelay; ///< delay of MAC, PHY and channel in terms of TTIs

    std::map<uint16_t, DlHarqProcessesBuffer_t>
        m_miDlHarqProcessesPackets; ///< Packet under transmission of the DL HARQ process

    uint8_t m_numberOfRaPreambles;  ///< number of RA preambles
    uint8_t m_preambleTransMax;     ///< preamble transmit maximum
    uint8_t m_raResponseWindowSize; ///< RA response window size
    uint8_t m_connEstFailCount;     ///< the counter value for T300 timer expiration

    /**
     * info associated with a preamble allocated for non-contention based RA
     *
     */
    struct NcRaPreambleInfo
    {
        uint16_t rnti;   ///< rnti previously allocated for this non-contention based RA procedure
        Time expiryTime; ///< value the expiration time of this allocation (so that stale preambles
                         ///< can be reused)
    };

    /**
     * map storing as key the random access preamble IDs allocated for
     * non-contention based access, and as value the associated info
     *
     */
    std::map<uint8_t, NcRaPreambleInfo> m_allocatedNcRaPreambleMap;

    std::map<uint8_t, uint32_t> m_receivedRachPreambleCount; ///< received RACH preamble count

    std::map<uint16_t, uint32_t> m_rapIdRntiMap; ///< RAPID RNTI map

    /// component carrier Id used to address sap
    uint8_t m_componentCarrierId;
};

} // end namespace ns3

#endif /* LTE_ENB_MAC_ENTITY_H */
