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

#import "MPExpression.h"


/*!
 @header
 This file contains the <code>MPFunction</code> class.
 
 <code>MPFunction</code> is a half abstract class that is designed to be
 subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called
 <i>functions</i>) need to regard the following guidelines:
 
 1. In their header file functions should declare <code>readwrite,
    nonatomic</code> properties for every child (subexpression) they have.
 2. For every public child accessor there should be one use of the
    <code>@link MPFunctionAccessorImplementation@/link</code> macro in the
    implementation file. See the documentation on the macro for details.
 3. Functions must override the <code>@link childrenAccessors@/link</code> method
    returning the KVC compatible names of the publicly declared property accessors
 4. Functions must also override the <code>@link functionTermClass@/link</code>
    method returning a <code>Class</code> that can evaluate the function. See the
    documentation on that method for details.
 5. Optionally functions can implement
    <code>@link expectsVariableDefinitionInChildAtIndex:@/link</code> if one of
    its children is supposed to contain a variable definition.
 6. Functions should also override the <code>NSObject</code> method
    <code>description</code>. Although this is not a requirement it
    is strongly recommended.
 
 Functions play an important role in the <i>expression tree</i>. For details on
 the that subject see the documentation for <code>@link
 MPExpression@/link</code>.
 
 @note
 None of the children of a function may be <code>nil</code> at any time.
 */


/*!
    @define     MPFunctionAccessorImplementation
    @abstract   This macro implements the setter of a previously declared
                property of a <code>MPFunction</code> subclass.
 
    @discussion Overriding the default setter implementation is necessary to
                maintain the consistency of the expression tree. Not implementing
                a custom setter using this macro or a similar custom method will
                lead to unexpected behaviour. If you need to implement additional
                custom behaviour in a property setter it is recommended that you
                copy the content of the macro.
 
    @param      Accessor
                The capitalized name of the property to be implemented. This is
                used for the name of the method: <code>set...</code>.
 
    @param      variableName
                The name of the instance variable that backs the property.
                Normally this is the same as the name of the property prefixed
                with an underscore.
 */
#define MPFunctionAccessorImplementation(Accessor, variableName) \
- (void)set##Accessor:(MPExpression *)value \
{ \
    variableName.parent = nil; \
    variableName = value; \
    variableName.parent = self; \
    [self didChangeChildAtIndex:[self indexOfChild:variableName]]; \
}



@class MPFunction, MPExpression, MPRangePath;
@protocol MPExpressionElement;


/*!
    @class      MPFunction
    @abstract   The <code>MPFunction</code> class represents a mathematical function.
 
    @discussion A mathematical function can be anything that exceeds the
                graphical capability of text. Functions like <code>sin</code>,
                <code>cos</code> or <code>ln</code> are not considered functions
                in this context because they can be represented by text.
 */
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>

#pragma mark Working With the Expression Tree
/*!
 @methodgroup Working With the Expression Tree
 */


/*!
    @property   parent
    @abstract   The receiver's parent.
 
    @discussion Expressions and functions are organized in a tree-like structure.
                Through this property a function's containing expression can be
                accessed.
       @warning Do not set this property manually. It will automatically be set
                when the function is added to or removed from an expression.
          @note If you need to know when a function is added to or removed from
                an expression you can observe this property using KVO.
 
    @return     The parent of the receiver or <code>nil</code> if the receiver
                does not have a parent.
 */
@property (nonatomic, weak) MPExpression *parent;


/*!
    @method     rootExpression
    @abstract   Returns the root expression from the receiver's expression tree.
 
    @discussion The root expression is the ultimate parent of all expressions and
                functions in the expression tree. A root expression does not have
                a parent.
 
    @return     The root expression from the receiver's expression tree or
                <code>nil</code> if this function does not have a parent.
 */
- (MPExpression *)rootExpression;


/*!
    @method     indexPath
    @abstract   Returns the index path of the receiver in the expression tree.
 
    @discussion The index path contains the indexes (starting from the root
                expression of the receiver's expression tree) that lead to the
                receiver. The indexes are expressed in the element reference
                frame.
 
    @return     The index path of the receiver in the expression tree or
                <code>nil</code> if this function does not have a parent.
 */
- (NSIndexPath *)indexPath;


/*!
    @method     numberOfChildren
    @abstract   Returns the number of children the receiver has.
 
    @return     The number of children the receiving function has.
 */
- (NSUInteger)numberOfChildren;


/*!
    @method     childAtIndex:
    @abstract   Returns the child at the specified index.
 
    @discussion The ordering of children is determined by the order of the
                child accessors as returned from the
                <code>@link childrenAccessors@/link</code> method.
 
    @param      index
                The index of the requested child.
 
    @return     The expression that corresponds to the child at
                <code>index</code>.
 */
- (MPExpression *)childAtIndex:(NSUInteger)index;


/*!
    @method     setChild:atIndex:
    @abstract   Sets the child at the specified index.
 
    @discussion Although this method does <b>not</b> call the respective accessor
                methods it behaves exactly as the setter method implemented using
                the <code>@link MPFunctionAccessorImplementation@/link</code>
                macro.
 
    @param      child
                The child that should replace the previous one.
 
    @param      index
                The index of the child to be replaced.
 */
