//
//  MPExpressionTests.m
//  MathKit
//
//  Created by Kim Wittenburg on 19.04.14.
//  Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//

#import <XCTest/XCTest.h>

#import "MPExpression.h"
#import "MPFunction.h"

@interface MPExpressionTests : XCTestCase

@end

@implementation MPExpressionTests

- (void)testInitialization {
    // Test empty expression
    MPExpression *testExpression = [[MPExpression alloc] init];
    XCTAssertEqual(testExpression.numberOfElements, 0);

    // Test expression with string
    testExpression = [[MPExpression alloc] initWithElement:@"1234+5678"];
    XCTAssertEqual(testExpression.numberOfElements, 1);
    XCTAssertEqualObjects([testExpression elementAtIndex:0], @"1234+5678");
    
    // Test expression with function
    testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
    XCTAssertEqual([testExpression countElements], 1);
    XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]);
    
    testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
    XCTAssertEqual([testExpression countElements], 4);
    XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17");
    
    // Test expression with subsequent strings
    testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]];
    XCTAssertEqual([testExpression countElements], 1);
    XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678");
    
    // Test expression with only empty string
    testExpression = [[MPExpression alloc] initWithElement:@""];
    XCTAssertEqual([testExpression countElements], 0);
}

- (void)testSubexpressions {
    MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
    
    /********** subexpressionFromIndex: **********/
    // Test with start index at front
    MPExpression *subexpression = [testExpression subexpressionFromLocation:0];
    XCTAssertEqual([subexpression countElements], 4);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234");
    
    // Test with start index in first element
    subexpression = [testExpression subexpressionFromLocation:2];
    XCTAssertEqual([subexpression countElements], 4);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
    
    // Test with start index in middle element starting with a literal
    subexpression = [testExpression subexpressionFromLocation:6];
    XCTAssertEqual([subexpression countElements], 2);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7");
    
    // Test with start index in middle element starting with a function
    subexpression = [testExpression subexpressionFromLocation:4];
    XCTAssertEqual([subexpression countElements], 3);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
    
    // Test with start index in last element
    subexpression = [testExpression subexpressionFromLocation:7];
    XCTAssertEqual([subexpression countElements], 1);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
    
    // Test with start index at end
    subexpression = [testExpression subexpressionFromLocation:8];
    XCTAssertEqual([subexpression countElements], 0);
    
    /********** subexpressionToIndex: **********/
    // Test with end index at front
    subexpression = [testExpression subexpressionToLocation:0];
    XCTAssertEqual([subexpression countElements], 0);

    // Test with end index in first Element
    subexpression = [testExpression subexpressionToLocation:2];
    XCTAssertEqual([subexpression countElements], 1);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12");
    
    // Test with end index in middle Element ending with a literal
    subexpression = [testExpression subexpressionToLocation:6];
    XCTAssertEqual([subexpression countElements], 3);
    XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
    
    // Test with end index in middle Element ending with a function
    subexpression = [testExpression subexpressionToLocation:5];
    XCTAssertEqual([subexpression countElements], 2);
    XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
    
    // Test with end index at end
    subexpression = [testExpression subexpressionToLocation:8];
    XCTAssertEqual([subexpression countElements], 4);
    XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]);
    
    /********** subexpressionWithRange: **********/
    // Test with empty range
    subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)];
    XCTAssertEqual([subexpression countElements], 0);
    
    // Test with start and end in first element
    subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)];
    XCTAssertEqual([subexpression countElements], 1);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23");
    
    // Test with start in first and end in middle after function
    subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)];
    XCTAssertEqual([subexpression countElements], 2);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
    XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
    
    // Test with start in first and end in middle after literal
    subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)];
    XCTAssertEqual([subexpression countElements], 3);
    XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
    XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
}

