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

#import "MPFunctionLayout.h"

#import "MPExpressionLayout.h"

#import "MPFunction.h"
#import "MPFractionFunction.h"
#import "MPRootFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
#import "MPSumFunction.h"
#import "MPProductFunction.h"

#import "MPFractionFunctionLayout.h"
#import "MPRootFunctionLayout.h"
#import "MPParenthesisFunctionLayout.h"
#import "MPPowerFunctionLayout.h"
#import "MPSumFunctionLayout.h"
#import "MPProductFunctionLayout.h"

#import "NSIndexPath+MPAdditions.h"



@implementation MPFunctionLayout

#pragma mark Creation Methods


+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
                                         parent:(MPExpressionLayout *)parent
{
    Class class = [function class];
    if (class == [MPSumFunction class]) {
        return [[MPSumFunctionLayout alloc] initWithFunction:function parent:parent];
    } else if (class == [MPParenthesisFunction class]) {
        return [[MPParenthesisFunctionLayout alloc] initWithFunction:function parent:parent];
    } else if (class == [MPPowerFunction class]) {
        return [[MPPowerFunctionLayout alloc] initWithFunction:function parent:parent];
    } else if (class == [MPFractionFunction class]) {
        return [[MPFractionFunctionLayout alloc] initWithFunction:function parent:parent];
    } else if (class == [MPProductFunction class]) {
        return [[MPProductFunctionLayout alloc] initWithFunction:function parent:parent];
    } else if (class == [MPRootFunction class]) {
        return [[MPRootFunctionLayout alloc] initWithFunction:function parent:parent];
    }
    return [[self alloc] initWithFunction:function
                                   parent:parent];
}


- (instancetype)initWithFunction:(MPFunction *)function
                          parent:(MPExpressionLayout *)parent
{
    self = [super initWithParent:parent];
    if (self) {
        _function = function;
    }
    return self;
}


#pragma mark Cache Methods

- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
                            generator:(CTLineRef (^)())generator
{
    NSUInteger actualIndex = self.function.numberOfChildren + index;
    id (^actualGenerator)() = ^{
        CTLineRef line = generator();
        return CFBridgingRelease(line);
    };
    id lineObject = [self cachableObjectForIndex:actualIndex
                                       generator:actualGenerator];
    return (__bridge CTLineRef)(lineObject);
}


- (id)objectForPrivateCacheIndex:(NSUInteger)index
                       generator:(id (^)())generator
{
    NSUInteger actualIndex = self.function.numberOfChildren + index;
    return [self cachableObjectForIndex:actualIndex generator:generator];
}


- (NSUInteger)numberOfChildren
{
    return self.function.numberOfChildren;
}


- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
    return [self cachableObjectForIndex:index generator:^id{
        MPExpression *child = [self.function childAtIndex:index];
        MPExpressionLayout *layout = [[MPExpressionLayout alloc] initWithExpression:child
                                                                             parent:self];
        layout.flipped = self.flipped;
        layout.usesSmallSize = self.usesSmallSize ? YES : [self childAtIndexUsesSmallSize:index];
        return layout;
    }];
}


- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
    return NO;
}


- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{
    // A single index is used to communicate back wether the
    // selection should be before or after the function.
    // A 0 means before, a 1 means after.
    for (NSUInteger index = 0; index < self.function.numberOfChildren; index++) {
        MPLayout *childLayout = [self childLayoutAtIndex:index];
        NSRect childBounds = childLayout.bounds;
        NSPoint childOffset = [self offsetOfChildLayoutAtIndex:index];
        childBounds.origin.x += childOffset.x;
        childBounds.origin.y += childOffset.y;
        if (NSMouseInRect(point, childBounds, self.flipped)) {
            NSPoint pointInChild = NSMakePoint(point.x - childOffset.x, point.y - childOffset.y);
            NSIndexPath *subPath = [childLayout indexPathForMousePoint:pointInChild];
            return [subPath indexPathByPreceedingIndex:index];
        }
    }
    return [self indexPathForLocalMousePoint:point];
}


- (NSUInteger)indexOfLeadingChild
{
    return 0;
}


- (NSUInteger)indexOfTrailingChild
{
    return 0;
}


- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
{
    return NSNotFound;
}


- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{
    return NSNotFound;
}


- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{
    return index;
}


- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{
    return index;
}


- (NSIndexSet *)indexesOfRemainingChildren
{
    return [NSIndexSet indexSet];
}

@end
