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

#import "MPSumFunction.h"
#import "MPExpression.h"

#import "MPExpressionEvaluator.h"

@implementation MPSumFunction

#pragma mark Creation Methods
- (instancetype)init
{
    self = [super init];
    if (self) {
        _startExpression = [[MPExpression alloc] init];
        _startExpression.parent = self;
        _targetExpression = [[MPExpression alloc] init];
        _targetExpression.parent = self;
        _sumExpression = [[MPExpression alloc] init];
        _sumExpression.parent = self;
    }
    return self;
}

#pragma mark Properties
- (void)setStartExpression:(MPExpression *)startExpression
{
    _startExpression.parent = nil;
    _startExpression = startExpression;
    _startExpression.parent = self;
    [self didChangeChildAtIndex:0];
}

- (void)setTargetExpression:(MPExpression *)targetExpression
{
    _targetExpression.parent = nil;
    _targetExpression = targetExpression;
    _targetExpression.parent = self;
    [self didChangeChildAtIndex:1];
}

- (void)setSumExpression:(MPExpression *)sumExpression
{
    _sumExpression.parent = nil;
    _sumExpression = sumExpression;
    _sumExpression.parent = self;
    [self didChangeChildAtIndex:2];
}

#pragma mark Working With the Expression Tree
- (NSUInteger)numberOfChildren
{
    return 3;
}

- (MPExpression *)childAtIndex:(NSUInteger)index
{
    switch (index) {
        case 0:
            return self.startExpression;
        case 1:
            return self.targetExpression;
        case 2:
            return self.sumExpression;
        default:
            return nil;
    }
}

- (void)setChild:(MPExpression *)child
         atIndex:(NSUInteger)index
{
    switch (index) {
            // TODO: Copy child?
        case 0:
            self.startExpression = child;
            break;
        case 1:
            self.targetExpression = child;
            break;
        case 2:
            self.sumExpression = child;
            break;
        default:
            return;
    }
    [self didChangeChildAtIndex:index];
}

- (NSArray *)children
{
    return @[self.startExpression, self.targetExpression, self.sumExpression];
}

#pragma mark Evaluating Functions
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error
{
    MPExpressionEvaluator *startEvaluator = self.startExpression.evaluator;
    NSDecimalNumber *start = [startEvaluator evaluateVariableDefinition:YES error:error];
    if (!start) {
        return nil;
    }
    NSDecimalNumber *target = [self.targetExpression evaluateWithError:error];
    if (!target) {
        return nil;
    }
    
    MPExpressionEvaluator *sumEvaluator = self.sumExpression.evaluator;
    NSDecimalNumber *value = [NSDecimalNumber zero];
    for (NSDecimalNumber *iterator = start; [iterator compare:target] <= 0; iterator = [iterator decimalNumberByAdding:[[NSDecimalNumber alloc] initWithInteger:1]]) {
        [sumEvaluator bindValue:iterator toVariableName:startEvaluator.definedVariable];
        NSDecimalNumber *summand = [sumEvaluator evaluateWithError:error];
        if (summand) {
            value = [value decimalNumberByAdding:summand];
        } else {
            return nil;
        }
    }
    return value;
}

#pragma mark Working With Functions
/*
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
{
    if (![aFunction isKindOfClass:[MPSumFunction class]]) {
        return NO;
    }
    MPSumFunction *sumFunction = (MPSumFunction *)aFunction;
    if (![self.startExpression isEqualToExpression:sumFunction.startExpression]) {
        return NO;
    }
    if (![self.targetExpression isEqualToExpression:sumFunction.targetExpression]) {
        return NO;
    }
    return [self.sumExpression isEqualToExpression:sumFunction.sumExpression];
}*/

- (NSString *)description
{
    return [NSString stringWithFormat:@"Sum(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.sumExpression];
}

- (NSUInteger)hash
{
#warning Unimplemented Method
    return [super hash];
}

#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
    MPSumFunction *copy = [[MPSumFunction allocWithZone:zone] init];
    copy.startExpression = [self.startExpression copy];
    copy.targetExpression = [self.targetExpression copy];
    copy.sumExpression = [self.sumExpression copy];
    return copy;
}

@end
