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

#import "MPExpressionEvaluator.h"
#import "MPExpression.h"

@interface MPExpressionEvaluator ()
@property (readwrite, nonatomic, strong) NSString *definedVariable;
@end

@implementation MPExpressionEvaluator {
    NSMutableDictionary *_variableBindings;
    MPElementParser *parser;
}
- (id)initWithExpression:(MPExpression *)expression
{
    self = [super init];
    if (self) {
        _expression = expression;
        _variableBindings = [[NSMutableDictionary alloc] init];
        parser = [[MPElementParser alloc] init];
    }
    return self;
}

#pragma mark Evaluating Expressions
- (NSDictionary *)variableBindings
{
    return [_variableBindings copy];
}

- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name
{
    [_variableBindings setObject:value
                          forKey:name];
}

- (void)unbindVariableName:(NSString *)name
{
    [_variableBindings removeObjectForKey:name];
}

- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
{
    return [self evaluateVariableDefinition:NO error:error];
}

- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
{
#warning Implement Cache
    // Empty Expression
    if (self.expression.numberOfElements == 0) {
        if (error) {
            *error = MPParseError(0, @"Empty Expression");
        }
        return nil;
    }
    
    // Expression of just one element
    if (self.expression.numberOfElements == 1) {
        id<MPExpressionElement> singleElement = [self.expression elementAtIndex:0];
        if ([singleElement isString]) {
            MPParsedElement *parseResult = [parser parseElement:(NSString *)singleElement
                                                          error:error];
            if ([parseResult isValidStandaloneElement:error]) {
                if (flag && parseResult.definedVariable) {
                    self.definedVariable = parseResult.definedVariable;
                } else if (flag) {
                    if (error) {
                        *error = MPParseError(0, @"Expected Variable Definition");
                    }
                    return nil;
                } else if (parseResult.definedVariable) {
                    if (error) {
                        *error = MPParseError(0, @"Unexpected Variable Definition");
                    }
                    return nil;
                }
                return parseResult.standaloneValue;
            } else {
                return nil;
            }
        } else {
            return [singleElement evaluate:error];
        }
    }

    // Expression with any number of elements
    NSDecimalNumber *value = [NSDecimalNumber zero];
    NSDecimalNumber *currentSummand = nil;
    
    // Process the first element
    id<MPExpressionElement> firstElement = [self.expression elementAtIndex:0];
    if ([firstElement isString]) {
        MPParsedElement *parseResult = [parser parseElement:(NSString *)firstElement
                                                      error:error];
        if (parseResult && [parseResult isValidElementAtBeginning:error]) {
            if (flag && parseResult.definedVariable) {
                self.definedVariable = parseResult.definedVariable;
            } else if (flag) {
                if (error) {
                    *error = MPParseError(0, @"Expected Variable Definition");
                }
                return nil;
            } else if (parseResult.definedVariable) {
                if (error) {
                    *error = MPParseError(0, @"Unexpected Variable Definition");
                }
                return nil;
            }
            if ([parseResult isFactor]) {
                currentSummand = parseResult.value;
            } else {
                value = [parseResult valueAtBeginning];
                currentSummand = parseResult.suffixMultiplicator;
            }
        }
    } else {
        currentSummand = [firstElement evaluate:error];
    }
    if (!currentSummand) {
        return nil;
    }
    
    // Process the elements between the first and last element
    for (NSUInteger index = 1; index < self.expression.numberOfElements-1; index++) {
        id<MPExpressionElement> element = [self.expression elementAtIndex:index];
        if ([element isString]) {
            MPParsedElement *parseResult = [parser parseElement:(NSString *)element
                                                          error:error];
            if (parseResult) {
                if (parseResult.definedVariable) {
                    if (error) {
                        *error = MPParseError(index, @"Unexpected Variable Definition");
                    }
                    return nil;
                }
                if ([parseResult isFactor]) {
                    currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
                } else {
                    currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
                    value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
                    currentSummand = parseResult.suffixMultiplicator;
                }
            } else {
                return nil;
            }
        } else {
            NSDecimalNumber *result = [element evaluate:error];
            if (result) {
                currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
            } else {
                return nil;
            }
        }
    }
    
    // Process the last element
    id<MPExpressionElement> lastElement = [self.expression elementAtIndex:self.expression.numberOfElements-1];
    if ([lastElement isString]) {
        MPParsedElement *parseResult = [parser parseElement:(NSString *)lastElement
                                                      error:error];
        if (parseResult && [parseResult isValidElementAtEnd:error]) {
            if (parseResult.definedVariable) {
                if (error) {
                    *error = MPParseError(0, @"Unexpected Variable Definition");
                }
                return nil;
            }
            if ([parseResult isFactor]) {
                currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
            } else {
                currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
                value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
                currentSummand = parseResult.suffixMultiplicator;
            }
        } else {
            return nil;
        }
    } else {
        NSDecimalNumber *result = [lastElement evaluate:error];
        if (result) {
            currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
        } else {
            return nil;
        }
    }
    value = [value decimalNumberByAdding:currentSummand];
    
    return value;
}

@end
