// TLGlobals.m
// by David Phillip Oster
// September 2007
//
// Cocoa centric lisp
//
//  Copyright 2007 David Phillip Oster

//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at

//         http://www.apache.org/licenses/LICENSE-2.0

//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
#import "TLLisp.h"

static NSMutableArray *gTLSymbolTableStack = nil;
static NSMutableDictionary *gTLTopLevel;

TLAtom *gTLOpenParen;
TLAtom *gTLCloseParen;
TLAtom *gTLSingleQuote;
TLAtom *gTLT;
TLAtom *gTLLambda;


TLAtom *TLInternAndSet(NSString *name, Class applies) {
  TLAtom *atom = TLIntern(name);
  [atom setValue:(TLID *)applies];
  return atom;
}


void TLInit(void) {
  if (nil == gTLSymbolTableStack) {
    gTLSymbolTableStack = [[NSMutableArray alloc] init];
    gTLTopLevel = [NSMutableDictionary dictionary];
    [gTLSymbolTableStack addObject:gTLTopLevel];
    gTLOpenParen = TLIntern(@"(");
    gTLCloseParen = TLIntern(@")");
    gTLSingleQuote = TLIntern(@"'");
    gTLT = TLIntern(@"T");
    unichar c = 0x3BB;
    NSMutableString *lambda = [NSMutableString stringWithCharacters:&c length:1];
    gTLLambda = TLIntern(lambda);

    TLInternAndSet(@"progn", [TLFuncprogn class]);
    TLInternAndSet(@"prog0", [TLFuncprog0 class]);
    TLInternAndSet(@"'", [TLFuncquote class]);
    TLInternAndSet(@"set", [TLFuncset class]);
    TLInternAndSet(@"perform", [TLFuncPerformSelector class]);
    TLInternAndSet(@"class", [TLFuncClassFromString class]);

    TLInternAndSet(@"if", [TLFunctlIf class]);
    TLInternAndSet(@"while", [TLFunctlWhile class]);
    TLInternAndSet(@"and", [TLFunctlAnd class]);
    TLInternAndSet(@"or", [TLFunctlOr class]);

    TLInternAndSet(@"+", [TLFuncplus class]);
    TLInternAndSet(@"-", [TLFuncminus class]);
    TLInternAndSet(@"*", [TLFunctimes class]);
    TLInternAndSet(@"/", [TLFuncdivide class]);

    TLInternAndSet(@"=", [TLFunceq class]);
    TLInternAndSet(@">", [TLFuncgt class]);
    TLInternAndSet(@"<", [TLFunclt class]);
    TLInternAndSet(@">=", [TLFuncge class]);
    TLInternAndSet(@"<=", [TLFuncle class]);

    TLInternAndSet(@"print", [TLFuncprint class]);

    TLInternAndSet(@"list",  [TLFunclist class]);
    TLInternAndSet(@"count", [TLFunccount class]);
    TLInternAndSet(@"at",    [TLFuncAtIndex class]);
    TLInternAndSet(@"insert",   [TLFuncInsertAtIndex class]);
    TLInternAndSet(@"remove",   [TLFuncRemoveAtIndex class]);
    TLInternAndSet(@"replace",   [TLFuncReplaceAtIndexWith class]);

    // property support
    TLInternAndSet(@"propertyForKey", [TLFuncPropertyForKey class]);
    TLInternAndSet(@"removePropertyForKey", [TLFuncRemovePropertyForKey class]);
    TLInternAndSet(@"setPropertyForKey", [TLFuncSetPropertyForKey class]);
    TLInternAndSet(@"propertyKeys", [TLFuncPropertyKeys class]);

    TLInternAndSet(@"nil", nil);

  }
}

void TLShutdown(void) {
  int i;
  for(i = [gTLSymbolTableStack count] - 1;0 <= i; --i){
    gTLTopLevel = [gTLSymbolTableStack lastObject];
    NSArray *allKeys = [gTLTopLevel allKeys];
    NSEnumerator *enumerator = [allKeys objectEnumerator];
    NSString *key;
    while (nil != (key = [enumerator nextObject])) {
      TLAtom *atom = [gTLTopLevel objectForKey:key];
      [atom setOwner:nil];  // break the backlink, since we are dieing
      [atom setValue:nil];  // break the default cycle, since we are dieing
    }
    [gTLSymbolTableStack removeLastObject];
  }
  [gTLSymbolTableStack release];
  gTLSymbolTableStack = nil;
  gTLTopLevel = nil;
}


NSMutableDictionary *TLTopLevel(void) {
  return gTLTopLevel;
}

TLAtom *TLIntern(NSString *name) {
  TLAtom *atom = [gTLTopLevel objectForKey:name];
  if (nil == atom) {
    atom = [[[TLAtom alloc] initWithOwner:gTLTopLevel] autorelease];
    [gTLTopLevel setObject:atom forKey:name];
    [atom setValue:atom];
  }
  return atom;
}


// bind an NSArray of atoms to the values: args returns a token for unbinding.
// note that the array here called "args" is really the call: the first element is the lambda expression we'll be evaluating
TLID *TLBind(NSArray *formals, NSArray *args) {
  NSMutableDictionary *tokenDict = [NSMutableDictionary dictionary];
  int i, iCount = [formals count];
  for(i = 0;  i < iCount; ++i) {
    NSString *key;
    key = [[formals objectAtIndex:i] name];
    TLID *val = [args objectAtIndex:i+1];
    TLAtom *atom = [gTLTopLevel objectForKey:key];
    TLID *oldValue = [atom value];
    if (nil != oldValue) {
      [tokenDict setObject:oldValue forKey:[NSValue valueWithPointer:atom]];
    }
    [atom setValue:val];
  }
  
  return tokenDict;
}


// unbind, using the token.
void TLUnbind(TLID *token) {
  NSMutableDictionary *tokenDict = (NSMutableDictionary *) token;
  NSArray *allKeys = [tokenDict allKeys];
  NSEnumerator *enumerator = [allKeys objectEnumerator];
  NSValue *key;
  while (nil != (key = [enumerator nextObject])) {
    TLID *oldValue = [tokenDict objectForKey:key];
    TLAtom *atom = [key pointerValue];
    [atom setValue:oldValue];
  }
}



