#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import "XMPPModule.h"

#define _XMPP_RECONNECT_H

#define DEFAULT_XMPP_RECONNECT_DELAY 2.0

#define DEFAULT_XMPP_RECONNECT_TIMER_INTERVAL 20.0


@protocol XMPPReconnectDelegate;

/**
 * XMPPReconnect handles automatically reconnecting to the xmpp server due to accidental disconnections.
 * That is, a disconnection that is not the result of calling disconnect on the xmpp stream.
 * 
 * Accidental disconnections may happen for a variety of reasons.
 * The most common are general connectivity issues such as disconnection from a WiFi access point.
 * 
 * However, there are several of issues that occasionaly occur.
 * There are some routers on the market that disconnect TCP streams after a period of inactivity.
 * In addition to this, there have been iPhone revisions where the OS networking stack would pull the same crap.
 * These issue have been largely overcome due to the keepalive implementation in XMPPStream.
 * 
 * Regardless of how the disconnect happens, the XMPPReconnect class can help to automatically re-establish
 * the xmpp stream so as to have minimum impact on the user (and hopefully they don't even notice).
 * 
 * Once a stream has been opened and authenticated, this class will detect any accidental disconnections.
 * If one occurs, an attempt will be made to automatically reconnect after a short delay.
 * This delay is configurable via the reconnectDelay property.
 * At the same time the class will begin monitoring the network for reachability changes.
 * When the reachability of the xmpp host has changed, a reconnect may be tried again.
 * In addition to all this, a timer may optionally be used to attempt a reconnect periodically.
 * The timer is started if the initial reconnect fails.
 * This reconnect timer is fully configurable (may be enabled/disabled, and it's timeout may be changed).
 * 
 * In all cases, prior to attempting a reconnect,
 * this class will invoke the shouldAttemptAutoReconnect delegate method.
 * The delegate may use this opportunity to optionally decline the auto reconnect attempt.
 * 
 * Auto reconnect may be disabled at any time via the autoReconnect property.
 * 
 * Note that auto reconnect will only occur for a stream that has been opened and authenticated.
 * So it will do nothing, for example, if there is no internet connectivity when your application
 * first launches, and the xmpp stream is unable to connect to the host.
 * In cases such as this it may be desireable to start monitoring the network for reachability changes.
 * This way when internet connectivity is restored, one can immediately connect the xmpp stream.
 * This is possible via the manualStart method,
 * which will trigger the class into action just as if an accidental disconnect occurred.
**/

@interface XMPPReconnect : XMPPModule
{
	Byte flags;
	Byte config;
	NSTimeInterval reconnectDelay;
	
	dispatch_source_t reconnectTimer;
	NSTimeInterval reconnectTimerInterval;
	
	SCNetworkReachabilityRef reachability;
	
	int reconnectTicket;
	
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_5
	SCNetworkConnectionFlags previousReachabilityFlags;
#else
	SCNetworkReachabilityFlags previousReachabilityFlags;
#endif
}

/**
 * Whether auto reconnect is enabled or disabled.
 * 
 * The default value is YES (enabled).
 * 
 * Note: Altering this property will only affect future accidental disconnections.
 * For example, if autoReconnect was true, and you disable this property after an accidental disconnection,
 * this will not stop the current reconnect process.
 * In order to stop a current reconnect process use the stop method.
 * 
 * Similarly, if autoReconnect was false, and you enable this property after an accidental disconnection,
 * this will not start a reconnect process.
 * In order to start a reconnect process use the manualStart method.
**/
@property (nonatomic, assign) BOOL autoReconnect;

/**
 * When the accidental disconnection first happens,
 * a short delay may be used before attempting the reconnection.
 * 
 * The default value is DEFAULT_XMPP_RECONNECT_DELAY (defined at the top of this file).
 * 
 * To disable this feature, set the value to zero.
 * 
 * Note: NSTimeInterval is a double that specifies the time in seconds.
**/
@property (nonatomic, assign) NSTimeInterval reconnectDelay;

/**
 * A reconnect timer may optionally be used to attempt a reconnect periodically.
 * The timer will be started after the initial reconnect delay.
 * 
 * The default value is DEFAULT_XMPP_RECONNECT_TIMER_INTERVAL (defined at the top of this file).
 * 
 * To disable this feature, set the value to zero.
 * 
 * Note: NSTimeInterval is a double that specifies the time in seconds.
**/
@property (nonatomic, assign) NSTimeInterval reconnectTimerInterval;

/**
 * Whether you want to reconnect using the legacy method -[XMPPStream oldSchoolSecureConnectWithTimeout:error:]
 * instead of the standard -[XMPPStream connect:].
 *
 * If you initially connect using -oldSchoolSecureConnectWithTimeout:error:, set this to YES to reconnect the same way.
 *
 * The default value is NO (disabled).
 */
@property (nonatomic, assign) BOOL usesOldSchoolSecureConnect;

/**
 * As opposed to using autoReconnect, this method may be used to manually start the reconnect process.
 * This may be useful, for example, if one needs network monitoring in order to setup the inital xmpp connection.
 * Or if one wants autoReconnect but only in very limited situations which they prefer to control manually.
 * 
 * After invoking this method one can expect the class to act as if an accidental disconnect just occurred.
 * That is, a reconnect attempt will be tried after reconnectDelay seconds,
 * and the class will begin monitoring the network for changes in reachability to the xmpp host.
 * 
 * A manual start of the reconnect process will effectively end once the xmpp stream has been opened.
 * That is, if you invoke manualStart and the xmpp stream is later opened,
 * then future disconnections will not result in an auto reconnect process (unless the autoReconnect property applies).
 * 
 * This method does nothing if the xmpp stream is not disconnected.
**/
- (void)manualStart;

/**
 * Stops the current reconnect process.
 * 
 * This method will stop the current reconnect process regardless of whether the
 * reconnect process was started due to the autoReconnect property or due to a call to manualStart.
 * 
 * Stopping the reconnect process does NOT prevent future auto reconnects if the property is enabled.
 * That is, if the autoReconnect property is still enabled, and the xmpp stream is later opened, authenticated and
 * accidentally disconnected, this class will still attempt an automatic reconnect.
 * 
 * Stopping the reconnect process does NOT prevent future calls to manualStart from working.
 * 
 * It only stops the CURRENT reconnect process.
**/
- (void)stop;

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@protocol XMPPReconnectDelegate
@optional

/**
 * This method may be used to fine tune when we
 * should and should not attempt an auto reconnect.
 * 
 * For example, if on the iPhone, one may want to prevent auto reconnect when WiFi is not available.
**/
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_5

- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags;
- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags;

#else

- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkReachabilityFlags)connectionFlags;
- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkReachabilityFlags)reachabilityFlags;

#endif

@end
