/*
 * Copyright (c) 2018 Fraunhofer ESK
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Vignesh Babu <ns3-dev@esk.fraunhofer.de>
 */

#ifndef LTE_TEST_RADIO_LINK_FAILURE_H
#define LTE_TEST_RADIO_LINK_FAILURE_H

#include "ns3/lte-ue-rrc.h"
#include "ns3/mobility-model.h"
#include "ns3/net-device-container.h"
#include "ns3/node-container.h"
#include "ns3/nstime.h"
#include "ns3/test.h"
#include "ns3/vector.h"

#include <vector>

namespace ns3
{

class LteUeNetDevice;

}

using namespace ns3;

/**
 * @brief Test suite for
 *
 * \sa ns3::LteRadioLinkFailureTestCase
 */
class LteRadioLinkFailureTestSuite : public TestSuite
{
  public:
    LteRadioLinkFailureTestSuite();
};

/**
 * @ingroup lte
 *
 * @brief Testing the cell reselection procedure by UE at IDLE state
 */
class LteRadioLinkFailureTestCase : public TestCase
{
  public:
    /**
     * @brief Creates an instance of the radio link failure test case.
     *
     * @param numEnbs number of eNodeBs
     * @param numUes number of UEs
     * @param simTime the simulation time
     * @param isIdealRrc if true, simulation uses Ideal RRC protocol, otherwise
     *                   simulation uses Real RRC protocol
     * @param uePositionList Position of the UEs
     * @param enbPositionList Position of the eNodeBs
     * @param ueJumpAwayPosition Vector holding the UE jump away coordinates
     * @param checkConnectedList the time at which UEs should have an active RRC connection
     */
    LteRadioLinkFailureTestCase(uint32_t numEnbs,
                                uint32_t numUes,
                                Time simTime,
                                bool isIdealRrc,
                                std::vector<Vector> uePositionList,
                                std::vector<Vector> enbPositionList,
                                Vector ueJumpAwayPosition,
                                std::vector<Time> checkConnectedList);

    ~LteRadioLinkFailureTestCase() override;

  private:
    /**
     * Builds the test name string based on provided parameter values
     * @param numEnbs the number of eNB nodes
     * @param numUes the number of UE nodes
     * @param isIdealRrc True if the Ideal RRC protocol is used
     * @returns the name string
     */
    std::string BuildNameString(uint32_t numEnbs, uint32_t numUes, bool isIdealRrc);
    /**
     * @brief Setup the simulation according to the configuration set by the
     *        class constructor, run it, and verify the result.
     */
    void DoRun() override;

    /**
     * Check connected function
     * @param ueDevice the UE device
     * @param enbDevices the ENB devices
     */
    void CheckConnected(Ptr<NetDevice> ueDevice, NetDeviceContainer enbDevices);

    /**
     * Check if the UE is in idle state
     * @param ueDevice the UE device
     * @param enbDevices the ENB devices
     */
    void CheckIdle(Ptr<NetDevice> ueDevice, NetDeviceContainer enbDevices);

    /**
     * @brief Check if the UE exist at the eNB
     * @param rnti the RNTI of the UE
     * @param enbDevice the eNB device
     * @return true if the UE exist at the eNB, otherwise false
     */
    bool CheckUeExistAtEnb(uint16_t rnti, Ptr<NetDevice> enbDevice);

    /**
     * @brief State transition callback function
     * @param context the context string
     * @param imsi the IMSI
     * @param cellId the cell ID
     * @param rnti the RNTI
     * @param oldState the old state
     * @param newState the new state
     */
    void UeStateTransitionCallback(std::string context,
                                   uint64_t imsi,
                                   uint16_t cellId,
                                   uint16_t rnti,
                                   LteUeRrc::State oldState,
                                   LteUeRrc::State newState);

    /**
     * @brief Connection established at UE callback function
     * @param context the context string
     * @param imsi the IMSI
     * @param cellId the cell ID
     * @param rnti the RNTI
     */
    void ConnectionEstablishedUeCallback(std::string context,
                                         uint64_t imsi,
                                         uint16_t cellId,
                                         uint16_t rnti);

    /**
     * @brief Connection established at eNodeB callback function
     * @param context the context string
     * @param imsi the IMSI
     * @param cellId the cell ID
     * @param rnti the RNTI
     */
    void ConnectionEstablishedEnbCallback(std::string context,
                                          uint64_t imsi,
                                          uint16_t cellId,
                                          uint16_t rnti);

    /**
     * @brief This callback function is executed when UE context is removed at eNodeB
     * @param context the context string
     * @param imsi the IMSI
     * @param cellId the cell ID
     * @param rnti the RNTI
     */
    void ConnectionReleaseAtEnbCallback(std::string context,
                                        uint64_t imsi,
                                        uint16_t cellId,
                                        uint16_t rnti);

    /**
     * @brief This callback function is executed when UE RRC receives an in-sync or out-of-sync
     * indication
     * @param context the context string
     * @param imsi the IMSI
     * @param rnti the RNTI
     * @param cellId the cell ID
     * @param type in-sync or out-of-sync indication
     * @param count the number of in-sync or out-of-sync indications
     */
    void PhySyncDetectionCallback(std::string context,
                                  uint64_t imsi,
                                  uint16_t rnti,
                                  uint16_t cellId,
                                  std::string type,
                                  uint8_t count);

    /**
     * @brief This callback function is executed when radio link failure is detected
     * @param context the context string
     * @param imsi the IMSI
     * @param rnti the RNTI
     * @param cellId the cell ID
     */
    void RadioLinkFailureCallback(std::string context,
                                  uint64_t imsi,
                                  uint16_t cellId,
                                  uint16_t rnti);

    /**
     * @brief Jump away function
     *
     * @param UeJumpAwayPositionList A list of positions where UE would jump
     */
    void JumpAway(Vector UeJumpAwayPositionList);

    uint32_t m_numEnbs;                    ///< number of eNodeBs
    uint32_t m_numUes;                     ///< number of UEs
    Time m_simTime;                        ///< simulation time
    bool m_isIdealRrc;                     ///< whether the LTE is configured to use ideal RRC
    std::vector<Vector> m_uePositionList;  ///< Position of the UEs
    std::vector<Vector> m_enbPositionList; ///< Position of the eNodeBs
    std::vector<Time>
        m_checkConnectedList;    ///< the time at which UEs should have an active RRC connection
    Vector m_ueJumpAwayPosition; ///< Position where the UE(s) would jump

    /// The current UE RRC state.
    LteUeRrc::State m_lastState;

    bool m_radioLinkFailureDetected;      ///< true if radio link fails
    uint32_t m_numOfInSyncIndications;    ///< number of in-sync indications detected
    uint32_t m_numOfOutOfSyncIndications; ///< number of out-of-sync indications detected
    Ptr<MobilityModel> m_ueMobility;      ///< UE mobility model
};

#endif /* LTE_TEST_RADIO_LINK_FAILURE_H */
