//
// ZYJMQTTSession.h
// ZYJMQTTClient.framework
//

/**
 Using ZYJMQTT in your Objective-C application
 
 @author Christoph Krey c@ckrey.de
 @copyright Copyright © 2013-2017, Christoph Krey. All rights reserved.
 
 based on Copyright (c) 2011, 2013, 2lemetry LLC
    All rights reserved. This program and the accompanying materials
    are made available under the terms of the Eclipse Public License v1.0
    which accompanies this distribution, and is available at
    http://www.eclipse.org/legal/epl-v10.html
 
 @see http://ZYJMQTT.org
 */


#import <Foundation/Foundation.h>

#import "ZYJMQTTMessage.h"
#import "ZYJMQTTPersistence.h"
#import "ZYJMQTTTransport.h"

@class ZYJMQTTSession;
@class ZYJMQTTSSLSecurityPolicy;

/**
 Enumeration of ZYJMQTTSession states
 */
typedef NS_ENUM(NSInteger, ZYJMQTTSessionStatus) {
    ZYJMQTTSessionStatusCreated,
    ZYJMQTTSessionStatusConnecting,
    ZYJMQTTSessionStatusConnected,
    ZYJMQTTSessionStatusDisconnecting,
    ZYJMQTTSessionStatusClosed,
    ZYJMQTTSessionStatusError
};

/**
 Enumeration of ZYJMQTTSession events
 */
typedef NS_ENUM(NSInteger, ZYJMQTTSessionEvent) {
    ZYJMQTTSessionEventConnected,
    ZYJMQTTSessionEventConnectionRefused,
    ZYJMQTTSessionEventConnectionClosed,
    ZYJMQTTSessionEventConnectionError,
    ZYJMQTTSessionEventProtocolError,
    ZYJMQTTSessionEventConnectionClosedByBroker
};

/**
 The error domain used for all errors created by ZYJMQTTSession
 */
extern NSString * const ZYJMQTTSessionErrorDomain;

/**
 The error codes used for all errors created by ZYJMQTTSession
 */
typedef NS_ENUM(NSInteger, ZYJMQTTSessionError) {
    ZYJMQTTSessionErrorConnectionRefused = -8, // Sent if the server closes the connection without sending an appropriate error CONNACK
    ZYJMQTTSessionErrorIllegalMessageReceived = -7,
    ZYJMQTTSessionErrorDroppingOutgoingMessage = -6, // For some reason the value is the same as for ZYJMQTTSessionErrorNoResponse
    ZYJMQTTSessionErrorNoResponse = -6, // For some reason the value is the same as for ZYJMQTTSessionErrorDroppingOutgoingMessage
    ZYJMQTTSessionErrorEncoderNotReady = -5,
    ZYJMQTTSessionErrorInvalidConnackReceived = -2, // Sent if the message received from server was an invalid connack message
    ZYJMQTTSessionErrorNoConnackReceived = -1, // Sent if first message received from server was no connack message

    ZYJMQTTSessionErrorConnackUnacceptableProtocolVersion = 1, // Value as defined by ZYJMQTT Protocol
    ZYJMQTTSessionErrorConnackIdentifierRejected = 2, // Value as defined by ZYJMQTT Protocol
    ZYJMQTTSessionErrorConnackServeUnavailable = 3, // Value as defined by ZYJMQTT Protocol
    ZYJMQTTSessionErrorConnackBadUsernameOrPassword = 4, // Value as defined by ZYJMQTT Protocol
    ZYJMQTTSessionErrorConnackNotAuthorized = 5, // Value as defined by ZYJMQTT Protocol
    ZYJMQTTSessionErrorConnackReserved = 6, // Should be value 6-255, as defined by ZYJMQTT Protocol
};

/** Session delegate gives your application control over the ZYJMQTTSession
 @note all callback methods are optional
 */

@protocol ZYJMQTTSessionDelegate <NSObject>

@optional

/** gets called when a new message was received
 @param session the ZYJMQTTSession reporting the new message
 @param data the data received, might be zero length
 @param topic the topic the data was published to
 @param qos the qos of the message
 @param retained indicates if the data retransmitted from server storage
 @param mid the Message Identifier of the message if qos = 1 or 2, zero otherwise
 */
