#ifndef MISSION_DEVICES_PLOCUPDATER_H_
#define MISSION_DEVICES_PLOCUPDATER_H_

#include "OBSWConfig.h"
#include "devicedefinitions/PlocSupervisorDefinitions.h"

#include "fsfw/action/CommandActionHelper.h"
#include "fsfw/action/ActionHelper.h"
#include "fsfw/action/HasActionsIF.h"
#include "fsfw/action/CommandsActionsIF.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "bsp_q7s/memory/SdCardManager.h"
#include "linux/fsfwconfig/objects/systemObjectList.h"
#include "fsfw/tmtcpacket/SpacePacket.h"


/**
 * @brief   An object of this class can be used to perform the software updates of the PLOC. The
 *          software update will be read from one of the SD cards, split into multiple space
 *          packets and sent to the PlocSupervisorHandler.
 *
 * @details The MPSoC has two boot memories (NVM0 and NVM1) where each stores two images (Partition A
 *          and Partition B)
 *
 * @author  J. Meier
 */
class PlocUpdater : public SystemObject,
        public HasActionsIF,
        public ExecutableObjectIF,
        public HasReturnvaluesIF,
        public CommandsActionsIF {
public:

    static const ActionId_t UPDATE_NVM0_A = 0;
    static const ActionId_t UPDATE_NVM0_B = 1;
    static const ActionId_t UPDATE_NVM1_A = 2;
    static const ActionId_t UPDATE_NVM1_B = 3;

    PlocUpdater(object_id_t objectId);
    virtual ~PlocUpdater();

    ReturnValue_t performOperation(uint8_t operationCode = 0) override;
    ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
            const uint8_t* data, size_t size);
    MessageQueueId_t getCommandQueue() const;
    ReturnValue_t initialize() override;
    MessageQueueIF* getCommandQueuePtr() override;
    void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) override;
    void stepFailedReceived(ActionId_t actionId, uint8_t step, ReturnValue_t returnCode) override;
    void dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) override;
    void completionSuccessfulReceived(ActionId_t actionId) override;
    void completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) override;

private:

    static const uint8_t INTERFACE_ID = CLASS_ID::PLOC_UPDATER;

    //! [EXPORT] : [COMMENT] Updater is already performing an update
    static const ReturnValue_t UPDATER_BUSY = MAKE_RETURN_CODE(0xA0);
    //! [EXPORT] : [COMMENT] Received update command with invalid path string (too long).
    static const ReturnValue_t NAME_TOO_LONG = MAKE_RETURN_CODE(0xA1);
    //! [EXPORT] : [COMMENT] Received command to initiate update but SD card with update image not mounted.
    static const ReturnValue_t SD_NOT_MOUNTED = MAKE_RETURN_CODE(0xA2);
    //! [EXPORT] : [COMMENT] Update file received with update command does not exist.
    static const ReturnValue_t FILE_NOT_EXISTS = MAKE_RETURN_CODE(0xA3);

    static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_UPDATER;

    //! [EXPORT] : [COMMENT] Try to read update file but the file does not exist.
    //! P1: Indicates in which state the file read fails
    //! P2: During the update transfer the second parameter gives information about the number of already sent packets
    static const Event UPDATE_FILE_NOT_EXISTS = MAKE_EVENT(0, severity::LOW);
    //! [EXPORT] : [COMMENT] Failed to send command to supervisor handler
    //! P1: Return value of CommandActionHelper::commandAction
    //! P2: Action ID of command to send
    static const Event ACTION_COMMANDING_FAILED = MAKE_EVENT(1, severity::LOW);
    //! [EXPORT] : [COMMENT] Supervisor handler replied action message indicating a command execution failure of the update available command
    static const Event UPDATE_AVAILABLE_FAILED = MAKE_EVENT(2, severity::LOW);
    //! [EXPORT] : [COMMENT] Supervisor handler failed to transfer an update space packet.
    //! P1: Parameter holds the number of update packets already sent (inclusive the failed packet)
    static const Event UPDATE_TRANSFER_FAILED = MAKE_EVENT(3, severity::LOW);
    //! [EXPORT] : [COMMENT] Supervisor failed to execute the update verify command.
    static const Event UPDATE_VERIFY_FAILED = MAKE_EVENT(4, severity::LOW);
    //! [EXPORT] : [COMMENT] MPSoC update successful completed
    static const Event UPDATE_FINISHED =  MAKE_EVENT(5, severity::INFO);

    static const uint32_t QUEUE_SIZE = config::PLOC_UPDATER_QUEUE_SIZE;
    static const size_t MAX_PLOC_UPDATE_PATH = 50;
    static const size_t SD_PREFIX_LENGTH = 8;
    // Maximum size of update payload data per space packet (max size of space packet is 1024 bytes)
    static const size_t MAX_SP_DATA = 1016;

    MessageQueueIF* commandQueue = nullptr;

    SdCardManager* sdcMan = nullptr;

    CommandActionHelper commandActionHelper;

    ActionHelper actionHelper;

    enum class State: uint8_t {
        IDLE,
        UPDATE_AVAILABLE,
        UPDATE_TRANSFER,
        UPDATE_VERIFY,
        COMMAND_EXECUTING
    };

    State state = State::IDLE;

    ActionId_t pendingCommand = PLOC_SPV::NONE;

    enum class Memory: uint8_t {
        NVM0,
        NVM1
    };

    Memory updateMemory = Memory::NVM0;

    enum class Partition: uint8_t {
        A,
        B
    };

    Partition updatePartition = Partition::A;

    uint32_t packetsSent = 0;
    uint32_t remainingPackets = 0;
    // Number of packets required to transfer the update image
    uint32_t numOfUpdatePackets = 0;

    std::string updateFile;
    uint32_t imageSize = 0;
    uint32_t imageCrc = 0;

    void readCommandQueue();
    void doStateMachine();

    /**
     * @brief   Extracts the path and name of the update image from the service 8 command data.
     */
    ReturnValue_t getImageLocation(const uint8_t* data, size_t size);

    ReturnValue_t checkNameLength(size_t size);

    /**
     * @brief   Prepares and sends update available command to PLOC supervisor handler.
     */
    void commandUpdateAvailable();

    /**
     * @brief   Prepares and sends and update packet to the PLOC supervisor handler.
     */
    void commandUpdatePacket();

    /**
     * @brief   Prepares and sends the update verification packet to the PLOC supervisor handler.
     */
    void commandUpdateVerify();

    /**
     * @brief   Checks whether the SD card to read from is mounted or not.
     */
    bool isSdCardMounted(sd::SdCard sdCard);

    ReturnValue_t makeCrc();

    void adjustSequenceFlags(PLOC_SPV::UpdatePacket& packet);
};

#endif /* MISSION_DEVICES_PLOCUPDATER_H_ */