#ifndef ECSS_SERVICES_TIMEBASEDSCHEDULINGSERVICE_HPP
#define ECSS_SERVICES_TIMEBASEDSCHEDULINGSERVICE_HPP

#include "ErrorHandler.hpp"
#include "Helpers/CRCHelper.hpp"
#include "MessageParser.hpp"
#include "Service.hpp"
#include "etl/list.h"

// Include platform specific files
#include "Helpers/TimeGetter.hpp"

/**
 * @def SUB_SCHEDULES_ENABLED
 * @brief Indicates whether sub-schedules are supported
 *
 * @details Sub-schedules are currently not implemented so this has no effect
 */
/**
 * @def GROUPS_ENABLED
 * @brief Indicates whether scheduling groups are enabled
 */
#define GROUPS_ENABLED 0
#define SUB_SCHEDULES_ENABLED 0

/**
 * @brief Namespace to access private members during test
 *
 * @details Define a namespace for the access of the private members to avoid conflicts
 */
namespace unit_test {
	struct Tester;
} // namespace unit_test

/**
 * @brief An implementation of the ECSS standard ST[11] service
 *
 * @details This service is taking care of the timed release of a received TC packet from the
 * ground.
 * @todo Define whether the parsed absolute release time is saved in the scheduled activity as an
 * uint32_t or in the time format specified by the time management service.
 *
 * @ingroup Services
 */
class TimeBasedSchedulingService : public Service {
private:
	/**
	 * @brief Indicator of the schedule execution
	 * True indicates "enabled" and False "disabled" state
	 * @details The schedule execution indicator will be updated by the process that is running
	 * the time scheduling service.
	 */
	bool executionFunctionStatus = false;

	/**
	 * @brief Request identifier of the received packet
	 *
	 * @details The request identifier consists of the application process ID, the packet
	 * sequence count and the source ID, all defined in the ECSS standard.
	 */
	struct RequestID {
		uint16_t applicationID = 0; ///< Application process ID
		uint16_t sequenceCount = 0; ///< Packet sequence count
		uint8_t sourceID = 0;       ///< Packet source ID

		bool operator!=(const RequestID& rightSide) const {
			return (sequenceCount != rightSide.sequenceCount) or (applicationID != rightSide.applicationID) or
			       (sourceID != rightSide.sourceID);
		}
	};

	/**
	 * @brief Instances of activities to run in the schedule
	 *
	 * @details All scheduled activities must contain the request they exist for, their release
	 * time and the corresponding request identifier.
	 *
	 * @todo If we decide to use sub-schedules, the ID of that has to be defined
	 * @todo If groups are used, then the group ID has to be defined here
	 */
	struct ScheduledActivity {
		Message request;                         ///< Hold the received command request
		RequestID requestID;                     ///< Request ID, characteristic of the definition
		Time::CustomCUC_t requestReleaseTime{0}; ///< Keep the command release time
	};

	/**
	 * @brief Hold the scheduled activities
	 *
	 * @details The scheduled activities in this list are ordered by their release time, as the
	 * standard requests.
	 */
	etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities> scheduledActivities;

	/**
	 * @brief Sort the activities by their release time
	 *
	 * @details The ECSS standard requires that the activities are sorted in the TM message
	 * response. Also it is better to have the activities sorted.
	 */
	inline void
	sortActivitiesReleaseTime(etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& schedActivities) {
		schedActivities.sort([](ScheduledActivity const& leftSide, ScheduledActivity const& rightSide) {
			// cppcheck-suppress
			return leftSide.requestReleaseTime < rightSide.requestReleaseTime;
		});
	}

	/**
	 * @brief Define a friend in order to be able to access private members during testing
	 *
	 * @details The private members defined in this class, must not in any way be public to avoid
	 * misuse. During testing, access to private members for verification is required, so an
	 * access friend structure is defined here.
	 */
	friend struct ::unit_test::Tester;

	/**
     * Notifies the timeBasedSchedulingTask after the insertion of activities to scheduleActivity list.
     */
	void notifyNewActivityAddition();

public:
	inline static const uint8_t ServiceType = 11;

	enum MessageType : uint8_t {
		EnableTimeBasedScheduleExecutionFunction = 1,
		DisableTimeBasedScheduleExecutionFunction = 2,
		ResetTimeBasedSchedule = 3,
		InsertActivities = 4,
		DeleteActivitiesById = 5,
		TimeShiftActivitiesById = 7,
		DetailReportActivitiesById = 9,
		TimeBasedScheduleReportById = 10,
		ActivitiesSummaryReportById = 12,
		TimeBasedScheduledSummaryReport = 13,
		TimeShiftALlScheduledActivities = 15,
		DetailReportAllScheduledActivities = 16,
	};

	/**
	 * @brief Class constructor
	 * @details Initializes the serviceType
	 */
	TimeBasedSchedulingService();

	/**
	 * This function executes the next activity and removes it from the list.
	 * @return the requestReleaseTime of next activity to be executed after this time
	 */
	Time::CustomCUC_t executeScheduledActivity(Time::CustomCUC_t currentTime);

