// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// $Header: /cvsroot/acdk/acdk/acdk_lisp/src/acdk/lisp/LispEnvironment.h,v 1.16 2005/04/08 10:53:20 kommer Exp $
#ifndef acdk_lisp_LispEnvironment_h
#define acdk_lisp_LispEnvironment_h

#include "LispCode.h"
#include "LispTokenizer.h"
#include "StackFrame.h"

#include <acdk/io/File.h>
#include <acdk/util/HashMap.h>
#include <acdk/util/HashSet.h>
#include <acdk/util/Properties.h>

#include <acdk/io/PrintWriter.h>
#include <acdk/io/InputReader.h>

namespace acdk {
namespace lisp {

typedef RLispVar (*LispNativFunction)(IN(RLispEnvironment) env, IN(RLispList) args);

using namespace acdk::lang;
using namespace acdk::io;
using namespace acdk::util;

USING_CLASS(acdk::io::, File);
USING_CLASS(acdk::io::, Reader);
USING_CLASS(acdk::io::, Writer);
USING_CLASS(acdk::util::, HashMap);


  Represents an Lisp-Interpreter
  @author Roger Rene Kommer
  @version $Revision: 1.16 $
  @date $Date: 2005/04/08 10:53:20 $
class ACDK_ACDK_LISP_PUBLIC LispEnvironment
: public acdk::lang::Object
  /// global instance
  static LispEnvironment* _lenv;
  /// this will be used if not already set
  static RLispEnvironment _glenv;
  /** normal buildin function*/
  static acdk::util::RHashMap __staticFuncs; // RString=RFunction
  static RLispAtom __trueVar; // true
  acdk::util::RProperties _environment; // RString=RString
  acdk::util::RHashMap _globals; // RString=RListVar
  acdk::util::RHashMap _defuns; // RString=RLispFunction
  acdk::util::RHashMap _macros; // RString=RLispFunction
  /** all fq names of included lsp-files */
  acdk::util::RHashSet _includes; // RString
  acdk::util::RHashSet _tracedSymbols; // RString
  bool _trace;
  RLispVar _lastEvaled;
  bool _returnNow;
  int _tracelevel;
  int _break;
  bool _exitNow;
  int _exitValue;
  RStringArray _cmlineArgs;

  acdk::io::RPrintWriter out;
  acdk::io::RPrintWriter err;
  acdk::io::RInputReader in;
    The current Modul stack 
  Stack<RFile> _modulStack;
  Stack<RLispStackFrame> _stackFrame;
  Stack<RLispVar> _evalStack;

  LispEnvironment(IN(acdk::util::RProperties) environment = Nil, IN(RStringArray) args = Nil, bool trace = false);
    loads standard lisp files in $(ACDK_HOME)/cfg/lib/acdk/lisp/autoload.lsp.
    @throws Error if ACDK_HOME is not defined.
    @param loadCode load code instead of image
  void init(bool loadCode = false);

    initialize environmnet variables
  void  initEnv();
    uninizialize the LispEnvironment.
    needed to release this instance.
  void deinit();
  RString parseEval(const char*& ptr, int& rest);
  RLispVar parseEval(IN(RString) str);
  static RLispList parseToList(IN(RString) str);

  RLispCode parse(IN(RLispTokenizer) in, IN(acdk::io::RPrintWriter) out, bool interactiv = false, bool parseOneToken = false);
  void interactive(IN(acdk::io::RCharReader) in, IN(acdk::io::RCharWriter) out);
  virtual RLispVar eval(IN(RLispVar) list);
    Lookup for var in 
    <li> locals in scopes from inner to outer
    <li> functions
    <li> global variables (setg)
    <li> environmnet
  RLispVar lookupVar(IN(RString) str, bool warn = true);
    Lookup for var in only local scopes
    returns Nil if not found
  RLispVar lookupLocalVar(IN(RString) str);
  RFunction lookupFunction(IN(RString) str);
  bool isMacro(IN(RString) str) 
    RFunction f = lookupFunction(str);
    if (f == Nil || instanceof(f, LispFunction) == false)
      return false;
    return RLispFunction(f)->isMacro();
  virtual void bindLocal(IN(RString) symbol, IN(RLispVar) value, bool forcelocal = false);
  virtual void bindGlobal(IN(RString) symbol, IN(RLispVar) value);
  virtual void bindGlobal(IN(RString) symbol, IN(RString) value) { bindGlobal(symbol, new LispAtom(value)); }
  virtual void bindToEnv(IN(RString) symbol, IN(RLispVar) value);
  virtual void bindToEnv(IN(RString) symbol, IN(RString) value) { bindToEnv(symbol, new LispAtom(value)); }
  static RLispAtom t();
  static void registerFunction(const char* name, const char* decl, LispNativFunction func, bool preeval = true);
  void registerDefun(IN(RLispFunction) func);
    load code into Lisp-Environment 
    @arg in the Intput
    @return The result of the evaluated code
  RString load(IN(RLispTokenizer) tok);
  RString load(IN(RString) filename);

