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

#import "MPEvaluationContext.h"



@interface MPEvaluationContext ()

@property (nonatomic, strong) NSMutableArray *stack;

@end



@implementation MPEvaluationContext

static MPEvaluationContext *sharedContext;


+ (MPEvaluationContext *)sharedContext
{
    if (!sharedContext) {
        sharedContext = [[MPEvaluationContext alloc] init];
    }
    return sharedContext;
}


- (instancetype)init
{
    if (sharedContext) {
        return sharedContext;
    }
    self = [super init];
    if (self) {
        _stack = [[NSMutableArray alloc] init];
        [self push];
        [self defineVariable:@"e"
                       value:[[NSDecimalNumber alloc] initWithDouble:M_E]];
        [self defineVariable:@"π"
                       value:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
    }
    return self;
}


- (void)push
{
    [self.stack addObject:[[NSMutableDictionary alloc] init]];
}


- (void)pop
{
    [self.stack removeLastObject];
}


- (BOOL)defineVariable:(NSString *)variable
                 value:(NSDecimalNumber *)value
{
    [self undefineVariable:variable];
    if ([self isVariableDefined:variable]) {
        return NO;
    }
    NSMutableDictionary *currentBindings = self.stack.lastObject;
    currentBindings[variable] = value;
    return YES;
}


- (void)undefineVariable:(NSString *)variable
{
    NSMutableDictionary *currentBindings = self.stack.lastObject;
    [currentBindings removeObjectForKey:variable];
}


- (BOOL)isVariableDefined:(NSString *)variable
{
    return [self valueForVariable:variable] != nil;
}


- (NSDecimalNumber *)valueForVariable:(NSString *)variable
{
    NSUInteger currentIndex = self.stack.count;
    NSDictionary *currentBindings;
    NSDecimalNumber *value = nil;
    while (!value && currentIndex > 0) {
        currentBindings = self.stack[--currentIndex];
        value = currentBindings[variable];
    }
    return value;
}

@end
