//
//  RiveRuntimeTests.m
//  RiveRuntimeTests
//
//  Created by Matt Sullivan on 8/30/20.
//  Copyright © 2020 Rive. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "Rive.h"
#import "CoreGraphicsRenderer.hpp"

@interface RiveRuntimeTests : XCTestCase

@end

@implementation RiveRuntimeTests

UInt8 brokenRiveFileBytes[] = {0x05, 0x25, 0x31, 0x31, 0x31, 0xFF, 0x00,
                               0x1F, 0x37, 0x0B, 0x41, 0x6E, 0x69, 0x6D,
                               0x61, 0x74, 0x00, 0x7A, 0x43, 0x00};

UInt8 pingPongRiveFileBytes[] = {
    0x52, 0x49, 0x56, 0x45, 0x07, 0x00, 0x8B, 0x94, 0x02, 0x00, 0x17, 0x00,
    0x01, 0x07, 0x00, 0x00, 0xFA, 0x43, 0x08, 0x00, 0x00, 0xFA, 0x43, 0x04,
    0x0C, 0x4E, 0x65, 0x77, 0x20, 0x41, 0x72, 0x74, 0x62, 0x6F, 0x61, 0x72,
    0x64, 0x00, 0x03, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x7A, 0x43, 0x0E, 0x00,
    0x00, 0x7A, 0x43, 0x00, 0x07, 0x05, 0x01, 0x14, 0xEA, 0xA3, 0xC7, 0x42,
    0x15, 0xEA, 0xA3, 0xC7, 0x42, 0x00, 0x14, 0x05, 0x01, 0x00, 0x12, 0x05,
    0x03, 0x00, 0x14, 0x05, 0x00, 0x00, 0x12, 0x05, 0x05, 0x25, 0x31, 0x31,
    0x31, 0xFF, 0x00, 0x1F, 0x37, 0x0B, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74,
    0x69, 0x6F, 0x6E, 0x20, 0x31, 0x39, 0x0A, 0x00, 0x19, 0x33, 0x01, 0x00,
    0x1A, 0x35, 0x0D, 0x00, 0x1E, 0x44, 0x01, 0x46, 0xE8, 0xA3, 0x47, 0x42,
    0x00, 0x1E, 0x43, 0x0A, 0x44, 0x01, 0x46, 0x83, 0x0B, 0xE1, 0x43, 0x00,
    0x1A, 0x35, 0x0E, 0x00, 0x1E, 0x44, 0x01, 0x46, 0x00, 0x00, 0x7A, 0x43,
    0x00, 0x1E, 0x43, 0x0A, 0x44, 0x01, 0x46, 0x00, 0x00, 0x7A, 0x43, 0x00};