  RString loadUnparsedFile(IN(RString) filename); 
    Debugging function
  void dumpEnv(IN(acdk::io::RCharWriter) out);
    Debugging function
  bool trace() { return _trace; }
    Debugging function
  void trace(bool t) { _trace = t; }
    Debugging function
  bool trace(IN(RString) symbol) { return _tracedSymbols->contains((acdk::lang::Object)symbol); }
    Debugging function
  void trace(IN(RString) symbol, bool dotrace) 
    if (dotrace)
    else if (trace(symbol) == false)
  void traceln(IN(RString) out);
  void traceflush(IN(RString) str);
  void trace_begin(IN(RString) out);
  void trace_end(IN(RString) out);
  void setBreak(int b) { _break = b; }
  int getBreak() { return _break; }
  RLispVar debug_interactive(IN(RLispVar) var);
  RLispVar lastEvaled() { return _lastEvaled; }
  RString lastEvaledString() { return _lastEvaled == Nil ? RString("") : _lastEvaled->toString(); }
  void returnNow(bool set) { _returnNow = set; }
  bool returnNow() { return _returnNow; }
  void exitNow(int exitval) { _exitNow = true; _exitValue = exitval; }
  bool exitNow() { return _exitNow; }
  int exitValue() { return _exitValue; }
  /** returns an KeyIterator, where value is RFunction */
  acdk::util::RIterator functionIterator();
  acdk::util::RIterator buildinsIterator();
  acdk::util::RIterator defunsIterator();

  acdk::util::RProperties environment() { return _environment; }
  acdk::util::RHashMap globals() { return _globals; }
  static acdk::util::RHashMap _staticFuncs();
  acdk::util::RHashSet includes() { return _includes; }
  void setInOut(IN(RCharReader) rin, IN(RCharWriter) rout, IN(RCharWriter) rerr);

  RLispVar _eval(IN(RLispVar) list);
  RLispVar _eval(IN(RLispList) list);
  RLispVar _eval(IN(RLispSymbol) list);
  RLispVar _eval(IN(RLispAtom) list);

    store all compiled functions, macros an globals variables
    a file.
    Environment and build in functions will not be stored
  void storeCompiled(IN(RString) file);
  void storeCompiled(IN(::acdk::io::RWriter) out);
    Loads image file
    @param replace replace current env.
  void loadCompiled(IN(RString) file, bool replace);
    Loads image file
    @param replace replace current env.
  void loadCompiled(IN(::acdk::io::RReader) in, bool replace);

  static RLispEnvironment lenv() 
    if (_lenv != 0)
      return _lenv; 
    _glenv = new LispEnvironment();
    return _lenv;

  friend class LispBinaryCode;

class ACDK_ACDK_LISP_PUBLIC StackVarHolder
  RLispEnvironment _env;
  StackVarHolder(IN(RLispEnvironment) env, bool boundtoparent = false) 
  : _env(env)
    if (boundtoparent == true && env->_stackFrame.size() > 0)
      env->_stackFrame.push(new LispStackFrame(env->_stackFrame.top())); 
      env->_stackFrame.push(new LispStackFrame());


void testLisp();

} // namespace lisp
} // namespace acdk
#endif //acdk_lisp_LispEnvironment_h