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

#import <XCTest/XCTest.h>
#import "IoTMultichannelState.h"
#import "IoTResourceAttributes.h"
#import "TestCategories.h"

@interface IoTMultichannelStateTests : XCTestCase

@property (nonatomic, strong) dispatch_queue_t syncQueue;
@property (nonatomic, strong) XCTestExpectation *multichannelVolumeExpectation;
@property (nonatomic, strong) XCTestExpectation *multichannelAvailabilityExpectation;

@end

@implementation IoTMultichannelStateTests

- (IoTGroupPlayersAttr *)playerWithName:(NSString *)name
                               deviceId:(NSString *)deviceId
                                channel:(AttrChannelMapNumber)channel
                              connected:(BOOL)connected
                            volumeRatio:(double)volumeRatio
{
    IoTGroupPlayersAttr *player = [[IoTGroupPlayersAttr alloc]initWithStateSyncQueue:self.syncQueue];
    [player setDisplayName:name];
    [player setDeviceId:deviceId];
    [player setChannelMap:@[@(channel)]];
    [player setConnected:connected];
    [player setVolumeRatio:volumeRatio];
    return player;
}

- (void)setUp
{
    [super setUp];
    self.syncQueue = dispatch_queue_create("com.qualcomm.qti.IoTMultichannelStateTests", DISPATCH_QUEUE_CONCURRENT);
    self.multichannelVolumeExpectation = [[XCTestExpectation alloc] initWithDescription:@"Wait for Multichannel Volume Change"];
    self.multichannelAvailabilityExpectation = [[XCTestExpectation alloc] initWithDescription:@"Wait for Multichannel Availability Change"];
}

- (void)testCorrectlyReturnsAsSoundbar
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupMainAttr* attr = [[IoTMultichannelGroupMainAttr alloc] initWithStateSyncQueue:self.syncQueue];
    [mcState updateWithMainAttr:attr
            availabilityChanged:nil
                  volumeChanged:nil
                    changeQueue:nil];
    XCTAssertTrue(mcState.isSoundbar);
}

- (void)testCorrectlyReturnsAsNotSoundbar
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupMainAttr* attr = nil;
    [mcState updateWithMainAttr:attr
            availabilityChanged:nil
                  volumeChanged:nil
                    changeQueue:nil];
    XCTAssertFalse(mcState.isSoundbar);
}

- (void)testCorrectlyReturnsAsSatellite
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupSatelliteAttr* attr = [[IoTMultichannelGroupSatelliteAttr alloc] initWithStateSyncQueue:self.syncQueue];
    [mcState updateWithSatelliteAttr:attr];
    XCTAssertTrue(mcState.isSatellite);
}

- (void)testCorrectlyReturnsAsNotSatellite
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupSatelliteAttr* attr = nil;
    [mcState updateWithSatelliteAttr:attr];
    XCTAssertFalse(mcState.isSatellite);
}

- (void)testCorrectlyUpdatesWhenAddingChannel
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupMainAttr* attr = [[IoTMultichannelGroupMainAttr alloc] initWithStateSyncQueue:self.syncQueue];
    IoTGroupPlayersAttr *player = [self playerWithName:@"Player1"
                                              deviceId:@"id1"
                                               channel:AttrChannelMapNumberFrontLeft
                                             connected:YES
                                           volumeRatio:0.5];
    [attr setGroup:@[player]];
    [mcState updateWithMainAttr:attr
            availabilityChanged:^(IoTHomeTheaterChannel channel) {
        XCTAssertEqual(channel, IoTHomeTheaterChannelLeft);
        NSArray<IoTMultichannelChannelDevice *> *deviceInfo = [mcState devicesForChannel:IoTHomeTheaterChannelLeft];
        IoTMultichannelChannelDevice *firstDevice = [deviceInfo firstObject];
        XCTAssertNotNil(firstDevice);
        XCTAssertEqualObjects(firstDevice.displayName, @"Player1");
        XCTAssertEqualObjects(firstDevice.playerID, @"id1");
        XCTAssertTrue(firstDevice.connected);
        XCTAssertEqualWithAccuracy(firstDevice.volume, 0.0, 0.01);
        [self.multichannelAvailabilityExpectation fulfill];
    } volumeChanged:^(IoTHomeTheaterChannel channel) {
        [self.multichannelVolumeExpectation fulfill];
    } changeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    [self waitForExpectations:@[self.multichannelVolumeExpectation, self.multichannelAvailabilityExpectation] timeout:5];
}

