#pragma once

#include "motordrivers.hpp"
#include "motorctl.hpp"
#include "buzzer.hpp"
#include "http.hpp"
#include "auto.hpp"


//--------------------------------------------
//---- struct, enum, variable declarations ---
//--------------------------------------------
//enum that decides how the motors get controlled
enum class controlMode_t {IDLE, JOYSTICK, MASSAGE, HTTP, MQTT, BLUETOOTH, AUTO, ADJUST_CHAIR};
//string array representing the mode enum (for printing the state as string)
extern const char* controlModeStr[8];

//--- control_config_t ---
//struct with config parameters
typedef struct control_config_t {
    controlMode_t defaultMode;  //default mode after startup and toggling IDLE
    //timeout options
    uint32_t timeoutMs;         //time of inactivity after which the mode gets switched to IDLE
    float timeoutTolerancePer;  //percentage the duty can vary between timeout checks considered still inactive
} control_config_t;




//==================================
//========= control class ==========
//==================================
//controls the mode the armchair operates
//repeatedly generates the motor commands corresponding to current mode and sends those to motorcontrol
class controlledArmchair {
    public:
        //--- constructor ---
        controlledArmchair (
                control_config_t config_f,
                buzzer_t* buzzer_f,
                controlledMotor* motorLeft_f,
                controlledMotor* motorRight_f,
                evaluatedJoystick* joystick_f,
                httpJoystick* httpJoystick_f
                );

        //--- functions ---
        //task that repeatedly generates motor commands depending on the current mode
        void startHandleLoop();

        //function that changes to a specified control mode
        void changeMode(controlMode_t modeNew);

        //function that toggle between IDLE and previous active mode (or default if not switched to certain mode yet)
        void toggleIdle();

        //function that toggles between two modes, but prefers first argument if entirely different mode is currently active
        void toggleModes(controlMode_t modePrimary, controlMode_t modeSecondary);

        //toggle between certain mode and previous mode
        void toggleMode(controlMode_t modePrimary);

        //function that restarts timer which initiates the automatic timeout (switch to IDLE) after certain time of inactivity
        void resetTimeout();

        //function for sending a button event (e.g. from button task at event) to control task
        //TODO: use queue instead?
        void sendButtonEvent(uint8_t count);

    private:

        //--- functions ---
        //function that evaluates whether there is no activity/change on the motor duty for a certain time, if so a switch to IDLE is issued. - has to be run repeatedly in a slow interval
        void handleTimeout();

        //--- objects ---
        buzzer_t* buzzer;
        controlledMotor* motorLeft;
        controlledMotor* motorRight;
        httpJoystick* httpJoystickMain_l;
        evaluatedJoystick* joystick_l;

        //---variables ---
        //struct for motor commands returned by generate functions of each mode
        motorCommands_t commands;
        //struct with config parameters
        control_config_t config;

        //store joystick data
        joystickData_t stickData;
        bool altStickMapping; //alternative joystick mapping (reverse mapped differently)

        //variables for http mode
        uint32_t http_timestamp_lastData = 0;

        //variables for MASSAGE mode
        bool freezeInput = false;

        //variables for AUTO mode
        auto_instruction_t instruction = auto_instruction_t::NONE; //variable to receive instructions from automatedArmchair
        
        //variable to store button event
        uint8_t buttonCount = 0;

        //definition of mode enum
        controlMode_t mode = controlMode_t::IDLE;

        //variable to store mode when toggling IDLE mode 
        controlMode_t modePrevious; //default mode

        //command preset for idling motors
        const motorCommand_t cmd_motorIdle = {
            .state = motorstate_t::IDLE,
            .duty = 0
        };
        const motorCommands_t cmds_bothMotorsIdle = {
            .left = cmd_motorIdle,
            .right = cmd_motorIdle
        };

        //variable for slow loop
        uint32_t timestamp_SlowLoopLastRun = 0;

        //variables for detecting timeout (switch to idle, after inactivity)
        float dutyLeft_lastActivity = 0;
        float dutyRight_lastActivity = 0;
        uint32_t timestamp_lastActivity = 0;
};