//
//  MPRange.m
//  MathPad
//
//  Created by Kim Wittenburg on 18.04.14.
//  Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//

#import "MPRangePath.h"
#import "MPExpression.h"

@implementation MPRangePath

#pragma mark Creation Methods

- (instancetype)init
{
    return [self initWithLocation:[[NSIndexPath alloc] init]
                           length:0];
}

- (instancetype)initWithLocation:(NSIndexPath *)location
                          length:(NSUInteger)length
{
    self = [super init];
    if (self) {
        _location = location;
        _length = length;
    }
    return self;
}

- (instancetype)initWithRange:(NSRange)aRange
{
    return [self initWithLocation:[[NSIndexPath alloc] initWithIndex:aRange.location]
                           length:aRange.length];
}

+ (instancetype)emptyRangePath
{
    return [[self alloc] init];
}

+ (instancetype)rangePathWithLocation:(NSIndexPath *)location
                           length:(NSUInteger)length
{
    return [[self alloc] initWithLocation:location
                                      length:length];
}

+ (instancetype)rangePathWithRange:(NSRange)aRange
{
    return [[self alloc] initWithRange:aRange];
}

#pragma mark Properties

- (NSIndexPath *)maxRangePath
{
    NSUInteger lastIndex = [self.location indexAtPosition:self.location.length-1];
    NSUInteger newLastIndex = lastIndex + self.length;
    return [[self.location indexPathByRemovingLastIndex] indexPathByAddingIndex:newLastIndex];
}

- (NSRange)rangeAtLastIndex
{
    return NSMakeRange([self.location indexAtPosition:self.location.length-1], self.length);
}

#pragma mark Working with Ranges

- (BOOL)containsRangePath:(MPRangePath *)aRangePath
{
    if (aRangePath.location.length < self.location.length) {
        return NO;
    }
    // Compare indices (except the last one)
    for (NSUInteger pos = 0; pos < self.location.length-1; pos++) {
        NSUInteger selfIndex = [self.location indexAtPosition:pos];
        NSUInteger otherIndex = [aRangePath.location indexAtPosition:pos];
        if (selfIndex != otherIndex) {
            return NO;
        }
    }
    // Compare range at last index
    NSUInteger selfIndex = [self.location indexAtPosition:self.location.length-1];
    NSUInteger otherIndex = [aRangePath.location indexAtPosition:self.location.length-1];
    if (aRangePath.location.length > self.location.length) {
        return NSLocationInRange(otherIndex, self.rangeAtLastIndex);
    } else {
        return otherIndex >= selfIndex && NSMaxRange(aRangePath.rangeAtLastIndex) <= NSMaxRange(self.rangeAtLastIndex);
    }
}

- (BOOL)containsLocation:(NSIndexPath *)location
{
    return [self containsRangePath:[[MPRangePath alloc] initWithLocation:location length:0]];
}

- (BOOL)isEqual:(id)object
{
    if (self == object) {
        return YES;
    }
    if (object == nil) {
        return NO;
    }
    if (![object isKindOfClass:[MPRangePath class]]) {
        return NO;
    }
    return [self isEqualToRangePath:(MPRangePath *)object];
}

- (BOOL)isEqualToRangePath:(MPRangePath *)aRangePath
{
    return [self.location isEqual:aRangePath.location] && self.length == aRangePath.length;
}

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone
{
    MPRangePath *copy = [[MPRangePath allocWithZone:zone] initWithLocation:self.location.copy length:self.length];
    return copy;
}

#pragma mark - NSCoding

- (id)initWithCoder:(NSCoder *)aDecoder
{
    return [self initWithLocation:[aDecoder decodeObjectForKey:@"location"]
                           length:[aDecoder decodeIntegerForKey:@"length"]];
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.location forKey:@"location"];
    [aCoder encodeInteger:self.length forKey:@"length"];
}

@end

@implementation MPExpression (MPRangeExtension)

- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
{
    MPExpression *targetExpression = [self elementAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]];
    if (![targetExpression isKindOfClass:[MPExpression class]]) {
        return nil;
    }
    return [targetExpression subexpressionWithRange:aRangePath.rangeAtLastIndex];
}

@end
