#ifndef CCSDSHANDLER_H_
#define CCSDSHANDLER_H_

#include "OBSWConfig.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
#include "fsfw/parameters/ParameterHelper.h"
#include "fsfw/action/ActionHelper.h"
#include "fsfw/action/HasActionsIF.h"
#include "fsfw/timemanager/Countdown.h"
#include "fsfw/events/EventMessage.h"
#include "linux/obc/TxRateSetterIF.h"
#include "fsfw_hal/common/gpio/gpioDefinitions.h"
#include "fsfw_hal/common/gpio/GpioIF.h"
#include "VirtualChannel.h"
#include <unordered_map>

/**
 * @brief   This class handles the data exchange with the CCSDS IP cores implemented in the
 *          programmable logic of the Q7S.
 *
 * @author  J. Meier
 */
class CCSDSHandler: public SystemObject,
		public ExecutableObjectIF,
		public AcceptsTelemetryIF,
		public AcceptsTelecommandsIF,
		public HasReturnvaluesIF,
		public ReceivesParameterMessagesIF,
		public HasActionsIF {
public:

    using VcId_t = uint8_t;

    /**
     * @brief Constructor
     *
     * @param objectId  Object ID of the CCSDS handler
     * @param ptmeId    Object ID of the PTME object providing access to the PTME IP Core.
     * @param tcDestination Object ID of object handling received TC space packets
     * @param txRateSetter  Object providing the functionality to switch the input bitrate of
     *                      the S-Band transceiver.
     * @param gpioIF    Required to enable TX data and TX clock RS485 transceiver chips.
     * @param enTxClock GPIO ID of RS485 tx clock enable
     * @param enTxData  GPIO ID of RS485 tx data enable
     */
    CCSDSHandler(object_id_t objectId, object_id_t ptmeId, object_id_t tcDestination,
            TxRateSetterIF* txRateSetterIF, GpioIF* gpioIF, gpioId_t enTxClock, gpioId_t enTxData);

	~CCSDSHandler();

	ReturnValue_t performOperation(uint8_t operationCode = 0) override;
	ReturnValue_t initialize();
	MessageQueueId_t getCommandQueue() const;

	/**
	 * @brief   Function to add a virtual channel
	 *
	 * @param virtualChannelId  ID of the virtual channel to add
	 * @param virtualChannel    Pointer to virtual channel object
	 */
	void addVirtualChannel(VcId_t virtualChannelId, VirtualChannel* virtualChannel);

	MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0);
    ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
            ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues,
            uint16_t startAtIndex);

    uint16_t getIdentifier() override;
    MessageQueueId_t getRequestQueue() override;

    virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
            const uint8_t* data, size_t size);

private:

    static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_HANDLER;

	static const uint32_t QUEUE_SIZE = common::CCSDS_HANDLER_QUEUE_SIZE;

	static const ActionId_t SET_LOW_RATE = 0;
	static const ActionId_t SET_HIGH_RATE = 1;
	static const ActionId_t EN_TRANSMITTER = 2;
	static const ActionId_t DIS_TRANSMITTER = 3;

	//! [EXPORT] : [COMMENT] Received action message with unknown action id
	static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xA0);

#if TMTC_TEST_SETUP == 0
	// syrlinks must not be transmitting more than 15 minutes (according to datasheet)
	static const uint32_t TRANSMITTER_TIMEOUT = 900000; //900000 ms = 15 min
#else
	// Set to high value when not sending via syrlinks
	static const uint32_t TRANSMITTER_TIMEOUT = 86400000; // 1 day
#endif /* TMTC_TEST_SETUP == 0 */

	static const bool UP = true;
	static const bool DOWN = false;

	using VirtualChannelMap = std::unordered_map<VcId_t, VirtualChannel*>;
    using VirtualChannelMapIter = VirtualChannelMap::iterator;

    VirtualChannelMap virtualChannelMap;

    // Object ID of PTME object
    object_id_t ptmeId;

    object_id_t tcDestination;

    MessageQueueIF* commandQueue = nullptr;
    MessageQueueIF* eventQueue = nullptr;

    ParameterHelper parameterHelper;

    ActionHelper actionHelper;

    MessageQueueId_t tcDistributorQueueId;

    TxRateSetterIF* txRateSetterIF = nullptr;

    GpioIF* gpioIF = nullptr;
    gpioId_t enTxClock = gpio::NO_GPIO;
    gpioId_t enTxData = gpio::NO_GPIO;

    // Countdown to disable transmitter after 15 minutes
    Countdown transmitterCountdown;

    // When true transmitting is started as soon as carrier lock has been detected
    bool enableTxWhenCarrierLock = false;

    bool linkState = DOWN;

    void readCommandQueue(void);
	void handleTelemetry();
	void handleTelecommands();
	void checkEvents();
	void handleEvent(EventMessage* eventMessage);

	void handleBitLockEvent();
	void handleCarrierLockEvent();

	/**
	 * @brief    Forward link state to virtual channels.
	 */
	void forwardLinkstate();

	/**
	 * @brief   Starts transmit timer and enables transmitter.
	 */
	void enableTransmit();

	/**
	 * @brief   Checks Tx timer for timeout and disables RS485 tx clock and tx data in case
	 *          timer has expired.
	 */
	void checkTxTimer();

	/**
	 * @brief   Disables the transmitter by pulling the enable tx clock and tx data pin of the
	 *          RS485 transceiver chips to high.
	 */
	void disableTransmit();
};

#endif /* CCSDSHANDLER_H_ */