- (void)testSubexpressionsIllegalRange {
    MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
    
    // Test with start index beyond end
    @try {
        [testExpression subexpressionFromLocation:10];
        XCTFail(@"Should have raised an exception.");
    }
    @catch (NSException *exception) {}

    // Test with range extending bounds
    @try {
        [testExpression subexpressionWithRange:NSMakeRange(5, 10)];
        XCTFail(@"Should have raised an exception.");
    }
    @catch (NSException *exception) {}
}

- (void)testEqualExpressions {
    MPExpression *expression1 = [[MPExpression alloc] initWithElement:@"123"];
    MPExpression *expression2 = [[MPExpression alloc] initWithElement:@"1234"];
    MPExpression *expression3 = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
    MPExpression *expression4 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
    MPExpression *expression5 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
    
    XCTAssertNotEqualObjects(expression1, expression2);
    XCTAssertNotEqualObjects(expression1, expression3);
    XCTAssertNotEqualObjects(expression1, expression4);
    XCTAssertNotEqualObjects(expression3, expression4);
    XCTAssertEqualObjects(expression4, expression5);
}

- (void)testDescription {
    // Test Simple Expressions
    MPExpression *testExpression = [[MPExpression alloc] initWithElement:@"1234"];
    XCTAssertEqualObjects([testExpression description], @"1234");
    
    testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
    XCTAssertEqualObjects([testExpression description], @"[]");
    
    // Test function after literal without explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[@"123", [[MPFunction alloc] init]]];
    XCTAssertEqualObjects([testExpression description], @"123*[]");
    
    // Test function after literal with explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[@"123+", [[MPFunction alloc] init]]];
    XCTAssertEqualObjects([testExpression description], @"123+[]");
    
    // Test literal after function without explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
    XCTAssertEqualObjects([testExpression description], @"[]*123");
    
    // Test literal after function with explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-123"]];
    XCTAssertEqualObjects([testExpression description], @"[]-123");
    
    // Test function after function without explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]];
    XCTAssertEqualObjects([testExpression description], @"[]*[]");
    
    // Test function after function with explicit operator
    testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]];
    XCTAssertEqualObjects([testExpression description], @"[]-[]");
    
    
    // Test whitespaces in literal
    testExpression = [[MPExpression alloc] initWithElements:@[@" 123 + ", [[MPFunction alloc] init]]];
    XCTAssertEqualObjects([testExpression description], @"123 +[]");
}

- (void)testCopying {
    MPExpression *baseExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
    MPExpression *copy = [baseExpression copy];
    
    XCTAssertEqual(baseExpression.numberOfElements, copy.numberOfElements);
    XCTAssertNotEqual(copy, baseExpression);
    XCTAssertNotEqual([baseExpression elementAtIndex:0], [copy elementAtIndex:0]);
    XCTAssertEqualObjects(baseExpression, copy);
}

- (void)testMutating {
    MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
    
    [testExpression appendElement:@"90"];
    XCTAssertEqual([testExpression countElements], 5);
    // 1234 [] 5678 [] 90
    
    [testExpression deleteElementsInRange:NSMakeRange(2, 4)];
    XCTAssertEqual([testExpression countElements], 3);
    // 12678 [] 90
    
    [testExpression deleteElementsInRange:NSMakeRange(0, 2)];
    XCTAssertEqual([testExpression countElements], 3);
    XCTAssertEqualObjects([testExpression elementAtIndex:0], @"678");
    
    [testExpression insertElement:[[MPFunction alloc] init]
                       atLocation:2];
    XCTAssertEqual([testExpression countElements], 5);
    // 67 [] 8 [] 90
    
    [testExpression replaceSymbolsInRange:NSMakeRange(2, 5)
                              withElements:@[[[MPFunction alloc] init]]];
    XCTAssertEqual([testExpression countElements], 2);
    XCTAssertEqualObjects([testExpression elementAtIndex:0], @"67");
    // 67 []
}

- (void)testInvalidMutatingRange {
    @try {
        MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
        [testExpression deleteElementsInRange:NSMakeRange(5, 17)];
        XCTFail(@"Should have raised an exception");
    }
    @catch (NSException *exception) {}
}

// TODO: Test evaluating expressions

@end
