//
// ZYJMQTTSessionSynchron.m
// ZYJMQTTClient.framework
//
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
//

/**
 Synchronous API
 
 @author Christoph Krey c@ckrey.de
 @see http://ZYJMQTT.org
 */

#import "ZYJMQTTSession.h"
#import "ZYJMQTTSessionLegacy.h"
#import "ZYJMQTTSessionSynchron.h"

#import "ZYJMQTTLog.h"

@interface ZYJMQTTSession()
@property (nonatomic) BOOL synchronPub;
@property (nonatomic) UInt16 synchronPubMid;
@property (nonatomic) BOOL synchronUnsub;
@property (nonatomic) UInt16 synchronUnsubMid;
@property (nonatomic) BOOL synchronSub;
@property (nonatomic) UInt16 synchronSubMid;
@property (nonatomic) BOOL synchronConnect;
@property (nonatomic) BOOL synchronDisconnect;

@end

@implementation ZYJMQTTSession(Synchron)

/** Synchron connect
 *
 */
- (BOOL)connectAndWaitTimeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];
    self.synchronConnect = TRUE;
    
    [self connect];
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronConnect && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
        NSLog(@"[ZYJMQTTSessionSynchron] waiting for connect");
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    
    NSLog(@"[ZYJMQTTSessionSynchron] end connect");
    
    return (self.status == ZYJMQTTSessionStatusConnected);
}

- (BOOL)subscribeAndWaitToTopic:(NSString *)topic atLevel:(ZYJMQTTQosLevel)qosLevel timeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];
    self.synchronSub = TRUE;
    UInt16 mid = [self subscribeToTopic:topic atLevel:qosLevel];
    self.synchronSubMid = mid;
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronSub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
        NSLog(@"[ZYJMQTTSessionSynchron] waiting for suback %d", mid);
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    
    NSLog(@"[ZYJMQTTSessionSynchron] end subscribe");
    
    if (self.synchronSub || self.synchronSubMid != mid) {
        return FALSE;
    } else {
        return TRUE;
    }
}

- (BOOL)subscribeAndWaitToTopics:(NSDictionary<NSString *, NSNumber *> *)topics timeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];
    self.synchronSub = TRUE;
    UInt16 mid = [self subscribeToTopics:topics];
    self.synchronSubMid = mid;
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronSub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
//        NSLog(@"[ZYJMQTTSessionSynchron] waiting for suback %d", mid);
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    
//    NSLog(@"[ZYJMQTTSessionSynchron] end subscribe");
    
    if (self.synchronSub || self.synchronSubMid != mid) {
        return FALSE;
    } else {
        return TRUE;
    }
}

- (BOOL)unsubscribeAndWaitTopic:(NSString *)theTopic timeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];

    self.synchronUnsub = TRUE;
    UInt16 mid = [self unsubscribeTopic:theTopic];
    self.synchronUnsubMid = mid;
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronUnsub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
        NSLog(@"[ZYJMQTTSessionSynchron] waiting for unsuback %d", mid);
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    
    NSLog(@"[ZYJMQTTSessionSynchron] end unsubscribe");
    
    if (self.synchronUnsub || self.synchronUnsubMid != mid) {
        return FALSE;
    } else {
        return TRUE;
    }
}

- (BOOL)unsubscribeAndWaitTopics:(NSArray<NSString *> *)topics timeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];
    self.synchronUnsub = TRUE;
    UInt16 mid = [self unsubscribeTopics:topics];
    self.synchronUnsubMid = mid;
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronUnsub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
        NSLog(@"[ZYJMQTTSessionSynchron] waiting for unsuback %d", mid);
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    
    NSLog(@"[ZYJMQTTSessionSynchron] end unsubscribe");
    
    if (self.synchronUnsub || self.synchronUnsubMid != mid) {
        return FALSE;
    } else {
        return TRUE;
    }
}

- (BOOL)publishAndWaitData:(NSData*)data
                   onTopic:(NSString*)topic
                    retain:(BOOL)retainFlag
                       qos:(ZYJMQTTQosLevel)qos
                   timeout:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];

    if (qos != ZYJMQTTQosLevelAtMostOnce) {
        self.synchronPub = TRUE;
    }
    
    UInt16 mid = self.synchronPubMid = [self publishData:data onTopic:topic retain:retainFlag qos:qos];
    if (qos == ZYJMQTTQosLevelAtMostOnce) {
        return TRUE;
    } else {
        
        [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
        
        while (self.synchronPub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
            NSLog(@"[ZYJMQTTSessionSynchron] waiting for mid %d", mid);
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
        }
        
        NSLog(@"[ZYJMQTTSessionSynchron] end publish");
        
        if (self.synchronPub || self.synchronPubMid != mid) {
            return FALSE;
        } else {
            return TRUE;
        }
    }
}

- (void)closeAndWait:(NSTimeInterval)timeout {
    NSDate *started = [NSDate date];
    self.synchronDisconnect = TRUE;
    [self closeWithDisconnectHandler:nil];
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    
    while (self.synchronDisconnect && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
        NSLog(@"[ZYJMQTTSessionSynchron] waiting for close");
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
    }
    NSLog(@"[ZYJMQTTSessionSynchron] end close");
}

@end