- (void)testCorrectlyUpdatesWhenRemovingChannel
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupMainAttr* attr = [[IoTMultichannelGroupMainAttr alloc] initWithStateSyncQueue:self.syncQueue];
    IoTGroupPlayersAttr *player = [self playerWithName:@"Player1"
                                              deviceId:@"id1"
                                               channel:AttrChannelMapNumberFrontLeft
                                             connected:YES
                                           volumeRatio:0.5];
    [attr setGroup:@[player]];
    [mcState updateWithMainAttr:attr
            availabilityChanged:nil
                  volumeChanged:nil
                    changeQueue:nil];

    attr = [[IoTMultichannelGroupMainAttr alloc] initWithStateSyncQueue:self.syncQueue];
    [mcState updateWithMainAttr:attr
            availabilityChanged:^(IoTHomeTheaterChannel channel) {
        XCTAssertEqual(channel, IoTHomeTheaterChannelLeft);
        NSArray<IoTMultichannelChannelDevice *> *deviceInfo = [mcState devicesForChannel:IoTHomeTheaterChannelLeft];
        IoTMultichannelChannelDevice *firstDevice = [deviceInfo firstObject];
        XCTAssertNil(firstDevice);
        [self.multichannelAvailabilityExpectation fulfill];
    } volumeChanged:nil
                    changeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    [self waitForExpectations:@[self.multichannelAvailabilityExpectation] timeout:5];
}

- (void)testCorrectlyUpdatesWhenAddingTwoChannels
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    IoTMultichannelGroupMainAttr* attr = [[IoTMultichannelGroupMainAttr alloc] initWithStateSyncQueue:self.syncQueue];
    IoTGroupPlayersAttr *player1 = [self playerWithName:@"Player1"
                                               deviceId:@"id1"
                                                channel:AttrChannelMapNumberFrontLeft
                                              connected:YES
                                            volumeRatio:0.5];

    IoTGroupPlayersAttr *player2 = [self playerWithName:@"Player2"
                                               deviceId:@"id2"
                                                channel:AttrChannelMapNumberFrontRight
                                              connected:YES
                                            volumeRatio:0.5];
    [attr setGroup:@[player1, player2]];
    __block int blockCalled = 0;
    
    [mcState updateWithMainAttr:attr
            availabilityChanged:^(IoTHomeTheaterChannel channel) {
        NSArray<IoTMultichannelChannelDevice *> *deviceInfo = [mcState devicesForChannel:IoTHomeTheaterChannelLeft];
        IoTMultichannelChannelDevice *firstDevice = [deviceInfo firstObject];
        XCTAssertNotNil(firstDevice);
        XCTAssertTrue(firstDevice.connected);
        XCTAssertEqualWithAccuracy(firstDevice.volume, 0.0, 0.01);
        blockCalled ++;
        if (blockCalled == 2) {
            [self.multichannelAvailabilityExpectation fulfill];
        }
    } volumeChanged:^(IoTHomeTheaterChannel channel) {
                      [self.multichannelVolumeExpectation fulfill];
                  } changeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    [self waitForExpectations:@[self.multichannelVolumeExpectation, self.multichannelAvailabilityExpectation] timeout:5];
}

- (void)testChannelsMappedCorrectlyAttrToHTC
{
	IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberFrontLeft)], IoTHomeTheaterChannelLeft);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberFrontRight)], IoTHomeTheaterChannelRight);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberFrontCenter)], IoTHomeTheaterChannelCenter);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberSubwoofer)], IoTHomeTheaterChannelSubwoofer);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberSideLeft)], IoTHomeTheaterChannelLeftSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberSideRight)], IoTHomeTheaterChannelRightSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberBackLeft)], IoTHomeTheaterChannelLeftRearSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberBackRight)], IoTHomeTheaterChannelRightRearSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberTopFrontLeft)], IoTHomeTheaterChannelLeftUpfiringSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberTopFrontRight)], IoTHomeTheaterChannelRightUpfiringSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberTopBackLeft)], IoTHomeTheaterChannelLeftRearUpfiringSurround);
    XCTAssertEqual([mcState homeTheaterChannelForAttrChannelMap:(AttrChannelMapNumberTopBackRight)], IoTHomeTheaterChannelRightRearUpfiringSurround);
}

- (void)testChannelsMappedCorrectlyHTCtoAttr
{
    IoTMultichannelState *mcState = [[IoTMultichannelState alloc] initWithStateSyncQueue:self.syncQueue];
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelLeft], AttrChannelMapNumberFrontLeft);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelRight], AttrChannelMapNumberFrontRight);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelCenter], AttrChannelMapNumberFrontCenter);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelSubwoofer], AttrChannelMapNumberSubwoofer);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelLeftSurround], AttrChannelMapNumberSideLeft);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelRightSurround], AttrChannelMapNumberSideRight);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelLeftRearSurround],AttrChannelMapNumberBackLeft);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelRightRearSurround], AttrChannelMapNumberBackRight);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelLeftUpfiringSurround], AttrChannelMapNumberTopFrontLeft);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelRightUpfiringSurround], AttrChannelMapNumberTopFrontRight);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelLeftRearUpfiringSurround], AttrChannelMapNumberTopBackLeft);
    XCTAssertEqual([mcState attrChannelMapForHomeTheaterChannel:IoTHomeTheaterChannelRightRearUpfiringSurround], AttrChannelMapNumberTopBackRight);
}

@end