UInt8 stateMachineFileBytes[] = {
    0x52, 0x49, 0x56, 0x45, 0x07, 0x00, 0xE1, 0x20, 0x00, 0x17, 0x00, 0x01,
    0x04, 0x0A, 0x4D, 0x79, 0x41, 0x72, 0x74, 0x62, 0x6F, 0x61, 0x72, 0x64,
    0x07, 0x00, 0x00, 0xFA, 0x43, 0x08, 0x00, 0x00, 0xFA, 0x43, 0x09, 0x00,
    0x00, 0xBE, 0xC2, 0x0A, 0x00, 0x00, 0xD2, 0xC2, 0x00, 0x03, 0x04, 0x0B,
    0x4D, 0x79, 0x52, 0x65, 0x63, 0x74, 0x61, 0x6E, 0x67, 0x6C, 0x65, 0x05,
    0x00, 0x0D, 0x78, 0x55, 0x48, 0x42, 0x0E, 0x78, 0x55, 0x48, 0x42, 0x00,
    0x07, 0x04, 0x0F, 0x4D, 0x79, 0x52, 0x65, 0x63, 0x74, 0x61, 0x6E, 0x67,
    0x6C, 0x65, 0x50, 0x61, 0x74, 0x68, 0x05, 0x01, 0x14, 0x7A, 0x55, 0xC8,
    0x42, 0x15, 0x7A, 0x55, 0xC8, 0x42, 0x00, 0x12, 0x05, 0x05, 0x00, 0x12,
    0x05, 0x06, 0x25, 0x31, 0x31, 0x31, 0xFF, 0x00, 0x14, 0x05, 0x01, 0x00,
    0x14, 0x04, 0x0A, 0x42, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E,
    0x64, 0x05, 0x00, 0x00, 0x1F, 0x37, 0x19, 0x57, 0x6F, 0x72, 0x6B, 0x41,
    0x72, 0x65, 0x61, 0x50, 0x69, 0x6E, 0x67, 0x50, 0x6F, 0x6E, 0x67, 0x41,
    0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3B, 0x02, 0x3C, 0x0F,
    0x3D, 0x2E, 0x3E, 0x01, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D,
    0x00, 0x1E, 0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43,
    0x14, 0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x28,
    0x44, 0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44,
    0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00, 0x1E,
    0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x14, 0x44,
    0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x28, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01, 0x46,
    0x78, 0x55, 0x48, 0x42, 0x00, 0x1F, 0x37, 0x18, 0x57, 0x6F, 0x72, 0x6B,
    0x41, 0x72, 0x65, 0x61, 0x4C, 0x6F, 0x6F, 0x70, 0x69, 0x6E, 0x67, 0x41,
    0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3B, 0x01, 0x3C, 0x0F,
    0x3D, 0x2D, 0x3E, 0x01, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D,
    0x00, 0x1E, 0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43,
    0x14, 0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x28,
    0x44, 0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44,
    0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00, 0x1E,
    0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x14, 0x44,
    0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x28, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01, 0x46,
    0x78, 0x55, 0x48, 0x42, 0x00, 0x1F, 0x37, 0x18, 0x57, 0x6F, 0x72, 0x6B,
    0x41, 0x72, 0x65, 0x61, 0x4F, 0x6E, 0x65, 0x53, 0x68, 0x6F, 0x74, 0x41,
    0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3C, 0x0F, 0x3D, 0x2D,
    0x3E, 0x01, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D, 0x00, 0x1E,
    0x44, 0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x14, 0x44,
    0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x28, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01, 0x46,
    0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00, 0x1E, 0x44, 0x01,
    0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x14, 0x44, 0x01, 0x46,
    0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x28, 0x44, 0x01, 0x46, 0x51,
    0xF5, 0xE0, 0x43, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01, 0x46, 0x78, 0x55,
    0x48, 0x42, 0x00, 0x1F, 0x37, 0x11, 0x50, 0x69, 0x6E, 0x67, 0x50, 0x6F,
    0x6E, 0x67, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3B,
    0x02, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D, 0x00, 0x1E, 0x44,
    0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00, 0x1E, 0x44,
    0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1F, 0x37, 0x10, 0x4F, 0x6E, 0x65,
    0x53, 0x68, 0x6F, 0x74, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F,
    0x6E, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D, 0x00, 0x1E, 0x44,
    0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00, 0x1E, 0x44,
    0x01, 0x46, 0x7A, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C, 0x44, 0x01,
    0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1F, 0x37, 0x10, 0x4C, 0x6F, 0x6F,
    0x70, 0x69, 0x6E, 0x67, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F,
    0x6E, 0x3B, 0x01, 0x00, 0x19, 0x33, 0x01, 0x00, 0x1A, 0x35, 0x0D, 0x00,
    0x1E, 0x44, 0x01, 0x46, 0x78, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C,
    0x44, 0x01, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x1A, 0x35, 0x0E, 0x00,
    0x1E, 0x44, 0x01, 0x46, 0x78, 0x55, 0x48, 0x42, 0x00, 0x1E, 0x43, 0x3C,
    0x44, 0x01, 0x45, 0x04, 0x46, 0x51, 0xF5, 0xE0, 0x43, 0x00, 0x35, 0x37,
    0x0C, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4D, 0x61, 0x63, 0x68, 0x69, 0x6E,
    0x65, 0x00, 0x38, 0x8A, 0x01, 0x05, 0x4D, 0x79, 0x4E, 0x75, 0x6D, 0x00,
    0x3B, 0x8A, 0x01, 0x06, 0x4D, 0x79, 0x42, 0x6F, 0x6F, 0x6C, 0x00, 0x3A,
    0x8A, 0x01, 0x06, 0x4D, 0x79, 0x54, 0x72, 0x69, 0x67, 0x00, 0x39, 0x8A,
    0x01, 0x07, 0x4C, 0x61, 0x79, 0x65, 0x72, 0x20, 0x31, 0x00, 0x3E, 0x00,
    0x3D, 0x95, 0x01, 0x05, 0x00, 0x41, 0x97, 0x01, 0x04, 0x00, 0x44, 0x9B,
    0x01, 0x02, 0x00, 0x40, 0x00, 0x3F, 0x00, 0x41, 0x97, 0x01, 0x01, 0x00,
    0x3D, 0x95, 0x01, 0x03, 0x00, 0x41, 0x97, 0x01, 0x02, 0x00, 0x47, 0x9B,
    0x01, 0x01, 0x00, 0x01, 0x04, 0x09, 0x41, 0x72, 0x74, 0x62, 0x6F, 0x61,
    0x72, 0x64, 0x32, 0x07, 0x00, 0x80, 0x8C, 0x43, 0x08, 0x00, 0x80, 0x8C,
    0x43, 0x09, 0x00, 0x80, 0xE9, 0xC3, 0x0A, 0x00, 0x00, 0x5B, 0x43, 0x00,
    0x12, 0x05, 0x02, 0x25, 0x31, 0x31, 0x31, 0xFF, 0x00, 0x14, 0x05, 0x00,
    0x00, 0x1F, 0x37, 0x0B, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F,
    0x6E, 0x20, 0x31, 0x00};