- (void)newMessage:(ZYJMQTTSession *)session
              data:(NSData *)data
           onTopic:(NSString *)topic
               qos:(ZYJMQTTQosLevel)qos
          retained:(BOOL)retained
               mid:(unsigned int)mid;

/** gets called when a new message was received
 @param session the ZYJMQTTSession reporting the new message
 @param data the data received, might be zero length
 @param topic the topic the data was published to
 @param qos the qos of the message
 @param retained indicates if the data retransmitted from server storage
 @param mid the Message Identifier of the message if qos = 1 or 2, zero otherwise
 @return true if the message was or will be processed, false if the message shall not be ack-ed
 */
- (BOOL)newMessageWithFeedback:(ZYJMQTTSession *)session
                          data:(NSData *)data
                       onTopic:(NSString *)topic
                           qos:(ZYJMQTTQosLevel)qos
                      retained:(BOOL)retained
                           mid:(unsigned int)mid;

/** for ZYJMQTTio-OBJC backward compatibility
 @param session see newMessage for description
 @param data see newMessage for description
 @param topic see newMessage for description
 */
- (void)session:(ZYJMQTTSession*)session newMessage:(NSData*)data onTopic:(NSString*)topic;

/** gets called when a connection is established, closed or a problem occurred
 @param session the ZYJMQTTSession reporting the event
 @param eventCode the code of the event
 @param error an optional additional error object with additional information
 */
- (void)handleEvent:(ZYJMQTTSession *)session event:(ZYJMQTTSessionEvent)eventCode error:(NSError *)error;

/** for ZYJMQTTio-OBJC backward compatibility
 @param session the ZYJMQTTSession reporting the event
 @param eventCode the code of the event
 */
- (void)session:(ZYJMQTTSession*)session handleEvent:(ZYJMQTTSessionEvent)eventCode;

/** gets called when a connection has been successfully established
 @param session the ZYJMQTTSession reporting the connect
 
 */
- (void)connected:(ZYJMQTTSession *)session;

/** gets called when a connection has been successfully established
 @param session the ZYJMQTTSession reporting the connect
 @param sessionPresent represents the Session Present flag sent by the broker
 
 */
- (void)connected:(ZYJMQTTSession *)session sessionPresent:(BOOL)sessionPresent;

/** gets called when a connection has been refused
 @param session the ZYJMQTTSession reporting the refusal
 @param error an optional additional error object with additional information
 */
- (void)connectionRefused:(ZYJMQTTSession *)session error:(NSError *)error;

/** gets called when a connection has been closed
 @param session the ZYJMQTTSession reporting the close

 */
- (void)connectionClosed:(ZYJMQTTSession *)session;

/** gets called when a connection error happened
 @param session the ZYJMQTTSession reporting the connect error
 @param error an optional additional error object with additional information
 */
- (void)connectionError:(ZYJMQTTSession *)session error:(NSError *)error;

/** gets called when an ZYJMQTT protocol error happened
 @param session the ZYJMQTTSession reporting the protocol error
 @param error an optional additional error object with additional information
 */
- (void)protocolError:(ZYJMQTTSession *)session error:(NSError *)error;

/** gets called when a published message was actually delivered
 @param session the ZYJMQTTSession reporting the delivery
 @param msgID the Message Identifier of the delivered message
 @note this method is called after a publish with qos 1 or 2 only
 */
- (void)messageDelivered:(ZYJMQTTSession *)session msgID:(UInt16)msgID;

/** gets called when a published message was actually delivered
 @param session the ZYJMQTTSession reporting the delivery
 @param msgID the Message Identifier of the delivered message
 @param topic the topic of the delivered message
 @param data the data Identifier of the delivered message
 @param qos the QoS level of the delivered message
 @param retainFlag the retain Flag of the delivered message
 @note this method is called after a publish with qos 1 or 2 only
 */
- (void)messageDelivered:(ZYJMQTTSession *)session
                   msgID:(UInt16)msgID
                   topic:(NSString *)topic
                    data:(NSData *)data
                     qos:(ZYJMQTTQosLevel)qos
              retainFlag:(BOOL)retainFlag;

/** gets called when a subscription is acknowledged by the ZYJMQTT broker
 @param session the ZYJMQTTSession reporting the acknowledge
 @param msgID the Message Identifier of the SUBSCRIBE message
 @param qoss an array containing the granted QoS(s) related to the SUBSCRIBE message
    (see subscribeTopic, subscribeTopics)
 */
