#ifndef PHAPP_H
#define PHAPP_H


#include "debug_config.h"
#include "config.h"
#include "enums.h"

#include <xmath.h>
#include <macros.h>
#include <osr-base.h>
#include <App.h>
#include <Component.h>
#include <Bridge.h>
#include <SerialMessage.h>

#include <ArduinoLog.h>

class POT;
class Relay;
class ModbusBridge;
class OmronVFD;
class OmronPID;
class Pos3Analog;
class StepperController;
class MotorLoad;
class StatusLight;

#define MAX_ADDONS 5

// test <<COMPONENT_KEY;CALL_TYPE;FLAGS;1:foo:2300:5>>
// test app::foo <<1;2;64;1:foo:2300:5>>
// test relay::_set <<51;2;64;1:set:1:0>>
class PHApp : public App
{
public:
    // states
    enum CONTROLLER_STATE
    {
        E_CS_OK = 0,
        E_CS_ERROR = 10
    };
    enum APP_STATE
    {
        RESET = 0,
        EXTRUDING = 1,
        STANDBY = 2,
        ERROR = 5,
        VFD_TIMEOUT = 10,
        PID_TIMEOUT = 11,
        FEED_TIMEOUT = 12,
        CONTROL_PANEL_INVALID = 13,
        PID_ERROR = 20,
        VFD_ERROR = 30,
        FEED_ERROR = 40,
    };
    PHApp() : App()
    {
    }
    
    SerialMessage *com_serial;
    POT *pot_0;
    POT *pot_1;
    POT *pot_2;

    Relay *relay_0;
    Relay *relay_1;
    Relay *relay_2;
    Relay *relay_3;
    Relay *relay_4;
    Relay *relay_5;
    Relay *relay_6;
    Relay *relay_7;

    Pos3Analog *pos3Analog_0;
    Pos3Analog *pos3Analog_1;

    StepperController *stepperController_0;
    StepperController *stepperController_1;

    StatusLight *statusLight_0;
    StatusLight *statusLight_1;

    ModbusBridge *modbusBridge;
    MotorLoad *mLoad;
    OmronVFD *omronVFD;
    OmronPID *pids;

    StepperController *extruderFeed;
    void setExtruderFeed(StepperController *feed) { extruderFeed = feed; }
    StepperController *getExtruderFeed() { return extruderFeed; }

    millis_t comTS;
    millis_t loopTS;
    millis_t wait;
    millis_t waitTS;
    Timer<10, millis> timer; // 10 concurrent tasks, using micros as resolution

    // error states and handling
    short _state;
    short _cstate;
    short _error;
    short getLastError()
    {
        return _error;
    }
    short setLastError(short val = 0)
    {
        _error = val;
        return _error;
    };
    short onError(short id, short code);
    short clearError();

    // unimpl. app states (JSON Rest)
    short setAppState(short newState);
    short getAppState(short val);

    // component callbacks
    short onStop(short code = 0);
    short onRun(short code = 0);
    short onWarning(short code);

    // component methods
    virtual short setup();
    short loop();
    virtual short list(short val0, short val1);
    virtual short onRegisterMethods(Bridge *bridge);

    // addons
    short loopExtruderFeed();
    short loopModbus();

    // debugging
    void printRegisters();
    short reset();
    short print(short arg1, short arg2);
};

#define STATUS_WARNING_BLINK           \
    if (statusLight_0)                 \
    {                                  \
        statusLight_0->setBlink(true); \
    }
#define STATUS_WARNING_BLINK_OFF        \
    if (statusLight_0)                  \
    {                                   \
        statusLight_0->setBlink(false); \
    }

#define STATUS_ERROR_BLINK             \
    if (statusLight_1)                 \
    {                                  \
        statusLight_1->setBlink(true); \
    }
#define STATUS_ERROR_BLINK_OFF          \
    if (statusLight_1)                  \
    {                                   \
        statusLight_1->setBlink(false); \
    }

#define STATUS_WARNING_ON      \
    if (statusLight_0)         \
    {                          \
        statusLight_0->set(1); \
    }
#define STATUS_WARNING_OFF     \
    if (statusLight_0)         \
    {                          \
        statusLight_0->set(0); \
    }
#define STATUS_ERROR_ON        \
    if (statusLight_1)         \
    {                          \
        statusLight_1->set(1); \
    }
#define STATUS_ERROR_OFF       \
    if (statusLight_1)         \
    {                          \
        statusLight_1->set(0); \
    }

#endif