- (void)setUp
{
    // Put setup code here. This method is called before the invocation of each
    // test method in the class.
}

- (void)tearDown
{
    // Put teardown code here. This method is called after the invocation of
    // each test method in the class.
}

/*
 * Tests creating Rive files
 */
- (void)testRiveFileCreation
{
    // Valid Rive file, should not be null
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    XCTAssert(file != NULL);
}

/*
 * Tests retrieving the default artboard from Rive files
 */
- (void)testRetrieveDefaultArtboardFromRiveFile
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboard:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([[artboard name] isEqual:@"New Artboard"]);
}

/*
 * Tests retrieving artboard count from Rive files
 */
- (void)testRetrieveArtboardCountFromRiveFile
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    NSInteger count = [file artboardCount];
    XCTAssert(count == 1);
}

/*
 * Tests retrieving artboard by index
 */
- (void)testRetrieveArtboardByIndex
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboardFromIndex:0 error:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([[artboard name] isEqual:@"New Artboard"]);
}

/*
 * Tests retrieving artboard by name
 */
- (void)testRetrieveArtboardByName
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboardFromName:@"New Artboard" error:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([[artboard name] isEqual:@"New Artboard"]);
}

/*
 * Tests retrieving animation count from artboards
 */
- (void)testRetrieveAnimationCountFromArtboard
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboard:nil];
    NSInteger count = [artboard animationCount];
    XCTAssert(count == 1);
}

/*
 * Tests retrieving the first animation
 */
- (void)testRetrieveFirstAnimation
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    XCTAssert(file != NULL);
    RiveArtboard* artboard = [file artboard:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([artboard animationCount] == 1);

    RiveLinearAnimationInstance* animation = [artboard animationFromIndex:0
                                                                    error:nil];
    XCTAssert(animation != NULL);
    XCTAssert([animation.name isEqual:@"Animation 1"]);
}

/*
 * Tests retrieving animation by index
 */
- (void)testRetrieveAnimationByIndex
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboard:nil];

    RiveLinearAnimationInstance* animation = [artboard animationFromIndex:0
                                                                    error:nil];
    XCTAssert(animation != NULL);
    XCTAssert([[animation name] isEqual:@"Animation 1"]);
}

/*
 * Tests retrieving animations by name
 */