- (void)subAckReceived:(ZYJMQTTSession *)session msgID:(UInt16)msgID grantedQoss:(NSArray<NSNumber *> *)qoss;

/** gets called when an unsubscribe is acknowledged by the ZYJMQTT broker
 @param session the ZYJMQTTSession reporting the acknowledge
 @param msgID the Message Identifier of the UNSUBSCRIBE message
 */
- (void)unsubAckReceived:(ZYJMQTTSession *)session msgID:(UInt16)msgID;

/** gets called when a command is sent to the ZYJMQTT broker
 use this for low level monitoring of the ZYJMQTT connection
 @param session the ZYJMQTTSession reporting the sent command
 @param type the ZYJMQTT command type
 @param qos the Quality of Service of the command
 @param retained the retained status of the command
 @param duped the duplication status of the command
 @param mid the Message Identifier of the command
 @param data the payload data of the command if any, might be zero length
 */
- (void)sending:(ZYJMQTTSession *)session type:(ZYJMQTTCommandType)type qos:(ZYJMQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;

/** gets called when a command is received from the ZYJMQTT broker
 use this for low level monitoring of the ZYJMQTT connection
 @param session the ZYJMQTTSession reporting the received command
 @param type the ZYJMQTT command type
 @param qos the Quality of Service of the command
 @param retained the retained status of the command
 @param duped the duplication status of the command
 @param mid the Message Identifier of the command
 @param data the payload data of the command if any, might be zero length
 */
- (void)received:(ZYJMQTTSession *)session type:(ZYJMQTTCommandType)type qos:(ZYJMQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;

/** gets called when a command is received from the ZYJMQTT broker
 use this for low level control of the ZYJMQTT connection
 @param session the ZYJMQTTSession reporting the received command
 @param type the ZYJMQTT command type
 @param qos the Quality of Service of the command
 @param retained the retained status of the command
 @param duped the duplication status of the command
 @param mid the Message Identifier of the command
 @param data the payload data of the command if any, might be zero length
 @return true if the sessionmanager should ignore the received message
 */
- (BOOL)ignoreReceived:(ZYJMQTTSession *)session type:(ZYJMQTTCommandType)type qos:(ZYJMQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;

/** gets called when the content of ZYJMQTTClients internal buffers change
 use for monitoring the completion of transmitted and received messages
 @param session the ZYJMQTTSession reporting the change
 @param queued for backward compatibility only: ZYJMQTTClient does not queue messages anymore except during QoS protocol
 @param flowingIn the number of incoming messages not acknowledged by the ZYJMQTTClient yet
 @param flowingOut the number of outgoing messages not yet acknowledged by the ZYJMQTT broker
 */
- (void)buffered:(ZYJMQTTSession *)session
          queued:(NSUInteger)queued
       flowingIn:(NSUInteger)flowingIn
      flowingOut:(NSUInteger)flowingOut;

/** gets called when the content of ZYJMQTTClients internal buffers change
 use for monitoring the completion of transmitted and received messages
 @param session the ZYJMQTTSession reporting the change
 @param flowingIn the number of incoming messages not acknowledged by the ZYJMQTTClient yet
 @param flowingOut the number of outgoing messages not yet acknowledged by the ZYJMQTT broker
 */
- (void)buffered:(ZYJMQTTSession *)session
       flowingIn:(NSUInteger)flowingIn
      flowingOut:(NSUInteger)flowingOut;

@end

typedef void (^ZYJMQTTConnectHandler)(NSError *error);
typedef void (^ZYJMQTTDisconnectHandler)(NSError *error);
typedef void (^ZYJMQTTSubscribeHandler)(NSError *error, NSArray<NSNumber *> *gQoss);
typedef void (^ZYJMQTTUnsubscribeHandler)(NSError *error);
typedef void (^ZYJMQTTPublishHandler)(NSError *error);

/** Session implements the ZYJMQTT protocol for your application
 *
 */

@interface ZYJMQTTSession : NSObject

/** set this member variable to receive delegate messages
 @code
 #import "ZYJMQTTClient.h"
 
 @interface MyClass : NSObject <ZYJMQTTSessionDelegate>
 ...
 @end
 
 ...
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 session.delegate = self;
 ...
 - (void)handleEvent:(ZYJMQTTSession *)session
        event:(ZYJMQTTSessionEvent)eventCode
        error:(NSError *)error {
    ...
 }
 - (void)newMessage:(ZYJMQTTSession *)session
        data:(NSData *)data
        onTopic:(NSString *)topic
        qos:(ZYJMQTTQosLevel)qos
        retained:(BOOL)retained
        mid:(unsigned int)mid {
    ...
 }
 @endcode
 
 */
@property (weak, nonatomic) id<ZYJMQTTSessionDelegate> delegate;

/** Control ZYJMQTT persistence by setting the properties of persistence before connecting to an ZYJMQTT broker.
    The settings are specific to a clientId.
 
    persistence.persistent = YES or NO (default) to establish file or in memory persistence. IMPORTANT: set immediately after creating the ZYJMQTTSession before calling any other method. Otherwise the default value (NO) will be used
        for this session.
 
    persistence.maxWindowSize (a positive number, default is 16) to control the number of messages sent before waiting for acknowledgement in Qos 1 or 2. Additional messages are
        stored and transmitted later.
 
    persistence.maxSize (a positive number of bytes, default is 64 MB) to limit the size of the persistence file. Messages published after the limit is reached are dropped.
 
    persistence.maxMessages (a positive number, default is 1024) to limit the number of messages stored. Additional messages published are dropped.
 
    Messages are deleted after they have been acknowledged.
*/
@property (strong, nonatomic) id<ZYJMQTTPersistence> persistence;

/** block called once when connection is established
 */
@property (copy, nonatomic) ZYJMQTTConnectHandler connectHandler;

/** block called when connection is established
 */
@property (strong) void (^connectionHandler)(ZYJMQTTSessionEvent event);

/** block called when message is received
 */
@property (strong) void (^messageHandler)(NSData* message, NSString* topic);

/** Session status
 */
@property (nonatomic, readonly) ZYJMQTTSessionStatus status;

/** Indicates if the broker found a persistent session when connecting with cleanSession:FALSE
 */
@property (nonatomic, readonly) BOOL sessionPresent;

/** streamSSLLevel an NSString containing the security level for read and write streams
 * For list of possible values see:
 * https://developer.apple.com/documentation/corefoundation/cfstream/cfstream_socket_security_level_constants
 * Please also note that kCFStreamSocketSecurityLevelTLSv1_2 is not in a list
 * and cannot be used as constant, but you can use it as a string value
 * defaults to kCFStreamSocketSecurityLevelNegotiatedSSL
 */
@property (strong, nonatomic) NSString *streamSSLLevel;

/** host an NSString containing the hostName or IP address of the Server
 */
@property (readonly) NSString *host;

/** port an unsigned 32 bit integer containing the IP port number of the Server
 */
@property (readonly) UInt32 port;

/** The Client Identifier identifies the Client to the Server. If nil, a random clientId is generated.
 */
@property (strong, nonatomic) NSString *clientId;

/** see userName an NSString object containing the user's name (or ID) for authentication. May be nil. */
@property (strong, nonatomic) NSString *userName;

/** see password an NSString object containing the user's password. If userName is nil, password must be nil as well.*/
@property (strong, nonatomic) NSString *password;

/** see keepAliveInterval The Keep Alive is a time interval measured in seconds.
 * The ZYJMQTTClient ensures that the interval between Control Packets being sent does not exceed
 * the Keep Alive value. In the  absence of sending any other Control Packets, the Client sends a PINGREQ Packet.
 */
@property (nonatomic) UInt16 keepAliveInterval;

/** The serverKeepAlive is a time interval measured in seconds.
 *  This value may be set by the broker and overrides keepAliveInterval if present
 *  Zero means the broker does not perform any keep alive checks
 */
@property (readonly, strong, nonatomic) NSNumber *serverKeepAlive;

/** effectiveKeepAlive is a time interval measured in seconds
 *  It indicates the effective keep alive interval after a successfull connect
 *  where keepAliveInterval might have been overridden by the broker.
 */
@property (readonly, nonatomic) UInt16 effectiveKeepAlive;


/**
 * dupTimeout If PUBACK or PUBREC not received, message will be resent after this interval
 */
@property (nonatomic) double dupTimeout;

/** leanSessionFlag specifies if the server should discard previous session information. */
@property (nonatomic) BOOL cleanSessionFlag;

/** willFlag If the Will Flag is set to YES this indicates that
 * a Will Message MUST be published by the Server when the Server detects
 * that the Client is disconnected for any reason other than the Client flowing a DISCONNECT Packet.
 */
@property (nonatomic) BOOL willFlag;

/** willTopic If the Will Flag is set to YES, the Will Topic is a string, nil otherwise. */
@property (strong, nonatomic) NSString *willTopic;

/** willMsg If the Will Flag is set to YES the Will Message must be specified, nil otherwise. */
@property (strong, nonatomic) NSData *willMsg;

/** willQoS specifies the QoS level to be used when publishing the Will Message.
 * If the Will Flag is set to NO, then the Will QoS MUST be set to 0.
 * If the Will Flag is set to YES, the Will QoS MUST be a valid ZYJMQTTQosLevel.
 */
@property (nonatomic) ZYJMQTTQosLevel willQoS;

/** willRetainFlag indicates if the server should publish the Will Messages with retainFlag.
 * If the Will Flag is set to NO, then the Will Retain Flag MUST be set to NO .
 * If the Will Flag is set to YES: If Will Retain is set to NO, the Serve
 * MUST publish the Will Message as a non-retained publication [ZYJMQTT-3.1.2-14].
 * If Will Retain is set to YES, the Server MUST publish the Will Message as a retained publication [ZYJMQTT-3.1.2-15].
 */
@property (nonatomic) BOOL willRetainFlag;

/** protocolLevel specifies the protocol to be used */
@property (nonatomic) ZYJMQTTProtocolVersion protocolLevel;

/** sessionExpiryInterval specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *sessionExpiryInterval;

/** authMethod specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSString *authMethod;

/** authData specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSData *authData;

/** requestProblemInformation specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *requestProblemInformation;

/** willDelayInterval specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *willDelayInterval;

/** requestResponseInformation specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *requestResponseInformation;

/** receiveMaximum specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *receiveMaximum;

/** topicAliasMaximum specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *topicAliasMaximum;

/** topicAliasMaximum specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSDictionary <NSString *, NSString*> *userProperty;

/** maximumPacketSize specifies the number of seconds after which a session should expire ZYJMQTT v5.0*/
@property (strong, nonatomic) NSNumber *maximumPacketSize;

/** queue The queue where the streams are scheduled. */
@property (strong, nonatomic) dispatch_queue_t queue;


/** for ZYJMQTTio-OBJC backward compatibility
 the connect message used is stored here
 */
@property (strong, nonatomic) ZYJMQTTMessage *connectMessage;

/** the transport provider for ZYJMQTTClient
 *
 * assign an in instance of a class implementing the ZYJMQTTTransport protocol e.g.
 * ZYJMQTTCFSocketTransport before connecting.
 */
@property (strong, nonatomic) id <ZYJMQTTTransport> transport;

/** certificates an NSArray holding client certificates or nil */
@property (strong, nonatomic) NSArray *certificates;

/** Require for VoIP background service
 * defaults to NO
 */
@property (nonatomic) BOOL voip;

/** connect to the given host through the given transport with the given
 *  ZYJMQTT session parameters asynchronously
 *
 */


- (void)connect;

/** connects to the specified ZYJMQTT server
 
 @param connectHandler identifies a block which is executed on successfull or unsuccessfull connect. Might be nil
 error is nil in the case of a successful connect
 sessionPresent indicates in ZYJMQTT 3.1.1 if persistent session data was present at the server
 returns nothing and returns immediately. To check the connect results, register as an ZYJMQTTSessionDelegate and
 - watch for events
 - watch for connect or connectionRefused messages
 - watch for error messages
 or use the connectHandler block
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connectWithConnectHandler:^(NSError *error, BOOL sessionPresent) {
 if (error) {
 NSLog(@"Error Connect %@", error.localizedDescription);
 } else {
 NSLog(@"Connected sessionPresent:%d", sessionPresent);
 }
 }];
 @endcode
 
 */

- (void)connectWithConnectHandler:(ZYJMQTTConnectHandler)connectHandler;


/** disconnect gracefully
 *
 */
- (void)disconnect;

/** disconnect V5
 *  @param returnCode the returncode send to the broker
 *  @param sessionExpiryInterval the time in seconds before the session can be deleted
 *  @param reasonString a string explaining the reason
 *  @param userProperty additional dictionary of user key/value combinations
 */
- (void)disconnectWithReturnCode:(ZYJMQTTReturnCode)returnCode
           sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
                    reasonString:(NSString *)reasonString
                    userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;


/** initialises the ZYJMQTT session with default values
 @return the initialised ZYJMQTTSession object
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 @endcode
 */
- (ZYJMQTTSession *)init;



/** subscribes to a topic at a specific QoS level
 
 @param topic see subscribeToTopic:atLevel:subscribeHandler: for description
 @param qosLevel  see subscribeToTopic:atLevel:subscribeHandler: for description
 @return the Message Identifier of the SUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 ...
 [session subscribeToTopic:@"example/#" atLevel:2];
 
 @endcode
 
 */

- (UInt16)subscribeToTopic:(NSString *)topic
                   atLevel:(ZYJMQTTQosLevel)qosLevel;
/** subscribes to a topic at a specific QoS level
 
 @param topic the Topic Filter to subscribe to.
 
 @param qosLevel specifies the QoS Level of the subscription.
 qosLevel can be 0, 1, or 2.
 @param subscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
 Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
 array of grantes Qos
 
 
 @return the Message Identifier of the SUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 ...
 [session subscribeToTopic:@"example/#" atLevel:2 subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss){
    if (error) {
        NSLog(@"Subscription failed %@", error.localizedDescription);
    } else {
        NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
    }
 }];
 
 @endcode
 
 */

- (UInt16)subscribeToTopic:(NSString *)topic
                   atLevel:(ZYJMQTTQosLevel)qosLevel
          subscribeHandler:(ZYJMQTTSubscribeHandler)subscribeHandler;

/** subscribes a number of topics
 
 @param topics an NSDictionary<NSString *, NSNumber *> containing the Topic Filters to subscribe to as keys and
    the corresponding QoS as NSNumber values
 
 @return the Message Identifier of the SUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session subscribeToTopics:@{
 @"example/#": @(0),
 @"example/status": @(2),
 @"other/#": @(1)
 }];
 
 @endcode
 */


- (UInt16)subscribeToTopics:(NSDictionary<NSString *, NSNumber *> *)topics;

/** subscribes a number of topics
 
 @param topics an NSDictionary<NSString *, NSNumber *> containing the Topic Filters to subscribe to as keys and
    the corresponding QoS as NSNumber values
 @param subscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
    Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
    array of grantes Qos
 
 @return the Message Identifier of the SUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session subscribeToTopics:@{
    @"example/#": @(0),
    @"example/status": @(2),
    @"other/#": @(1)
 } subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss){
    if (error) {
        NSLog(@"Subscription failed %@", error.localizedDescription);
    } else {
        NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
    }
 }];

 
 @endcode
 */


- (UInt16)subscribeToTopics:(NSDictionary<NSString *, NSNumber *> *)topics
           subscribeHandler:(ZYJMQTTSubscribeHandler)subscribeHandler;

/** unsubscribes from a topic
 
 @param topic the Topic Filter to unsubscribe from.

 @return the Message Identifier of the UNSUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session unsubscribeTopic:@"example/#"];
 
 @endcode
 */

- (UInt16)unsubscribeTopic:(NSString *)topic;

/** unsubscribes from a topic
 
 @param topic the Topic Filter to unsubscribe from.
 @param unsubscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
 Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
 array of grantes Qos
 
 @return the Message Identifier of the UNSUBSCRIBE message.
 
 @note returns immediately.
 
 */


- (UInt16)unsubscribeTopic:(NSString *)topic
        unsubscribeHandler:(ZYJMQTTUnsubscribeHandler)unsubscribeHandler;

/** unsubscribes from a number of topics
 
 @param topics an NSArray<NSString *> of topics to unsubscribe from
 
 @return the Message Identifier of the UNSUBSCRIBE message.
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session unsubscribeTopics:@[
 @"example/#",
 @"example/status",
 @"other/#"
 ]];
 
 @endcode
 
 */

- (UInt16)unsubscribeTopics:(NSArray<NSString *> *)topics;

/** unsubscribes from a number of topics
 
 @param topics an NSArray<NSString *> of topics to unsubscribe from
 
 @param unsubscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
    Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
    array of grantes Qos
 
 @return the Message Identifier of the UNSUBSCRIBE message.
 
 @note returns immediately.
 
 */
- (UInt16)unsubscribeTopics:(NSArray<NSString *> *)topics
         unsubscribeHandler:(ZYJMQTTUnsubscribeHandler)unsubscribeHandler;

/** publishes data on a given topic at a specified QoS level and retain flag
 
 @param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
 @param topic the Topic to identify the data
 @param retainFlag if YES, data is stored on the ZYJMQTT broker until overwritten by the next publish with retainFlag = YES
 @param qos specifies the Quality of Service for the publish
 qos can be 0, 1, or 2.
 @return the Message Identifier of the PUBLISH message. Zero if qos 0. If qos 1 or 2, zero if message was dropped
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session publishData:[@"Sample Data" dataUsingEncoding:NSUTF8StringEncoding]
 topic:@"example/data"
 retain:YES
 qos:1];
 @endcode
 
 */

- (UInt16)publishData:(NSData *)data
              onTopic:(NSString *)topic
               retain:(BOOL)retainFlag
                  qos:(ZYJMQTTQosLevel)qos;

/** publishes data on a given topic at a specified QoS level and retain flag
 
 @param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
 @param topic the Topic to identify the data
 @param retainFlag if YES, data is stored on the ZYJMQTT broker until overwritten by the next publish with retainFlag = YES
 @param qos specifies the Quality of Service for the publish
 qos can be 0, 1, or 2.
 
 
 @param publishHandler identifies a block which is executed on successfull or unsuccessfull publsh. Might be nil
 error is nil in the case of a successful connect
 sessionPresent indicates in ZYJMQTT 3.1.1 if persistent session data was present at the server
 

 @return the Message Identifier of the PUBLISH message. Zero if qos 0. If qos 1 or 2, zero if message was dropped
 
 @note returns immediately. To check results, register as an ZYJMQTTSessionDelegate and watch for events.
 
 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 [session publishData:[@"Sample Data" dataUsingEncoding:NSUTF8StringEncoding]
 topic:@"example/data"
 retain:YES
 qos:1
 publishHandler:^(NSError *error){
 if (error) {
 NSLog(@"error: %@ %@", error.localizedDescription, payload);
 } else {
 NSLog(@"delivered:%@", payload);
 delivered++;
 }
 }];
 @endcode
 
 */

- (UInt16)publishData:(NSData *)data
              onTopic:(NSString *)topic
               retain:(BOOL)retainFlag
                  qos:(ZYJMQTTQosLevel)qos
       publishHandler:(ZYJMQTTPublishHandler)publishHandler;

/** closes an ZYJMQTTSession gracefully
 
 If the connection was successfully established before, a DISCONNECT is sent.
 
 @param disconnectHandler identifies a block which is executed on successfull or unsuccessfull disconnect. Might be nil. error is nil in the case of a successful disconnect

 @code
 #import "ZYJMQTTClient.h"
 
 ZYJMQTTSession *session = [[ZYJMQTTSession alloc] init];
 ...
 [session connect];
 
 ...
 
 [session closeWithDisconnectHandler^(NSError *error) {
    if (error) {
        NSLog(@"Error Disconnect %@", error.localizedDescription);
    }
    NSLog(@"Session closed");
 }];

 
 @endcode
 
 */
- (void)closeWithDisconnectHandler:(ZYJMQTTDisconnectHandler)disconnectHandler;

/** close V5
 *  @param returnCode the returncode send to the broker
 *  @param sessionExpiryInterval the time in seconds before the session can be deleted
 *  @param reasonString a string explaining the reason
 *  @param userProperty additional dictionary of user key/value combinations
 *  @param disconnectHandler will be called when the disconnect finished
 */
- (void)closeWithReturnCode:(ZYJMQTTReturnCode)returnCode
      sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
               reasonString:(NSString *)reasonString
               userProperty:(NSDictionary <NSString *, NSString *> *)userProperty
          disconnectHandler:(ZYJMQTTDisconnectHandler)disconnectHandler;

@end
