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

#import "IoTPlayer+CategoriesHeader.h"
#import "IoTPlayerGroupsInfo.h"

@implementation IoTPlayer(Private)

- (instancetype)initWithAllPlayController:(IoTAllPlayController *)allPlayController
                          applicationName:(NSString *)appName
                           stateSyncQueue:(dispatch_queue_t)syncQueue
{
    self = [super init];
    if (self) {
        [self replaceAllPlayController:allPlayController];
        self.stateSyncQueue = syncQueue;
        self.applicationName = [appName copy];
        self.backgroundQueue = dispatch_queue_create("com.qualcomm.qti.IoTPlayer.backgroundQueue", DISPATCH_QUEUE_SERIAL);
        self.state = [[IoTPlayerState alloc] initWithStateSyncQueue:syncQueue];
        self.state.multiChannelState.playerID = allPlayController.serviceInfo.identifier;
    }
    return self;
}

- (void)replaceAllPlayController:(IoTAllPlayController *)allPlayController
{
    self.dispatchedAfterInitialFetch = NO;
    self.allPlayController = allPlayController;
    allPlayController.observer = self;
}

- (void)dealloc
{
    [self.refreshTimer invalidate];
}

- (NSString *)host
{
    return self.allPlayController.serviceInfo.host;
}

- (void)updateWithCompletion:(void(^)(BOOL))completion
             completionQueue:(dispatch_queue_t)queue
{
    dispatch_group_t requestGroup = dispatch_group_create();

    BOOL success = NO;
    makeWeak(self)

    dispatch_group_enter(requestGroup); // Top prevent races this must be done first even if next call maybe false.
    success = [self.allPlayController getMediaPlayerWithCompletion:^(IoTMediaPlayerAttr * attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getMediaPlayerWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.mediaPlayerAttr = success ? attr : nil;
        [self handlePlayStateUpdate:YES];
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.mediaPlayerAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getGroupWithCompletion:^(IoTGroupAttr * attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getGroupWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.groupAttr = success ? attr : nil;
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.groupAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getVolumeControlWithCompletion:^(IoTVolumeControlAttr * attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getVolumeControlWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.volumeControlAttr = success ? attr : nil;
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.volumeControlAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getGroupVolumeControlWithCompletion:^(IoTVolumeControlAttr * attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getGroupVolumeControlWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        NSLog(@"*** GROUP VOLUME INITIAL FETCH: SUCCESS = %@", success ? @"YES" : @"NO");
        self.state.groupVolumeControlAttr = success ? attr : nil;
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.groupVolumeControlAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getInputSelectorWithCompletion:^(IoTInputSelectorAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getInputSelectorWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.inputSelectorAttr = success ? attr : nil;
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.inputSelectorAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getOutputSelectorWithCompletion:^(IoTOutputSelectorAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getOutputSelectorWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.outputSelectorAttr = success ? attr : nil;
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.outputSelectorAttr = nil;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getPlaylistWithCompletion:^(IoTPlaylistAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getPlaylistWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        if (!success) {
            self.state.playlistState.playlistSupported = NO;
        } else {
            self.state.playlistState.playlistSupported = YES;
            self.state.playlistState.ownerInfo = attr.ownerInfo;
            self.state.playlistState.userData = attr.userData;
            self.state.playlistState.snapshotID = attr.snapshotId;
        }
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.playlistState.playlistSupported = NO;
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self getPlaylistSizeAndSnapshotIDWithCompletion:^(BOOL success) {
        makeStrongAndReturnIfNil(self)
        NSLog(@"***** getPlaylistSizeAndSnapshotIDWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getMultichannelGroupMainWithCompletion:^(IoTMultichannelGroupMainAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getMultichannelGroupMainWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        [self.state.multiChannelState updateWithMainAttr:success ? attr : nil
                                     availabilityChanged:nil
                                           volumeChanged:nil
                                             changeQueue:nil];
        if (success) {
            [self.allPlayController postRegisterForSurroundSignalsWithCompletion:nil completionQueue:nil];
        }
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        [self.state.multiChannelState updateWithMainAttr:nil
                                     availabilityChanged:nil
                                           volumeChanged:nil
                                             changeQueue:nil];
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getMultichannelVolumeWithCompletion:^(IoTMultichannelVolumeControlAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getMultichannelVolumeWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        [self.state.multiChannelState updateWithMCVolumeAttr:success ? attr : nil
                                           volumeChanged:nil
                                             changeQueue:nil];
        if (success) {
            [self.allPlayController postRegisterForSurroundSignalsWithCompletion:nil completionQueue:nil];
        }
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        [self.state.multiChannelState updateWithMCVolumeAttr:nil
                                           volumeChanged:nil
                                             changeQueue:nil];
        dispatch_group_leave(requestGroup);
    }

    dispatch_group_enter(requestGroup);
    success = [self.allPlayController getMultichannelGroupSatelliteWithCompletion:^(IoTMultichannelGroupSatelliteAttr *attr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getMultichannelGroupSatelliteWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        [self.state.multiChannelState updateWithSatelliteAttr:success ? attr : nil];
        dispatch_group_leave(requestGroup);
    } completionQueue:self.backgroundQueue];
    if (!success) {
        [self.state.multiChannelState updateWithSatelliteAttr: nil];
        dispatch_group_leave(requestGroup);
    }

// ***** Audio effects are non-gating on discovery

    success = [self.allPlayController getBassboostWithCompletion:^(BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getBassboostWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.bassboostAttr = success ? [[IoTBassboostAttr alloc] initWithStateSyncQueue:self.stateSyncQueue] : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.bassboostAttr = nil;
    }

    success = [self.allPlayController getPresetReverbWithCompletion:^(BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getPresetReverbWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.reverbAttr = success ? [[IoTPresetReverbAttr alloc] initWithStateSyncQueue:self.stateSyncQueue] : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.reverbAttr = nil;
    }

    success = [self.allPlayController getEqualizerWithCompletion:^(BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getEqualizerWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.equalizerAttr = success ? [[IoTEqualizerAttr alloc] initWithStateSyncQueue:self.stateSyncQueue] : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
		self.state.audioAdjustmentState.equalizerAttr = nil;
    }

    success = [self.allPlayController getDolbyWithCompletion:^(IoTDolbyAttr *outAttr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getDolbyWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.dolbyAttr = success ? outAttr : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.dolbyAttr = nil;
    }

    success = [self.allPlayController getVirtualXSoundXWithCompletion:^(IoTVirtualXSoundXAttr *outAttr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getDTSWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.virtualXAttr = success ? outAttr : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.dolbyAttr = nil;
    }

    success = [self.allPlayController getEffectsTrumpetWithCompletion:^(IoTEffectsTrumpetAttr *outAttr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getEffectsTrumpetWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.trumpetAttr = success ? outAttr : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.trumpetAttr = nil;
    }

    success = [self.allPlayController getMeloDWithCompletion:^(IoTMeloDAttr *outAttr, BOOL success) {
        makeStrongAndReturnIfNil(self);
        NSLog(@"***** getMeloDWithCompletion ... %@", success ? @"SUCCESS" : @"FAILED");
        self.state.audioAdjustmentState.meloDAttr = success ? outAttr : nil;
    } completionQueue:self.backgroundQueue];
    if (!success) {
        self.state.audioAdjustmentState.meloDAttr = nil;
    }

    dispatch_group_notify(requestGroup, self.backgroundQueue, ^{
        makeStrongAndReturnIfNil(self)
        if (!self.dispatchedAfterInitialFetch && completion && queue) {
            self.dispatchedAfterInitialFetch = YES;
            dispatch_async(queue, ^{
                completion(YES);
            });
        }
    });

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.backgroundQueue, ^{
        makeStrongAndReturnIfNil(self)
        if (!self.dispatchedAfterInitialFetch && completion && queue) {
            self.dispatchedAfterInitialFetch = YES;
            dispatch_async(queue, ^{
                completion(NO);
            });
        }
    });
}

- (BOOL)getPlaylistSizeAndSnapshotIDWithCompletion:(void(^)(BOOL))completion
                                   completionQueue:(dispatch_queue_t)queue
{
    IoTPlaylistGetRangeInAttr *inAttr = [[IoTPlaylistGetRangeInAttr alloc] initWithStateSyncQueue:self.stateSyncQueue];
    inAttr.snapshotId = @""; // Pass empty to get a valid snapshotID back.
    inAttr.start = 0;
    inAttr.count = 1;
    makeWeak(self)
    return [self.allPlayController postPlaylistGetRangeWith:inAttr
                                                completion:^(IoTPlaylistGetRangeOutAttr *attr, BOOL success) {
                                                    makeStrongAndReturnIfNil(self);
                                                    if (success) {
                                                        self.state.playlistState.size = attr.totalSize;
                                                        self.state.playlistState.snapshotID = attr.latestSnapshotId;
                                                        if (attr.totalSize == 0) {
                                                            self.state.playlistState.ownerInfo = @"";
                                                            self.state.playlistState.userData = @"";
                                                        }
                                                    }
                                                    dispatch_async(queue, ^{
                                                        completion(success);
                                                    });
                                                } completionQueue:self.backgroundQueue];
}

- (NSString *)groupID
{
    RETURN_GETTER(NSString *, groupAttr, currentGroupId, @"")
}

- (NSString *)leadPlayerInGroupID
{
    RETURN_GETTER(NSString *, mediaPlayerAttr.playState, groupLeadId, @"");
}

- (IoTPlayerGroupsInfo *)groupsInfo
{
    if (![self groupingSupported]) {
        return nil;
    }
    return [[IoTPlayerGroupsInfo alloc] initWithGroupAttribute:self.state.groupAttr];
}

- (void)setUpdatesDelegate:(id<IoTPlayerUpdates>)updatesDelegate
{
    _updatesDelegate = updatesDelegate;
}

- (id<IoTPlayerUpdates>)updatesDelegate
{
    return _updatesDelegate;
}

- (NSString *)appID
{
    return @"";
}

- (BOOL)isAudioSupported
{
    BOOL ret = NO;
    for (NSString *cap in self.state.mediaPlayerAttr.capabilities) {
        if ([cap isEqualToString:@"audio"]) {
            ret = YES;
            break;
        }
    }
    return ret;
}

- (BOOL)isVideoSupported
{
    BOOL ret = NO;
    for (NSString *cap in self.state.mediaPlayerAttr.capabilities) {
        if ([cap isEqualToString:@"video"]) {
            ret = YES;
            break;
        }
    }
    return ret;
}

- (BOOL)isPhotoSupported
{
    return [self isVideoSupported];
}

- (int)apiVersion
{
    RETURN_GETTER(int, mediaPlayerAttr, version, 0)
}

@end