- (void)setChild:(MPExpression *)child
         atIndex:(NSUInteger)index;


/*!
    @method     children
    @abstract   Returns the receiver's children.
 
    @discussion The ordering of the children is the same as in the array returned
                from <code>@link childrenAccessors@/link</code>.

    @return     An array containing all the function's children.
 */
- (NSArray *)children;


/*!
    @method     indexOfChild:
    @abstract   Returns the index of <code>child</code> in the receiver.
 
    @param      child
                The child to be searched.
 
    @return     The index of <code>child</code> or <code>NSNotFound</code> if
                none of the receiver's children is equal to <code>child</code>.
 */
- (NSUInteger)indexOfChild:(MPExpression *)child;


/*!
    @method     elementAtIndexPath:
    @abstract   Returns the element at the specified index path.
 
    @discussion This method finds the elements at the respective indexes starting
                at the root expression from the receiver's expression tree.
                The returned object can be a <code>NSString</code>, a
                <code>MPFunction</code> or a <code>@link
                MPExpression@/link</code> instance depending on the specified
                <code>indexPath</code>. If any of the indexes exceed the bounds
                of the respective receiver a <code>NSRangeException</code> is
                raised.
 
                If the index path does not contain any indexes the receiver
                itself is returned.
 
    @param      indexPath
                The index path the required object is located at. The indexes are
                expressed in the element reference frame.
 
    @return     The element located at <code>indexPath</code>. The element is not
                copied before it is returned. Be aware of the fact that any
                mutations made to the returned object will update the receiver's
                expression tree to reflect the change.
 */
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;


#pragma mark Notifications
/*!
 @methodgroup Notifications
 */


/*!
    @method     didChangeElementsInRangePath:replacementLength:
    @abstract   This Notification message is sent automatically from the receiver
                to itself if one of its children was mutated or changed.
 
    @discussion This method should also be called when one of the children is
                changed via one of the accessor methods.
 
    @param      rangePath
                The location of the change that happened. Because it may have
                happened somewhere deep down in the expression tree a range path
                is used.
 
    @param      replacementLength
                The number of elements that replaced the elements addressed by
                the range path.
 */
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
                   replacementLength:(NSUInteger)replacementLength;


/*!
    @method     didChangeChildAtIndex:
    @abstract   Convenience method that calls
                <code>@link
                didChangeElementsInRangePath:replacementLength:@/link</code>
 
    @discussion This method is automatically called when one of the children is
                changed using one of the accessor methods or
                <code>@link setChild:atIndex:@/link</code>.
 
    @param      index
                The index of the child that was changed.
 */
- (void)didChangeChildAtIndex:(NSUInteger)index;


#pragma mark Evaluating Functions
/*!
 @methodgroup Evaluating Functions
 */


/*!
    @method     expectsVariableDefinitionInChildAtIndex:
    @abstract   Returns whether the child at <code>anIndex</code> is supposed to
                contain a variable definition.
 
    @discussion This method is automatically called during the parsing process.
                You can override this method to opt into the default behaviour.
                If you override this method you do not need to call super.
                
                The default implementation just returns <code>NO</code> for every
                index.
 
    @param      anIndex
                The index of the child to check.
 
    @return     <code>YES</code> if the child at <code>anIndex</code> is supposed
                to contain a variable definition, <code>NO</code> otherwise.
 */
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)anIndex;

@end



/*!
    @category   MPFunction (MPSubclassOverride)
    @abstract   The methods in this category must be implemented by any concrete
                subclasses of <code>MPFunction</code>.
 
    @discussion <code>MPFunction</code> does not implement the functions itself but calls
                them at various points. If a subclass does not provide
                implementations your app will most likely crash.
 */
@interface MPFunction (MPSubclassOverride)

#pragma mark Working With the Expression Tree
/*!
 @methodgroup Working With the Expression Tree
 */


/*!
    @method     childrenAccessors
    @abstract   Returns the names of the children properties as strings.
 
    @discussion All objects in the returned array must be strings.

    @return     An array of strings describing the names of children a function
                has.
 */
- (NSArray *)childrenAccessors;


#pragma mark Evaluating Functions
/*!
 @methodgroup Evaluating Functions
 */


/*!
    @method     functionTermClass
    @abstract   Returns the class that represents the receiver's evaluation
                behaviour.
 
    @discussion Subclasses of <code>MPFunction</code> can (like
                <code>@link MPExpression@/link</code>) not evaluate themselves.
                Instead this method is called on every function. The returned
                class must be a valid subclass of <code>@link
                //apple_ref/occ/cl/MPFunctionTerm@/link</code>. It will be
                instanciated using the <code>init</code> method.
 
    @return     The <code>@link //apple_ref/occ/cl/MPFunctionTerm@/link</code>
                subclass that can evaluate instances of the receiver's class.
 */
- (Class)functionTermClass;

@end
