//
// © 2018-2020 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
//

#import "IoTAllPlayController.h"
#import "IoTAppListenerProtocol.h"
#import "IoTServiceDiscoveryResult.h"
#import "IoTWrappedRepresentation.h"
#import "IoTGlobals.h"
#import "IoTTransport.h"

@interface IoTAllPlayController ()

@property (nonatomic, copy) NSSet<NSString *> *discoveryResourceTypes;
@property (nonatomic, copy) NSString *uriPrefix;

@property (nonatomic, assign) NSUInteger foundResources;
@property (nonatomic, assign) NSUInteger failedResourceScans;
@property (nonatomic, readonly) dispatch_queue_t internalQueue;
@property (nonatomic, readonly) dispatch_queue_t stateSyncQueue;
@property (nonatomic, readonly) id<IoTTransport> transport;
@property (nonatomic, assign) BOOL observing; 

// Resource clients

{% for swagger_data in swagger_objects %}
{% if "allplay" in swagger_data.type %}
@property (nonatomic, strong) IoT{{swagger_data.name|class_name_syntax}}ResourceClient *{{swagger_data.name|variable_syntax(True, False)}}ResourceClient;
{% endif %}
{% endfor %}

@end

@implementation IoTAllPlayController

- (instancetype)initWithServiceInfo:(IoTServiceDiscoveryResult *)serviceInfo
                    stateSyncQueue:(dispatch_queue_t)syncQueue
                         transport:(id<IoTTransport>)transport
{
    self = [self init];
    if (self) {
        self->_stateSyncQueue = syncQueue;
        self->_internalQueue = dispatch_queue_create("com.qualcomm.qti.allplaycontroller.internalQueue", DISPATCH_QUEUE_SERIAL);
        self->_transport = transport;
        self->_observing = NO;
        self->_foundResources = 0;
        self->_failedResourceScans = 0;
        self->_serviceInfo = serviceInfo;
        self->_discoveryResourceTypes = [NSSet setWithArray: @[
 /*
 {% for swagger_data in swagger_objects %}
 {% if "allplay" in swagger_data.type %}
            @{{swagger_data.type}},
{% endif %}
{% endfor %}
*/
            @"qti.allplay.r.mediaPlayer",
        ]];
        self->_uriPrefix = @"/qti/allplay";
    }
    return self;
}

- (void)setDiscoveryParams:(NSSet<NSString *> *)resourcetypes uriPrefix:(NSString *)uriPrefix
{
    self.discoveryResourceTypes = resourcetypes;
    self.uriPrefix = uriPrefix;
}

- (void)discoverWithCompletion:(void(^)(BOOL))completion
               completionQueue:(dispatch_queue_t)completionQueue
{
    makeWeak(self)
	[self.transport scanForResourcesForHost:self.serviceInfo.host
                                  uriPrefix:self.uriPrefix
                         resourceDiscovered:^(NSString * _Nonnull resource) {
                             makeStrongAndReturnIfNil(self)
                             BOOL isRequiredResource = [self.discoveryResourceTypes member:resource] != nil;
                             if (isRequiredResource) {
                                 self.foundResources ++;
                                 NSLog(@"****** We have found a required resource: %@ (%lu of %lu)",
                                       resource,
                                       (unsigned long)self.foundResources,
                                       (unsigned long)self.discoveryResourceTypes.count);
                                 if (self.foundResources == self.discoveryResourceTypes.count) {
                                     NSLog(@"****** We have found all required resources");
                                     [self createResourceClientsForHost:self.serviceInfo.host];
                                     if (completion && completionQueue) {
                                         dispatch_async(completionQueue, ^{
                                             completion(YES);
                                         });
                                     }
                                 }
                             }
                         } queue:self.internalQueue];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.internalQueue, ^{
        makeStrongAndReturnIfNil(self)
        if (self.foundResources != self.discoveryResourceTypes.count) {
            self.failedResourceScans ++;
            if (self.failedResourceScans < 5) {
                NSLog(@"Have not found all required resources on %@ starting again", self.serviceInfo.host);
                self.foundResources = 0;
                [self discoverWithCompletion:completion completionQueue:completionQueue];
            } else {
                NSLog(@"Have not found all required resources on %@ and have exceeded retry limit", self.serviceInfo.host);
                if (completion && completionQueue) {
                    dispatch_async(completionQueue, ^{
                        completion(NO);
                    });
                }
            }
        }
    });
}

- (void)createResourceClientsForHost:(NSString *)host
{
{% for swagger_data in swagger_objects %}
{% if "allplay" in swagger_data.type %}
    self.{{swagger_data.name|variable_syntax(True, False)}}ResourceClient = [[IoT{{swagger_data.name|class_name_syntax}}ResourceClient alloc] initWithHost:host
                                                    dispatcher:[self.transport dispatcher]];
{% endif %}
{% endfor %}
}

- (void)startObserving
{
    self.observing = YES;
    makeWeak(self)
{% for swagger_data in swagger_objects %}
{% if "allplay" in swagger_data.type %}
{% if swagger_data.prop != 0 %}
    [self.{{swagger_data.name|variable_syntax(True, False)}}ResourceClient startObservingWithQueryParameters:@{}
                                                    didObserve:^(IoTWrappedRepresentation * wrapped, BOOL status) {
                                                        makeStrongAndReturnIfNil(self)
                                                        if (status) {
                                                            [self.observer allPlayController:self
                                                                            didObserveChange:IoTAllPlayObserveOrigin{{swagger_data.name|class_name_syntax}}
                                                                       wrappedRepresentation:wrapped];
                                                        }
                                                    } observeQueue: self.internalQueue];
{% endif %}
{% endif %}
{% endfor %}
}

- (void)prepareForDisposal
{
    if (!self.observing) {
        return;
    }

{% for swagger_data in swagger_objects %}
{% if "allplay" in swagger_data.type %}
{% if swagger_data.prop != 0 %}
    [self.{{swagger_data.name|variable_syntax(True, False)}}ResourceClient stopObserving];
    self.{{swagger_data.name|variable_syntax(True, False)}}ResourceClient = nil;
{% endif %}
{% endif %}
{% endfor %}
}

{% for swagger_data in swagger_objects %}
{% if "allplay" in swagger_data.type %}
{% include "device_controller_method.m.jinja2" %}
{% endif %}
{% endfor %}

@end