- (void)testRetrieveAnimationByName
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:pingPongRiveFileBytes
                                          byteLength:156
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboard:nil];
    NSError* error = nil;

    RiveLinearAnimationInstance* animation =
        [artboard animationFromName:@"Animation 1" error:&error];
    XCTAssert(animation != NULL);
    XCTAssert([animation.name isEqual:@"Animation 1"]);
}

/*
 * Tests retrieving the first state machine
 */
- (void)testRetrieveFirstStateMachine
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:stateMachineFileBytes
                                          byteLength:916
                                             loadCdn:false
                                               error:nil];
    XCTAssert(file != NULL);
    RiveArtboard* artboard = [file artboard:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([artboard stateMachineCount] == 1);

    RiveStateMachineInstance* machine = [artboard stateMachineFromIndex:0
                                                                  error:nil];
    XCTAssert(machine != NULL);
    XCTAssert([machine.name isEqual:@"StateMachine"]);
}

/*
 * Tests retrieving state machines by index
 */
- (void)testRetrieveStateMachineByIndex
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:stateMachineFileBytes
                                          byteLength:916
                                             loadCdn:false
                                               error:nil];
    XCTAssert(file != NULL);
    RiveArtboard* artboard = [file artboard:nil];
    XCTAssert(artboard != NULL);
    XCTAssert([artboard stateMachineCount] == 1);

    RiveStateMachineInstance* machine = [artboard stateMachineFromIndex:0
                                                                  error:nil];
    XCTAssert(machine != NULL);
    XCTAssert([machine.name isEqual:@"StateMachine"]);

    NSError* error = nil;
    machine = [artboard stateMachineFromIndex:1 error:&error];
    XCTAssertNil(machine);
    XCTAssertEqualObjects([error domain], @"rive.app.ios.runtime");
    XCTAssertEqual([error code], 301);
    XCTAssertEqualObjects([[error userInfo] valueForKey:@"name"],
                          @"NoStateMachineFound");

    error = nil;
    machine = [artboard stateMachineFromIndex:-1 error:&error];
    XCTAssertNil(machine);
    XCTAssertEqualObjects([error domain], @"rive.app.ios.runtime");
    XCTAssertEqual([error code], 301);
    XCTAssertEqualObjects([[error userInfo] valueForKey:@"name"],
                          @"NoStateMachineFound");
}

/*
 * Tests retrieving state machines by name
 */
- (void)testRetrieveStateMachineByName
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:stateMachineFileBytes
                                          byteLength:916
                                             loadCdn:false
                                               error:nil];
    XCTAssert(file != NULL);
    RiveArtboard* artboard = [file artboard:nil];
    XCTAssert(artboard != NULL);
    RiveStateMachineInstance* machine =
        [artboard stateMachineFromName:@"StateMachine" error:nil];
    XCTAssert(machine != NULL);

    NSError* error = nil;
    machine = [artboard stateMachineFromName:@"Bad Name" error:&error];
    XCTAssertNil(machine);
    XCTAssertEqualObjects([error domain], @"rive.app.ios.runtime");
    XCTAssertEqual([error code], 301);
    XCTAssertEqualObjects([[error userInfo] valueForKey:@"name"],
                          @"NoStateMachineFound");
}

/*
 * Tests creating state machine instances
 */
- (void)testCreateStateMachineInstance
{
    RiveFile* file = [[RiveFile alloc] initWithBytes:stateMachineFileBytes
                                          byteLength:916
                                             loadCdn:false
                                               error:nil];
    RiveArtboard* artboard = [file artboard:nil];

    RiveStateMachineInstance* machine = [artboard stateMachineFromIndex:0
                                                                  error:nil];
    XCTAssert(machine != NULL);
    RiveStateMachineInstance* instance =
        [[RiveStateMachineInstance alloc] init];
    XCTAssert(instance != NULL);
}

//- (void)testPerformanceExample {
//    // This is an example of a performance test case.
//    [self measureBlock:^{
//        // Put the code you want to measure the time of here.
//    }];
//}

@end
