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

#import "IoTBaseResourceClient.h"
#import "IoTGlobals.h"
#import "IoTWrappedRepresentation.h"
#import "IoTIotivityDispatcher.h"

#include "octypes.h" // We need only the error codes.

@interface IoTBaseResourceClient ()

@property(nonatomic,readonly) id<IoTTransportDispatcher> dispatcher;
@property(nonatomic,readonly) dispatch_queue_t internalQueue;
//@property(nonatomic,readonly) NSString *host;
@property(nonatomic,assign) BOOL observing;

@end

@implementation IoTBaseResourceClient

- (instancetype)initWithHost:(NSString *)host
                  dispatcher:(nullable id<IoTTransportDispatcher>)dispatcher
{
    self = [super init];
    if (self) {
        self->_host = host;
        self->_internalQueue = dispatch_queue_create("com.qti.qualcomm.IoTBaseResourceClient.sync", 0);
        self->_dispatcher = dispatcher ?: [[IoTIotivityDispatcher alloc] init];
        [self configureDispatcher:dispatcher];
    }
    return self;
}

- (NSString *)className
{
    return NSStringFromClass([self class]);
}

- (NSString *)errorStringFromCode:(int)code
{
    NSString *stringFromCode = [NSString stringWithFormat:@"%d", code];
    if (code == OC_STACK_RESOURCE_ERROR) {
        stringFromCode = @"OC_STACK_RESOURCE_ERROR";
    } else if (code == OC_STACK_NO_RESOURCE) {
        stringFromCode = @"OC_STACK_NO_RESOURCE";
    } else if (code == OC_STACK_SERVICE_UNAVAILABLE) {
        stringFromCode = @"OC_STACK_SERVICE_UNAVAILABLE";
    }
    return stringFromCode;
}

- (BOOL)getWithQueryParameters:(IoTQueryParams)params
                    completion:(IoTBaseResourceClientCompletion)completion
               completionQueue:(dispatch_queue_t)completionQueue
{
    if (![self isGetSupported]) {
//		NSLog(@"**** %@::get is not supported ", [self className]);
        return NO;
    }

    makeWeak(self)
    BOOL success = [self.dispatcher getWithQueryParameters:params
                                                completion:^(IoTWrappedRepresentation * _Nonnull rep, int code) {
                                                    makeStrongAndReturnIfNil(self)
                                                    BOOL success = (OC_STACK_OK == code);
                                                    // code is non-zero for error
                                                    if (success) {
                                                        NSLog(@"%@::GET CB successful",[self className]);
                                                    } else {
                                                        NSLog(@"**** %@::GET CB error %@", [self className], [self errorStringFromCode:code]);
                                                    }

                                                    if (completion && completionQueue) {
                                                        dispatch_async(completionQueue, ^{
                                                            completion(rep, success);
                                                        });
                                                    }
                                                } completionQueue:self.internalQueue];

    if (success) {
        NSLog(@"**** %@::GET request placed", [self className]);
    } else {
        NSLog(@"**** %@::GET request failed!!!", [self className]);
    }

    return success;
}

- (BOOL)postAttributes:(IoTWrappedRepresentation *)packed
       queryParameters:(IoTQueryParams)params
            completion:(IoTBaseResourceClientCompletion)completion
       completionQueue:(dispatch_queue_t)completionQueue
{
    if (![self isPostSupported]) {
//        NSLog(@"**** %@::post is not supported", [self className]);
        return NO;
    }

    makeWeak(self)
    BOOL success = [self.dispatcher postAttributes:packed
                                   queryParameters:params
                                        completion:^(IoTWrappedRepresentation * _Nonnull rep, int code) {
                                            makeStrongAndReturnIfNil(self)
                                            BOOL success = (OC_STACK_OK == code || OC_STACK_RESOURCE_CHANGED == code);
                                            if (success) {
                                                NSLog(@"**** %@::POST CB successful",[self className]);
                                            } else {
                                                NSLog(@"**** %@::POST CB error %@", [self className], [self errorStringFromCode:code]);
                                            }

                                            if (completion && completionQueue) {
                                                dispatch_async(completionQueue, ^{
                                                    completion(rep, success);
                                                });
                                            }
                                        } completionQueue:self.internalQueue];
    if (success) {
        NSLog(@"**** %@::POST request placed", [self className]);
    } else {
        NSLog(@"**** %@::POST request failed!!!", [self className]);
    }

    return success;
}

- (BOOL)startObservingWithQueryParameters:(IoTQueryParams)params
                               didObserve:(IoTBaseResourceClientCompletion)didObserve
                             observeQueue:(dispatch_queue_t)observeQueue
{
    if (![self isObserveSupported]) {
//        NSLog(@"**** %@::observe is not supported", [self className]);
        return NO;
    }

    makeWeak(self)
    BOOL success = [self.dispatcher startObservingWithQueryParameters:params
                                                           didObserve:^(IoTWrappedRepresentation * _Nonnull rep, int code, int sequenceNumber) {
                                                               makeStrongAndReturnIfNil(self)
                                                               BOOL success = (OC_STACK_OK == code && sequenceNumber <= MAX_SEQUENCE_NUMBER);
  //                                                             NSLog(@"%@::observeCB sequence number %d", [self className], sequenceNumber);
                                                               if (success) {
   /*                                                                if (sequenceNumber == OC_OBSERVE_REGISTER) {
                                                                       NSLog(@"%@::observeCB registration done", [self className]);
                                                                   }
*/
                                                                   self.observing = YES;
                                                               } else {
                                                                   /* TODO
                                                                    * Understand how the observeCB behavior is
                                                                    * 1. Observe request success
                                                                    * 2. Observe request fail
                                                                    * 3. Observe cancellation request
                                                                    * 4. Try different combinations
                                                                    */
 /*                                                                  if (OC_STACK_OK == code) {
                                                                       NSLog(@"%@::observeCB No observe option in header", [self className]);
                                                                   } else {
                                                                       NSLog(@"%@::observeCB error %@", [self className], [self errorStringFromCode:code]);
                                                                   }
  */
                                                               }
                                                               if (didObserve && observeQueue) {
                                                                   // At this point the wrapped representation contains the changes to parent/child resources.
                                                                   dispatch_async(observeQueue, ^{
                                                                       didObserve(rep, success);
                                                                   });
                                                               }
                                                           } observeQueue:self.internalQueue];

/*
 	if (success) {
        NSLog(@"**** %@::OBSERVE request placed", [self className]);
    } else {
        NSLog(@"**** %@::OBSERVE request failed!!!", [self className]);
    }
 */
    return success;
}

- (void)stopObserving
{
    dispatch_sync(self.internalQueue, ^{
        if (self.observing) {
            [self.dispatcher stopObserving];
        }
        self.observing = NO;
    });
}

- (BOOL)isGetSupported
{
    return NO;
}

- (BOOL)isPostSupported
{
    return NO;
}

- (BOOL)isObserveSupported
{
    return NO;
}

#pragma mark - Private

- (void)configureDispatcher:(id<IoTTransportDispatcher>)dispatcher
{
}

@end
