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

@import Foundation;

#import "MPExpression.h"

#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]

@class MPRangePath, MPExpression;


/*!
    @class      MPRangePath
    @brief      @c MPRangePath is like @c NSRange but with an @c NSIndexPath as
                location.
 
    @discussion Range paths are intended to address a range (as defined by @c
                NSRange) that resides in a tree structure. Thus the @c location
                property of a range path is a @c NSIndexPath.
 
 
                @c MPRangePath instances are immutable.
 */
@interface MPRangePath : NSObject <NSCopying, NSCoding>

#pragma mark Creation Methods


/*!
    @method     init
    @brief      Initializes an empty range path.
 
    @discussion This method is a convenience initializer to create a range path with
                an empty location and a length of @c 0.
 
    @return     A @c MPRangePath instance.
 */
- (instancetype)init;


/*!
    @method     initWithRange:
    @brief      Initializes a newly created range path with the given range.
 
    @discussion This method is a convenience initializer to convert from a @c
                NSRange to a @c MPRangePath. The location of @c aRange is
                translated into a location for the range path.
 
    @param      aRange
                The range to be converted into a @c MPRangePath.
 
    @return     A @c MPRangePath instance.
 */
- (instancetype)initWithRange:(NSRange)aRange;


/*!
    @method     initWithLocation:lenght:
    @brief      Initializes a newly created range path with the given location
                and length.
 
    @discussion The last index in the @c location should address the first item
                that is actually selected.
 
    @param      location
                The location of the first element that should be addressed by
                this range path.
 
    @param      length
                The number of elements to be addressed by this range path.
 
    @return     A @c MPRangePath instance.
 */
- (instancetype)initWithLocation:(NSIndexPath *)location
                          length:(NSUInteger)length; /* designated initializer */


/*!
    @method     emptyRangePath
    @brief      Allocates and initializes an empty @c NSRangePath instance.
 
    @discussion An empty range path's location has @c 0 indexes and a length of
                @c 0.

    @return     A newly created @c MPRangePath instance.
 */
+ (instancetype)emptyRangePath;


/*!
    @method     rangePathWithLocation:length:
    @brief      Allocates a new @c MPRangePath instance and initializes it with
                the given location and length.

    @param      location
                The location of the first element that should be addressed by the
                range path.
 
    @param      length
                The number of elements that should be addressed by the range
                path.
 
    @return     A newly created @c MPRangePath instance.
 */
+ (instancetype)rangePathWithLocation:(NSIndexPath *)location
                           length:(NSUInteger)length;


/*!
    @method     rangePathWithRange:
    @brief      Allocates a new @c MPRangePath instance and initializes it with
                the given location and length.
 
    @discussion The location of @c aRange is translated into an index path for
                the range path.
 
    @param      aRange
                The range to be converted into a range path.
 
    @return     A newly created @c MPRangePath instance.
 */
+ (instancetype)rangePathWithRange:(NSRange)aRange;

#pragma mark Properties


/*!
    @property   location
    @brief      The location of the first element addressed by the receiver.
 */
@property (readonly, nonatomic, strong) NSIndexPath *location;


/*!
    @property   length
    @brief      The number of elements addressed by the receiver. The element
                addressed by the last index of the location property plus the
                length of the receiver is NOT considered inside of the range
                path.
 */
@property (readonly, nonatomic) NSUInteger length;


/*!
    @method     maxRangePath
    @brief      Similar to the @c NSRange method @c NSMaxRange this method
                returns the first index after the receiving @c NSRangePath.
 
    @return     The first index after the receiving @c NSRangePath.
 */
- (NSIndexPath *)maxRangePath;


/*!
    @method     rangeAtLastIndex
    @brief      Returns a @c NSRange constructed from the last index of the
                receiver's location and the receiver's length.
 
    @return     The range identifying the elements at the last index in the
                receiver's index path.
 */
- (NSRange)rangeAtLastIndex;

#pragma mark Working with Ranges


/*!
    @method     containsRangePath:
    @brief      Checks wether the receiver completely contains @c aRangePath.
 
    @discussion A range path is contained by another range path if either the
                receiver's range at its last index contains the index at the
                respective location of the location path of @c aRangePath or (if
                the locations are of the same length) the receiver's range at its
                last index contains the last index's range of @c aRangePath.
 
    @param      aRangePath
                The range path to check wether it is contained in the receiver.
 
    @return     @c YES if the range path addressed by the receiver also includes
                @c aRangePath, @c NO otherwise.
 */
- (BOOL)containsRangePath:(MPRangePath *)aRangePath;


/*!
    @method     containsLocation:
    @brief      Checks wether the range path addressed by the receiver includes
                @c location.
 
    @discussion The index path is considered inside the receiver if the indexes
                in the receiver's @c location property are equal to the
                respective indexes index in @c location. For the last position of
                the last index of the receiver's @c location it is also valid if
                the respective index is inside the range at the last index.
 
    @param      location
                The index path to check wether it is contained in the receiver.
 
    @return     @c YES if the range path addressed by the receiver also contains
                @c location, @c NO otherwise.
 */
- (BOOL)containsLocation:(NSIndexPath *)location;


- (BOOL)isEqual:(id)object;


/*!
    @method     isEqualToRangePath:
    @brief      Checks wether the receiver and @c aRangePath are equal.
 
    @discussion If you know that you are comparing two range paths (that are not
                @c nil) you should use this method over @c -isEqual: because it
                is more performant.
 
    @param      aRangePath
                The range path to check for equality. If this parameter is @c nil
                this method may raise an exception.
 
    @return     @c YES if the receiver is equal to @c aRangePath, @c NO
                otherwise.
 */
- (BOOL)isEqualToRangePath:(MPRangePath *)aRangePath;

@end

@interface MPExpression (MPRangeExtension)


/*!
    @method     subexpressionWithRangePath:
    @brief      Creates a new expression with the symbols in the specified range
                path.
 
    @discussion The elements in the newly created expression are copied to the
                new exoression. The range path is specified in the length
                reference frame.
 
                If the given range exceeds the receiver's bounds a @c
                NSRangeException is raised.
 
    @param      aRangePath
                The range path from which to create the new expression.
 
    @return     A new expression.
 */
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath; // If location points to expression (in function) nil is returned

@end
