/*---------------------------------------------------------------------------*/
/* mqtt.h                                                                    */
/* copyright (c) innovaphone 2024                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

enum MqttError {
    Success = 0,
    WrongPacketType,
    Malformed,
    BufferAllocationFailed,
    NullBufferRecived,
    MaxBinDataExceeded,
    StringSizeExceeded,
    VariableHeaderError,
    PayloadError,
    FlagSetButNoTopic,
    FlagSetButNoPayload,
    FlagSetButNoUserName,
    FlagSetButNoPassword,
    EmptyData,
    IndexOutOfRange,
    SubscriptionIdentifierMissing,
    IncorrectValue,
    NoConnectionEstablished,
    UnspecifierError,
    PacketTooLarge,
    StringTooLarge,
    OutputBufferToSmall,
    InputClientIdIncorrect,
    RetainNotSupportedByBroker,
    TopicAliasNonExistant,
    TopicAliasNotSupported,
    TopicAliasUnavailable,
    InvalidTopicName
};

enum MQTTReasonCode {
    SUCCESS = 0x00,
    NORMAL_DISCONNECTION = 0x00,
    GRANTED_QOS0 = 0x00,
    GRANTED_QOS1 = 0x01,
    GRANTED_QOS2 = 0x02,
    DISCONNECT_WITH_WILL_MESSAGE = 0x04,
    NO_MATCHING_SUBSCRIBERS = 0x10,
    NO_SUBSCRIPTION_EXISTED = 0x11,
    CONTINUE_AUTHENTICATION = 0x18,
    RE_AUTHENTICATE = 0x19,
    UNSPECIFIED_ERROR = 0x80,
    MALFORMED_PACKET = 0x81,
    PROTOCOL_ERROR = 0x82,
    IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
    UNSUPPORTED_PROTOCOL_VERSION = 0x84,
    CLIENT_IDENTIFIER_NOT_VALID = 0x85,
    BAD_USER_NAME_OR_PASSWORD = 0x86,
    NOT_AUTHORIZED = 0x87,
    SERVER_UNAVAILABLE = 0x88,
    SERVER_BUSY = 0x89,
    BANNED = 0x8A,
    SERVER_SHUTTING_DOWN = 0x8B,
    BAD_AUTHENTICATION_METHOD = 0x8C,
    KEEP_ALIVE_TIMEOUT = 0x8D,
    SESSION_TAKEN_OVER = 0x8E,
    TOPIC_FILTER_INVALID = 0x8F,
    TOPIC_NAME_INVALID = 0x90,
    PACKET_IDENTIFIER_IN_USE = 0x91,
    PACKET_IDENTIFIER_NOT_FOUND = 0x92,
    RECEIVE_MAXIMUM_EXCEEDED = 0x93,
    TOPIC_ALIAS_INVALID = 0x94,
    PACKET_TOO_LARGE = 0x95,
    MESSAGE_RATE_TOO_HIGH = 0x96,
    QUOTA_EXCEEDED = 0x97,
    ADMINISTRATIVE_ACTION = 0x98,
    PAYLOAD_FORMAT_INVALID = 0x99,
    RETAIN_NOT_SUPPORTED = 0x9A,
    QOS_NOT_SUPPORTED = 0x9B,
    USE_ANOTHER_SERVER = 0x9C,
    SERVER_MOVED = 0x9D,
    SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9E,
    CONNECTION_RATE_EXCEEDED = 0x9F,
    MAXIMUM_CONNECT_TIME = 0xA0,
    SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 0xA1,
    WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xA2
};

// MQTT Authentication (AUTH) reason codes
enum MQTTAuthReasonCode {
    ContinueAuthentication = 0x18,
    Reauthenticate = 0x19
};

enum MQTTClientDisconnectReason {
    NormalDisconnection = 0x00,
};

class IMqttClient {
public:
    static IMqttClient* Create(class IIoMux* const iomux,
        class ISocketProvider* const tcpSocketProvider,
        class ISocketProvider* const tlsSocketProvider,
        class UMqttClient* const user,
        class IInstanceLog* const log,
        class IDns* const dns);

    virtual MqttError MQTTConnect(const char* broker, char* outputClientId, size_t outputClientIdSize, word port = 1883, bool tls = false, const char* payloadUserName = nullptr,
        const byte* payloadPassword = nullptr, word payloadPasswordSize = 0, const char* inputClientId = nullptr, word keepAlive = 0, const char* willTopic = nullptr,
        const byte* willPayload = nullptr, word willPayloadSize = 0, byte willQoS = 0, bool willRetain = false, const char* userPropertyName = nullptr, const char* userPropertyValue = nullptr,
        dword sessionExpiryInterval = 0, const char* authenticationMethod = nullptr, const byte* authenticationData = nullptr, word authenticationDataSize = 0,
        word reciveMaximum = 65535, dword maximumPacketSize = 65535, word topicAliasMaximum = 0, bool requestResponseInformation = 0, bool requestProblemInformation = 1,
        dword willDelayInterval = 0, bool payloadFormatIndicator = 0, dword messageExpiryInterval = 0, const char* contentType = nullptr,
        const char* responseTopic = nullptr, const byte* correlationData = nullptr, word correlationDataSize = 0,
        const char* payloadUserPropertyName = nullptr, const char* payloadUserPropertyValue = nullptr) = 0;

    virtual MqttError MQTTSubscribe(word subscriptionIdentifier, const char* topicFilter,
        const char* qosLevels = nullptr, const char* noLocalFlags = nullptr,
        const char* retainFlags = nullptr, const char* retainHandlingFlags = nullptr) = 0;

    virtual MqttError MQTTPublish(const char* topicName, byte QoS = 0, word topicAlias = 0, const byte* payload = nullptr, size_t payloadSize = 0, const char* contentType = nullptr,
        bool retain = false, const char* userPropertyName = nullptr, const char* userPropertyValue = nullptr, dword messageExpiryInterval = 0,
        const char* responseTopic = nullptr, const byte* correlationData = nullptr, word correlationDataSize = 0
    ) = 0;

    virtual MqttError MQTTUnsubscribe(const char* topicFilter, const char* userPropertyName = nullptr, const char* userPropertyValue = nullptr) = 0;

    virtual MqttError MQTTPing() = 0;

    virtual MqttError MQTTDisconnect(MQTTClientDisconnectReason reason = NormalDisconnection, dword sessionExpiryInterval = 0, const char* reasonString = nullptr, const char* userPropertyname = nullptr,
        const char* userPropertyValue = nullptr, const char* serverReference = nullptr) = 0;

    virtual MqttError MQTTSendAuthData(const char* authenticationMethod, const byte* authenticationData, word authenticationDataSize,
        MQTTAuthReasonCode reasonCode = MQTTAuthReasonCode::ContinueAuthentication, const char* reasonString = nullptr,
        const char* userPropertyName = nullptr, const char* userPropertyValue = nullptr) = 0;
    virtual word GetAliasByTopicName(const char* topicName) const = 0;
    virtual const char* GetNameByTopicAlias(word alias) const = 0;

};

class UMqttClient {
public:
    ~UMqttClient() {}

    virtual void MQTTClientConnectComplete(IMqttClient* const mqttClient, bool isNewSession, dword sessionExpiryInterval, word receiveMaximum, byte maxQoS, byte retainAvailable, dword maxPacketSize,
        const char* clientId, dword topicAliasMax, const char* userPropertyName, const char* userPropertyValue, byte wildCardAvailable, byte sharedSubscriptionAvailable, dword serverKeepAlive) = 0;
    virtual void MQTTClientConnectFail(IMqttClient* const mqttClient, MQTTReasonCode reasonCode, const char* reasonString) = 0;
    virtual void MQTTClientDisconnect(IMqttClient* const mqttClient, MQTTReasonCode reasonCode) = 0;

    virtual void MQTTClientSubscribeComplete(IMqttClient* const mqttClient, const char* topic, const char* userPropertyName, const char* userPropertyValue) = 0;
    virtual void MQTTClientSubscribeFail(IMqttClient* const mqttClient, const char* topic, MQTTReasonCode reasonCode) = 0;
    virtual void MQTTClientPublishRecv(IMqttClient* const mqttClient, const char* topic, const char* msg, size_t msg_len, word subscriptionIdentifier,
        const char* userPropertyName, const char* userPropertyValue) = 0;

    virtual void MQTTClientUnsubscribeComplete(IMqttClient* const mqttClient, const char* topic, const char* userPropertyName, const char* userPropertyValue) = 0;
    virtual void MQTTClientUnsubscribeFail(IMqttClient* const mqttClient, const char* topic, MQTTReasonCode reasonCode) = 0;

    virtual void MQTTClientPublishComplete(IMqttClient* const mqttClient, const byte QoS, const char* userPropertyName, const char* userPropertyValue) = 0;
    virtual void MQTTClientPublishFail(IMqttClient* const mqttClient, MQTTReasonCode reasonCode) = 0;

    virtual void MQTTClientPingComplete(IMqttClient* const mqttClient) = 0;

    virtual void MQTTClientAuthenticate(IMqttClient* const mqttClient, MQTTReasonCode reasonCode) = 0;

    virtual void MQTTClientError(IMqttClient* const mqttClient, MqttError reason) = 0;
};