	/**
	 * @brief TC[11,1] enable the time-based schedule execution function
	 *
	 * @details Enables the time-based command execution scheduling
	 * @param request Provide the received message as a parameter
	 */
	void enableScheduleExecution(Message& request);

	/**
	 * @brief TC[11,2] disable the time-based schedule execution function
	 *
	 * @details Disables the time-based command execution scheduling
	 * @param request Provide the received message as a parameter
	 */
	void disableScheduleExecution(Message& request);

	/**
	 * @brief TC[11,3] reset the time-based schedule
	 *
	 * @details Resets the time-based command execution schedule, by clearing all scheduled
	 * activities.
	 * @param request Provide the received message as a parameter
	 */
	void resetSchedule(Message& request);

	/**
	 * @brief TC[11,4] insert activities into the time based schedule
	 *
	 * @details Add activities into the schedule for future execution. The activities are inserted
	 * by ascending order of their release time. This done to avoid confusion during the
	 * execution of the schedule and also to make things easier whenever a release time sorted
	 * report is requested by he corresponding service.
	 * @param request Provide the received message as a parameter
	 * @todo Definition of the time format is required
	 * @throws ExecutionStartError If there is request to be inserted and the maximum
	 * number of activities in the current schedule has been reached, then an @ref
	 * ErrorHandler::ExecutionStartErrorType is being issued.  Also if the release time of the
	 * request is less than a set time margin, defined in @ref ECSS_TIME_MARGIN_FOR_ACTIVATION,
	 * from the current time a @ref ErrorHandler::ExecutionStartErrorType is also issued.
	 */
	void insertActivities(Message& request);

	/**
	 * @brief TC[11,15] time-shift all scheduled activities
	 *
	 * @details All scheduled activities are shifted per user request. The relative time offset
	 * received and tested against the current time.
	 * @param request Provide the received message as a parameter
	 * @todo Definition of the time format is required for the relative time format
	 * @throws ExecutionStartError If the release time of the request is less than a
	 * set time margin, defined in @ref ECSS_TIME_MARGIN_FOR_ACTIVATION, from the current time an
	 * @ref ErrorHandler::ExecutionStartErrorType report is issued for that instruction.
	 */
	void timeShiftAllActivities(Message& request);

	/**
	 * @brief TC[11,16] detail-report all activities
	 *
	 * @details Send a detailed report about the status of all the activities
	 * on the current schedule. Generates a TM[11,10] response.
	 * @param request Provide the received message as a parameter
	 * @todo Replace the time parsing with the time parser
	 */
	void detailReportAllActivities(Message& request);

	/**
	 * @brief TC[11,9] detail-report activities identified by request identifier
	 *
	 * @details Send a detailed report about the status of the requested activities, based on the
	 * provided request identifier. Generates a TM[11,10] response. The matched activities are
	 * contained in the report, in an ascending order based on their release time.
	 * @param request Provide the received message as a parameter
	 * @todo Replace time parsing with the time parser
	 * @throws ExecutionStartError If a requested activity, identified by the provided
	 * request identifier is not found in the schedule issue an @ref
	 * ErrorHandler::ExecutionStartErrorType for that instruction.
	 */
	void detailReportActivitiesByID(Message& request);

	/**
	 * @brief TC[11,12] summary-report activities identified by request identifier
	 *
	 * @details Send a summary report about the status of the requested activities. Generates a
	 * TM[11,13] response, with activities ordered in an ascending order, based on their release
	 * time.
	 * @param request Provide the received message as a parameter
	 * @throws ExecutionStartError If a requested activity, identified by the provided
	 * request identifier is not found in the schedule issue an @ref
	 * ErrorHandler::ExecutionStartErrorType for that instruction.
	 */
	void summaryReportActivitiesByID(Message& request);

	/**
	 * @brief TC[11,5] delete time-based scheduled activities identified by a request identifier
	 *
	 * @details Delete certain activities by using the unique request identifier.
	 * @param request Provide the received message as a parameter
	 * @throws ExecutionStartError If a requested activity, identified by the provided
	 * request identifier is not found in the schedule issue an @ref
	 * ErrorHandler::ExecutionStartErrorType for that instruction.
	 */
	void deleteActivitiesByID(Message& request);

	/**
	 * @brief TC[11,7] time-shift scheduled activities identified by a request identifier
	 *
	 * @details Time-shift certain activities by using the unique request identifier
	 * @param request Provide the received message as a parameter
	 * @todo Definition of the time format is required
	 * @throws ExecutionStartError If the requested time offset is less than the earliest
	 * time from the currently scheduled activities plus the @ref ECSS_TIME_MARGIN_FOR_ACTIVATION,
	 * then the request is rejected and an @ref ErrorHandler::ExecutionStartErrorType is issued.
	 * Also if an activity with a specified request identifier is not found, generate a failed
	 * start of execution for that specific instruction.
	 */
	void timeShiftActivitiesByID(Message& request);

	/**
	 * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet
	 * is the ground station.
	 *
	 * @note This function is called from the main execute() that is defined in the file MessageParser.hpp
	 * @param message Contains the necessary parameters to call the suitable subservice
	 */
	void execute(Message& message);
};

#endif // ECSS_SERVICES_TIMEBASEDSCHEDULINGSERVICE_HPP