@startuml

skinparam backgroundColor White
skinparam classBackgroundColor LightGray
skinparam arrowColor DarkBlue
skinparam classBorderColor DarkBlue

title Service

enum ServiceId {
    APP,
    WIFI,
    MQTT,
    OLED,
    JOYSTICK,
    MAX_ID
}

interface IService {
    ServiceId getServiceId()
    void setup()
    void loop()
    IRegistry *getRegistry()
    IMessageBus *getMessageBus()
}

interface IPropertiesSource {
    std::string getStr(const std::string &name, const std::string &def)
    void setStr(const std::string &name, const std::string &value)
    uint16_t getUint16(const std::string &name, uint16_t def)
    void setUint16(const std::string &name, uint16_t value)
    Property *getProperty(const std::string &name)
    void setProperty(const std::string &name, Property* props)
    template<class T> T *get(const std::string &name)
}

interface IRegistry {
    template<typename C, typename ... T> void create(T &&... all)
    void add(IService *service)
    ServiceArray &getServices()
    template<typename C> C *getService(ServiceId id)
    IMessageBus *getMessageBus()
    IPropertiesSource *getProperties()
}

class Service {
    -IRegistry* _registry
    IRegistry* getRegistry()
    void setup()
    void loop()
    IMessageBus *getMessageBus()
}

IService <|-- Service

class InCodePropertiesSource {
    std::string getStr(const std::string &name, const std::string &def)
    void setStr(const std::string &name, const std::string &value)
    uint16_t getUint16(const std::string &name, uint16_t def)
    void setUint16(const std::string &name, uint16_t value)
    Property *getProperty(const std::string &name)
    void setProperty(const std::string &name, Property* props)
}

IPropertiesSource <|-- InCodePropertiesSource

class Registry {
    -ServiceArray _services
    -IMessageBus *_bus
    -IPropertiesSource *_props
    void add(IService *service)
    ServiceArray &getServices()
    IMessageBus *getMessageBus()
    IPropertiesSource *getProperties()
}

IRegistry <|-- Registry

class WifiService {
    -Ticker _reconnectTimer;
    -void raiseConnect()
    ServiceId getServiceId() const
    {static} void onTimer(WifiService *service)
    void setup()
    void onWifiEvent(arduino_event_id_t event, arduino_event_info_t info)
}

Service <|.. WifiService

class MQTTService {
    -AsyncMqttClient _mqttClient
    -std::string _clientId
    -std::string _username
    -std::string _password
    -std::string _host
    -uint16_t _port{}
    ServiceId getServiceId()
    void setup()
    void onMessage(const char *topic, const char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
    void onMqttConnect(bool sessionPresent)
    void onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
    void onMqttSubscribe(uint16_t packetId, uint8_t qos)
    void onMqttPublish(uint16_t packetId)
    void onMessage(const WifiConnected &msg)
    void onMessage(const WifiDisconnected &msg)
    void publish(std::string_view topic, std::string_view data)
}

Service <|.. MQTTService

class JoystickService {
    -JoystickEvent _event;
    -JoystickProperties *_props{nullptr};

    ServiceId getServiceId()
    void setup()
    void loop()
}

Service <|.. JoystickService

class DisplayService {
    -std::unique_ptr<U8G2> _display;
    -unsigned long _lastUpdate{0};
    -std::array<std::string, 5> _lines;

    -void refresh()

    ServiceId getServiceId() const
    void setText(int line, std::string_view text)

    void setup()
    void loop()
}

Service <|.. DisplayService

IPropertiesSource --r[hidden]-> IService
ServiceId --r[hidden]-> IService

@enduml