/* bzflag
 * Copyright (c) 1993-2017 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#if defined(_MSC_VER)
#pragma warning(4:4503)
#endif

// interface header
#include "StateDatabase.h"

// system headers
#include <assert.h>
#include <ctype.h>
#include <stack>
#include <set>
#include <iostream>
#include <math.h>
#include <string>
#include <string.h>

// local implementation headers
#include "ErrorHandler.h"
#include "TextUtils.h"

#if defined(DEBUG) || defined(_DEBUG)
// headers needed only for _debugLookups()
#include <map>
#include "TimeKeeper.h"

void	_debugLookups(const std::string &name)
{
  if (!BZDB.getDebug())
    return;

  typedef std::map<std::string,int> EvalCntMap;
  static const float interval = 20.0f;

  /* This bit of nastyness help debug BDZB->eval accesses sorted from worst to best*/
  static EvalCntMap cnts;
  static TimeKeeper last = TimeKeeper::getCurrent();

  EvalCntMap::iterator it = cnts.find(name);
  if (it == cnts.end())
    cnts[name] = 1;
  else
    it->second++;

  TimeKeeper now = TimeKeeper::getCurrent();
  if (now - last > interval) {
    std::multimap<int,std::string> order;
    for (it = cnts.begin(); it != cnts.end(); it++) {
      order.insert(std::pair<int,std::string>(-it->second, it->first));
      it->second = 0;
    }

    for (std::multimap<int,std::string>::iterator it2 = order.begin(); it2 != order.end(); ++it2) {
      if (-it2->first / interval < 1.0f)
	break;
      logDebugMessage(1,"%-25s = %.2f acc/sec\n", it2->second.c_str(), -it2->first / interval);
    }
    last = now;
  }
}

  #define debugLookups(name) _debugLookups(name)
#else
  #define debugLookups(name)
#endif

// initialize the singleton
template <>
StateDatabase* Singleton<StateDatabase>::_instance = (StateDatabase*)0;


const std::string StateDatabase::BZDB_AGILITYADVEL	   = std::string("_agilityAdVel");
const std::string StateDatabase::BZDB_AGILITYTIMEWINDOW	   = std::string("_agilityTimeWindow");
const std::string StateDatabase::BZDB_AGILITYVELDELTA	   = std::string("_agilityVelDelta");
const std::string StateDatabase::BZDB_AMBIENTLIGHT	   = std::string("_ambientLight");
const std::string StateDatabase::BZDB_ANGLETOLERANCE	   = std::string("_angleTolerance");
const std::string StateDatabase::BZDB_ANGULARAD		   = std::string("_angularAd");
const std::string StateDatabase::BZDB_AVENUESIZE	   = std::string("_avenueSize");
const std::string StateDatabase::BZDB_BASESIZE		   = std::string("_baseSize");
const std::string StateDatabase::BZDB_BOXBASE		   = std::string("_boxBase");
const std::string StateDatabase::BZDB_BOXHEIGHT		   = std::string("_boxHeight");
const std::string StateDatabase::BZDB_BURROWDEPTH	   = std::string("_burrowDepth");
const std::string StateDatabase::BZDB_BURROWSPEEDAD	   = std::string("_burrowSpeedAd");
const std::string StateDatabase::BZDB_BURROWANGULARAD	   = std::string("_burrowAngularAd");
const std::string StateDatabase::BZDB_COLDETDEPTH	   = std::string("_coldetDepth");
const std::string StateDatabase::BZDB_COLDETELEMENTS	   = std::string("_coldetElements");
const std::string StateDatabase::BZDB_COUNTDOWNRESDELAY	   = std::string("_countdownResumeDelay");
const std::string StateDatabase::BZDB_CULLDEPTH		   = std::string("_cullDepth");
const std::string StateDatabase::BZDB_CULLELEMENTS	   = std::string("_cullElements");
const std::string StateDatabase::BZDB_CULLOCCLUDERS	   = std::string("_cullOccluders");
const std::string StateDatabase::BZDB_DISABLEBOTS	   = std::string("_disableBots");
const std::string StateDatabase::BZDB_DRAWCELESTIAL	   = std::string("_drawCelestial");
const std::string StateDatabase::BZDB_DRAWCLOUDS	   = std::string("_drawClouds");
const std::string StateDatabase::BZDB_DRAWGROUND	   = std::string("_drawGround");
const std::string StateDatabase::BZDB_DRAWGROUNDLIGHTS	   = std::string("_drawGroundLights");
const std::string StateDatabase::BZDB_DRAWMOUNTAINS	   = std::string("_drawMountains");
const std::string StateDatabase::BZDB_DRAWSKY		   = std::string("_drawSky");
const std::string StateDatabase::BZDB_ENDSHOTDETECTION	   = std::string("_endShotDetection");
const std::string StateDatabase::BZDB_EXPLODETIME	   = std::string("_explodeTime");
const std::string StateDatabase::BZDB_FLAGALTITUDE	   = std::string("_flagAltitude");
const std::string StateDatabase::BZDB_FLAGEFFECTTIME	   = std::string("_flagEffectTime");
const std::string StateDatabase::BZDB_FLAGHEIGHT	   = std::string("_flagHeight");
const std::string StateDatabase::BZDB_FLAGPOLEWIDTH	   = std::string("_flagPoleWidth");
const std::string StateDatabase::BZDB_FLAGPOLESIZE	   = std::string("_flagPoleSize");
const std::string StateDatabase::BZDB_FLAGRADIUS	   = std::string("_flagRadius");
const std::string StateDatabase::BZDB_FOGMODE		   = std::string("_fogMode");
const std::string StateDatabase::BZDB_FOGDENSITY	   = std::string("_fogDensity");
const std::string StateDatabase::BZDB_FOGSTART		   = std::string("_fogStart");
const std::string StateDatabase::BZDB_FOGEND		   = std::string("_fogEnd");
const std::string StateDatabase::BZDB_FOGCOLOR		   = std::string("_fogColor");
const std::string StateDatabase::BZDB_FRICTION		   = std::string("_friction");
const std::string StateDatabase::BZDB_GMACTIVATIONTIME	   = std::string("_gmActivationTime");
const std::string StateDatabase::BZDB_GMADLIFE		   = std::string("_gmAdLife");
const std::string StateDatabase::BZDB_GMSIZE		   = std::string("_gmSize");
const std::string StateDatabase::BZDB_GMTURNANGLE	   = std::string("_gmTurnAngle");
const std::string StateDatabase::BZDB_GRAVITY		   = std::string("_gravity");
const std::string StateDatabase::BZDB_HANDICAPSCOREDIFF	   = std::string("_handicapScoreDiff");
const std::string StateDatabase::BZDB_HANDICAPVELAD	   = std::string("_handicapVelAd");
const std::string StateDatabase::BZDB_HANDICAPANGAD	   = std::string("_handicapAngAd");
const std::string StateDatabase::BZDB_HANDICAPSHOTAD	   = std::string("_handicapShotAd");
const std::string StateDatabase::BZDB_HIDEFLAGSONRADAR	   = std::string("_hideFlagsOnRadar");
const std::string StateDatabase::BZDB_HIDETEAMFLAGSONRADAR = std::string("_hideTeamFlagsOnRadar");
const std::string StateDatabase::BZDB_IDENTIFYRANGE	   = std::string("_identifyRange");
const std::string StateDatabase::BZDB_JUMPVELOCITY	   = std::string("_jumpVelocity");
const std::string StateDatabase::BZDB_LASERADVEL	   = std::string("_laserAdVel");
const std::string StateDatabase::BZDB_LASERADRATE	   = std::string("_laserAdRate");
const std::string StateDatabase::BZDB_LASERADLIFE	   = std::string("_laserAdLife");
const std::string StateDatabase::BZDB_LATITUDE		   = std::string("_latitude");
const std::string StateDatabase::BZDB_LOCKONANGLE	   = std::string("_lockOnAngle");
const std::string StateDatabase::BZDB_LONGITUDE		   = std::string("_longitude");
const std::string StateDatabase::BZDB_LRADRATE		   = std::string("_lRAdRate");
const std::string StateDatabase::BZDB_MAXBUMPHEIGHT	   = std::string("_maxBumpHeight");
const std::string StateDatabase::BZDB_MAXFLAGGRABS	   = std::string("_maxFlagGrabs");
const std::string StateDatabase::BZDB_MAXLOD		   = std::string("_maxLOD");
const std::string StateDatabase::BZDB_MIRROR		   = std::string("_mirror");
const std::string StateDatabase::BZDB_MOMENTUMLINACC	   = std::string("_momentumLinAcc");
const std::string StateDatabase::BZDB_MOMENTUMANGACC	   = std::string("_momentumAngAcc");
const std::string StateDatabase::BZDB_MOMENTUMFRICTION	   = std::string("_momentumFriction");
const std::string StateDatabase::BZDB_MGUNADVEL		   = std::string("_mGunAdVel");
const std::string StateDatabase::BZDB_MGUNADRATE	   = std::string("_mGunAdRate");
const std::string StateDatabase::BZDB_MGUNADLIFE	   = std::string("_mGunAdLife");
const std::string StateDatabase::BZDB_MUZZLEFRONT	   = std::string("_muzzleFront");
const std::string StateDatabase::BZDB_MUZZLEHEIGHT	   = std::string("_muzzleHeight");
const std::string StateDatabase::BZDB_NOCLIMB		   = std::string("_noClimb");
const std::string StateDatabase::BZDB_NOSHADOWS		   = std::string("_noShadows");
const std::string StateDatabase::BZDB_NOSMALLPACKETS	   = std::string("_noSmallPackets");
const std::string StateDatabase::BZDB_NOTRESPONDINGTIME	   = std::string("_notRespondingTime");
const std::string StateDatabase::BZDB_OBESEFACTOR	   = std::string("_obeseFactor");
const std::string StateDatabase::BZDB_PAUSEDROPTIME	   = std::string("_pauseDropTime");
const std::string StateDatabase::BZDB_POSITIONTOLERANCE	   = std::string("_positionTolerance");
const std::string StateDatabase::BZDB_PYRBASE		   = std::string("_pyrBase");
const std::string StateDatabase::BZDB_PYRHEIGHT		   = std::string("_pyrHeight");
const std::string StateDatabase::BZDB_RADARLIMIT	   = std::string("_radarLimit");
const std::string StateDatabase::BZDB_REJOINTIME	   = std::string("_rejoinTime");
const std::string StateDatabase::BZDB_RELOADTIME	   = std::string("_reloadTime");
const std::string StateDatabase::BZDB_RFIREADVEL	   = std::string("_rFireAdVel");
const std::string StateDatabase::BZDB_RFIREADRATE	   = std::string("_rFireAdRate");
const std::string StateDatabase::BZDB_RFIREADLIFE	   = std::string("_rFireAdLife");
const std::string StateDatabase::BZDB_SHIELDFLIGHT	   = std::string("_shieldFlight");
const std::string StateDatabase::BZDB_SHOCKADLIFE	   = std::string("_shockAdLife");
const std::string StateDatabase::BZDB_SHOCKINRADIUS	   = std::string("_shockInRadius");
const std::string StateDatabase::BZDB_SHOCKOUTRADIUS	   = std::string("_shockOutRadius");
const std::string StateDatabase::BZDB_SHOTRADIUS	   = std::string("_shotRadius");
const std::string StateDatabase::BZDB_SHOTRANGE		   = std::string("_shotRange");
const std::string StateDatabase::BZDB_SHOTSPEED		   = std::string("_shotSpeed");
const std::string StateDatabase::BZDB_SHOTTAILLENGTH	   = std::string("_shotTailLength");
const std::string StateDatabase::BZDB_SHOTSKEEPVERTICALV   = std::string("_shotsKeepVerticalVelocity");
const std::string StateDatabase::BZDB_SPEEDCHECKSLOGONLY   = std::string("_speedChecksLogOnly");
const std::string StateDatabase::BZDB_SRRADIUSMULT	   = std::string("_srRadiusMult");
const std::string StateDatabase::BZDB_SQUISHFACTOR	   = std::string("_squishFactor");
const std::string StateDatabase::BZDB_SQUISHTIME	   = std::string("_squishTime");
const std::string StateDatabase::BZDB_SYNCTIME		   = std::string("_syncTime");
const std::string StateDatabase::BZDB_SYNCLOCATION	   = std::string("_syncLocation");
const std::string StateDatabase::BZDB_TANKANGVEL	   = std::string("_tankAngVel");
const std::string StateDatabase::BZDB_TANKEXPLOSIONSIZE	   = std::string("_tankExplosionSize");
const std::string StateDatabase::BZDB_TANKHEIGHT	   = std::string("_tankHeight");
const std::string StateDatabase::BZDB_TANKLENGTH	   = std::string("_tankLength");
const std::string StateDatabase::BZDB_TANKRADIUS	   = std::string("_tankRadius");
const std::string StateDatabase::BZDB_TANKSPEED		   = std::string("_tankSpeed");
const std::string StateDatabase::BZDB_TANKWIDTH		   = std::string("_tankWidth");
const std::string StateDatabase::BZDB_TARGETINGANGLE	   = std::string("_targetingAngle");
const std::string StateDatabase::BZDB_TARGETINGDISTANCE	   = std::string("_targetingDistance");
const std::string StateDatabase::BZDB_TELEBREADTH	   = std::string("_teleportBreadth");
const std::string StateDatabase::BZDB_TELEHEIGHT	   = std::string("_teleportHeight");
const std::string StateDatabase::BZDB_TELEPORTTIME	   = std::string("_teleportTime");
const std::string StateDatabase::BZDB_TELEWIDTH		   = std::string("_teleportWidth");
const std::string StateDatabase::BZDB_THIEFADLIFE	   = std::string("_thiefAdLife");
const std::string StateDatabase::BZDB_THIEFADRATE	   = std::string("_thiefAdRate");
const std::string StateDatabase::BZDB_THIEFADSHOTVEL	   = std::string("_thiefAdShotVel");
const std::string StateDatabase::BZDB_THIEFTINYFACTOR	   = std::string("_thiefTinyFactor");
const std::string StateDatabase::BZDB_THIEFVELAD	   = std::string("_thiefVelAd");
const std::string StateDatabase::BZDB_THIEFDROPTIME	   = std::string("_thiefDropTime");
const std::string StateDatabase::BZDB_TINYFACTOR	   = std::string("_tinyFactor");
const std::string StateDatabase::BZDB_TRACKFADE		   = std::string("_trackFade");
const std::string StateDatabase::BZDB_UPDATETHROTTLERATE   = std::string("_updateThrottleRate");
const std::string StateDatabase::BZDB_VELOCITYAD	   = std::string("_velocityAd");
const std::string StateDatabase::BZDB_WALLHEIGHT	   = std::string("_wallHeight");
const std::string StateDatabase::BZDB_WEAPONS		   = std::string("_weapons");
const std::string StateDatabase::BZDB_WIDEANGLEANG	   = std::string("_wideAngleAng");
const std::string StateDatabase::BZDB_WINGSGRAVITY	   = std::string("_wingsGravity");
const std::string StateDatabase::BZDB_WINGSJUMPCOUNT	   = std::string("_wingsJumpCount");
const std::string StateDatabase::BZDB_WINGSJUMPVELOCITY	   = std::string("_wingsJumpVelocity");
const std::string StateDatabase::BZDB_WINGSSLIDETIME	   = std::string("_wingsSlideTime");
const std::string StateDatabase::BZDB_WORLDSIZE		   = std::string("_worldSize");

//
// StateDatabase::Item
//

StateDatabase::Item::Item() : value(),
			      defValue(),
			      isSet(false),
			      isTrue(false),
			      save(true), // FIXME -- false by default?
			      permission(ReadWrite)
{
  // do nothing
}


//
// StateDatabase
//


StateDatabase::StateDatabase() : debug(false), saveDefault(false)
{
  // do nothing
}

StateDatabase::~StateDatabase()
{
  // do nothing
}

void			StateDatabase::set(const std::string& name,
					   const std::string& value,
					   Permission access)
{
  Map::iterator index = lookup(name);
  if (access >= index->second.permission) {
    index->second.value  = value;
    index->second.isSet  = true;
    std::string valuelow = TextUtils::tolower(value);
    index->second.isTrue = (valuelow != "0" &&
			    valuelow != "off" &&
			    valuelow != "false" &&
			    valuelow != "no" &&
			    valuelow != "disable");

	if (saveDefault)
		index->second.defValue = value;
    notify(index);
  }
}

void		     StateDatabase::setInt(const std::string& name,
					   const int& value,
					   Permission access)
{
  set(name,TextUtils::format("%d",value),access);
}

void		    StateDatabase::setBool(const std::string& name,
					   const bool& value,
					   Permission access)
{
  set(name,value ? std::string("1") : std::string("0"),access);
}

void		   StateDatabase::setFloat(const std::string& name,
					   const float& value,
					   Permission access)
{
  set(name,TextUtils::format("%f",value),access);
}

void			StateDatabase::setPointer(const std::string& name,
					   const void * value,
					   Permission access)
{
  char address[32];
  memset(address, 0, 32);
  snprintf(address, 32, "%lu", (unsigned long)value);
  std::string ssaddress = address;
  this->set(name, ssaddress, access);
}


void			StateDatabase::unset(const std::string& name,
					     Permission access)
{
  Map::iterator index = lookup(name);
  if (access >= index->second.permission) {
    index->second.value  = "";
    index->second.isSet  = false;
    index->second.isTrue = false;
    notify(index);
  }
}

void			StateDatabase::touch(const std::string& name,
					     Permission access)
{
  Map::iterator index = lookup(name);
  if (access >= index->second.permission)
    notify(index);
}

void			StateDatabase::setPersistent(
					const std::string& name, bool save)
{
  Map::iterator index = lookup(name);
  index->second.save = save;
}

void			StateDatabase::setDefault(
					const std::string& name, const std::string& value)
{
  Map::iterator index = lookup(name);
  index->second.defValue = value;
}

void			StateDatabase::setPermission(
					const std::string& name,
					Permission permission)
{
  Map::iterator index = lookup(name);
  index->second.permission = permission;
}

void			StateDatabase::addCallback(
					const std::string& name,
					Callback callback,
					void* userData)
{
  Map::iterator index = lookup(name);
  index->second.callbacks.add(callback, userData);
}

void			StateDatabase::removeCallback(
					const std::string& name,
					Callback callback,
					void* userData)
{
  Map::iterator index = lookup(name);
  index->second.callbacks.remove(callback, userData);
}

void StateDatabase::addGlobalCallback(Callback callback, void* userData)
{
  globalCallbacks.add(callback, userData);
}

void StateDatabase::removeGlobalCallback(Callback callback, void* userData)
{
  globalCallbacks.remove(callback, userData);
}


bool			StateDatabase::isSet(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  return !(index == items.end() || !index->second.isSet);
}

std::string		StateDatabase::get(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  if (index == items.end() || !index->second.isSet)
    return std::string();
  else
    return index->second.value;
}

int		StateDatabase::getIntClamped(const std::string& name, const int min, const int max) const
{
  int val;
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  if (index == items.end() || !index->second.isSet)
    val=0;
  else
    val = atoi(index->second.value.c_str());
  if (val < min)
    return min;
  else if (val > max)
    return max;
  return val;
}

void *		StateDatabase::getPointer(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  if (index == items.end() || !index->second.isSet)
    return (void *)NULL;
  else
    return (void *)strtoul(index->second.value.c_str(), NULL, 0);
}

float			StateDatabase::eval(const std::string& name)
{
  typedef std::set<std::string> VariableSet;
  debugLookups(name);

  EvalMap::const_iterator cit = evalCache.find(name);
  if (cit != evalCache.end())
    return cit->second;

  //this is to catch recursive definitions
  static VariableSet variables;
  // ugly hack, since gcc 2.95 doesn't have <limits>
  float NaN;
  memset(&NaN, 0xff, sizeof(float));

  if (variables.find(name) != variables.end())
    return NaN;

  VariableSet::iterator ins_it = variables.insert(name).first;

  Map::const_iterator index = items.find(name);
  if (index == items.end() || !index->second.isSet || index->second.value.empty()) {
    variables.erase(ins_it);
    return NaN;
  }
  Expression pre, inf;
  std::string value = index->second.value;
  if (!value.size())
	  return NaN;
  value >> inf;
  pre = infixToPrefix(inf);
  float retn = evaluate(pre);
  variables.erase(ins_it);

  if (retn != NaN)
    evalCache[name] = retn;
  return retn;
}

int			StateDatabase::evalInt(const std::string& name)
{
  return (int)eval(name);
}

bool		StateDatabase::evalTriplet(const std::string& name, float data[4])
{
  if (!isSet(name) || !data)
    return false;
  if (sscanf(get(name).c_str(), "%f %f %f", data, data+1, data+2) != 3)
    return false;
  return true;
}

bool		StateDatabase::evalPair(const std::string& name, float data[2])
{
  if (!isSet(name) || !data)
    return false;
  if (sscanf(get(name).c_str(), "%f %f", data, data+1) != 2)
    return false;
  return true;
}

bool			StateDatabase::isTrue(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  return !(index == items.end() || !index->second.isTrue);
}

bool			StateDatabase::isEmpty(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  return (index == items.end() || !index->second.isSet ||
	  index->second.value.empty());
}

bool			StateDatabase::isPersistent(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  return (index != items.end() && index->second.save);
}

std::string		StateDatabase::getDefault(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  if (index != items.end())
    return index->second.defValue;
  else
    return "";
}

StateDatabase::Permission
			StateDatabase::getPermission(const std::string& name) const
{
  debugLookups(name);
  Map::const_iterator index = items.find(name);
  if (index != items.end())
    return index->second.permission;
  else
    return ReadWrite;
}

StateDatabase::Map::iterator
			StateDatabase::lookup(const std::string& name)
{
  debugLookups(name);
  Map::iterator index = items.find(name);
  if (index == items.end()) {
    Item tmp;
    return items.insert(std::make_pair(name, tmp)).first;
  } else {
    return index;
  }
}

void			StateDatabase::notify(Map::iterator index)
{
  const std::string& name = index->first;
  const Item& item = index->second;

  evalCache.erase(index->first);

  void* namePtr = const_cast<void*>(static_cast<const void*>(&name));
  globalCallbacks.iterate(&onCallback, namePtr);
  item.callbacks.iterate(&onCallback, namePtr);
}

bool			StateDatabase::onCallback(Callback callback,
						  void* userData,
						  void* iterateData)
{
  callback(*static_cast<std::string*>(iterateData), userData);
  return true;
}

void			StateDatabase::iterate(Callback callback, void* userData) const
{
  assert(callback != NULL);

  for (Map::const_iterator index = items.begin(); index != items.end(); ++index) {
    if (index->second.isSet) {
      (*callback)(index->first, userData);
    }
  }
}

void			StateDatabase::write(Callback callback, void* userData) const
{
  assert(callback != NULL);

  for (Map::const_iterator index = items.begin(); index != items.end(); ++index) {
    if (index->second.isSet && index->second.save) {
      (*callback)(index->first, userData);
    }
  }
}

void		     StateDatabase::setDebug(bool print) {
  debug = print;
}

void		     StateDatabase::setSaveDefault(bool save) {
  saveDefault = save;
}

StateDatabase::ExpressionToken::ExpressionToken()
{
  tokenType = Number;
  tokenContents.number = 0;
}

StateDatabase::ExpressionToken::ExpressionToken(Type _tokenType)
{
  tokenType = _tokenType;
  switch (tokenType) {
    case Number:
      tokenContents.number = 0;
      break;
    case Variable:
      break;
    case Oper:
      tokenContents.oper = none;
      break;
  }
}

StateDatabase::ExpressionToken::ExpressionToken(Type _tokenType, Contents _tokenContents)
{
  tokenType = _tokenType;
  tokenContents = _tokenContents;
}

void StateDatabase::ExpressionToken::setType(Type _tokenType)
{
  tokenType = _tokenType;
  switch (tokenType) {
    case Number:
      tokenContents.number = 0;
      break;
    case Variable:
      break;
    case Oper:
      tokenContents.oper = none;
      break;
  }
}

void StateDatabase::ExpressionToken::setContents(Contents _tokenContents)
{
  tokenContents = _tokenContents;
}

void StateDatabase::ExpressionToken::setNumber(double num)
{
  tokenType = Number;
  tokenContents.number = num;
}

void StateDatabase::ExpressionToken::setVariable(std::string var)
{
  tokenType = Variable;
  tokenContents.variable = var;
}

void StateDatabase::ExpressionToken::setOper(Operator op)
{
  tokenType = Oper;
  tokenContents.oper = op;
}

StateDatabase::ExpressionToken::Type StateDatabase::ExpressionToken::getTokenType() const
{
  return tokenType;
}

StateDatabase::ExpressionToken::Contents StateDatabase::ExpressionToken::getTokenContents() const
{
  return tokenContents;
}

double StateDatabase::ExpressionToken::getNumber() const
{
  // note that the necessary type check must be done first
  return tokenContents.number;
}

std::string	StateDatabase::ExpressionToken::getVariable() const
{
  // note that the necessary type check must be done first
  return tokenContents.variable;
}

StateDatabase::ExpressionToken::Operator StateDatabase::ExpressionToken::getOperator() const
{
  // note that the necessary type check must be done first
  return tokenContents.oper;
}

int StateDatabase::ExpressionToken::getPrecedence() const
{
  switch (tokenContents.oper) {
    case add:
    case subtract:
      return 1;
    case multiply:
    case divide:
      return 2;
    case power:
      return 3;
    case lparen:
      return 4;
    case rparen:
    default:
      return 0;
  }
}

std::istream&operator >> (std::istream& src, StateDatabase::ExpressionToken& dst)
{
  char temp;
  std::string tempname;

  src >> temp;
  if ((temp >= '0' && temp <= '9') || temp == '.') {
    // number
    tempname += temp;
    temp = src.peek();
    while ((temp >= '0' && temp <= '9') || temp == '.') {
      src >> temp;
      tempname += temp;
      temp = src.peek();
    }
    dst.setNumber(atof(tempname.c_str()));
  } else if (temp == '+' || temp == '-' || temp == '*' || temp == '/' || temp == '^' || temp == '(' || temp == ')') {
    // operator
    switch (temp) {
      case '+':
	dst.setOper(StateDatabase::ExpressionToken::add);
	break;
      case '-':
	dst.setOper(StateDatabase::ExpressionToken::subtract);
	break;
      case '*':
	dst.setOper(StateDatabase::ExpressionToken::multiply);
	break;
      case '/':
	dst.setOper(StateDatabase::ExpressionToken::divide);
	break;
      case '^':
	dst.setOper(StateDatabase::ExpressionToken::power);
	break;
      case '(':
	dst.setOper(StateDatabase::ExpressionToken::lparen);
	break;
      case ')':
	dst.setOper(StateDatabase::ExpressionToken::rparen);
	break;
    }
  } else if ((temp >= 'a' && temp <= 'z') || (temp >= 'A' && temp <= 'Z') || temp == '_') {
    // variable (perhaps prefix with $?)
    tempname += temp;
    temp = src.peek();
    while ((temp >= 'a' && temp <= 'z') || (temp >= 'A' && temp <= 'Z') || temp == '_') {
      src >> temp;
      tempname += temp;
      temp = src.peek();
    }
    dst.setVariable(tempname);
  } else {
    // throw an error?
  }
  return src;
}

std::string& operator >> (std::string& src, StateDatabase::ExpressionToken& dst)
{
  char temp;
  std::string tempname;

  if (src.size() == 0)
    return src;

  temp = src[0]; src = src.substr(1);
  if ((temp >= '0' && temp <= '9') || temp == '.') {
    // number
    tempname += temp;
    if (!src.empty())
      temp = src[0];
    while (((temp >= '0' && temp <= '9') || temp == '.') && (src.length() != 0)) {
      src = src.substr(1);
      tempname += temp;
      if (!src.empty())
	temp = src[0];
    }
    dst.setNumber(atof(tempname.c_str()));
  } else if (temp == '+' || temp == '-' || temp == '*' || temp == '/' || temp == '^' || temp == '(' || temp == ')') {
    // operator
    switch (temp) {
      case '+':
	dst.setOper(StateDatabase::ExpressionToken::add);
	break;
      case '-':
	dst.setOper(StateDatabase::ExpressionToken::subtract);
	break;
      case '*':
	dst.setOper(StateDatabase::ExpressionToken::multiply);
	break;
      case '/':
	dst.setOper(StateDatabase::ExpressionToken::divide);
	break;
      case '^':
	dst.setOper(StateDatabase::ExpressionToken::power);
	break;
      case '(':
	dst.setOper(StateDatabase::ExpressionToken::lparen);
	break;
      case ')':
	dst.setOper(StateDatabase::ExpressionToken::rparen);
	break;
    }
  } else if ((temp >= 'a' && temp <= 'z') || (temp >= 'A' && temp <= 'Z') || temp == '_') {
    tempname += temp;
    if (!src.empty())
      temp = src[0];
    while (((temp >= 'a' && temp <= 'z') || (temp >= 'A' && temp <= 'Z') || temp == '_') && (src.length() != 0)) {
      src = src.substr(1);
      tempname += temp;
      if (!src.empty())
	temp = src[0];
    }
    dst.setVariable(tempname);
  } else {
    // throw an error?
  }
  return src;
}

std::ostream& operator << (std::ostream& dst, const StateDatabase::ExpressionToken& src)
{
  switch (src.getTokenType()) {
    case StateDatabase::ExpressionToken::Number:
      dst << src.getNumber();
      break;
    case StateDatabase::ExpressionToken::Oper:
      switch (src.getOperator()) {
	case StateDatabase::ExpressionToken::add:
	  dst << '+';
	  break;
	case StateDatabase::ExpressionToken::subtract:
	  dst << '-';
	  break;
	case StateDatabase::ExpressionToken::multiply:
	  dst << '*';
	  break;
	case StateDatabase::ExpressionToken::divide:
	  dst << '/';
	  break;
	case StateDatabase::ExpressionToken::power:
	  dst << '^';
	  break;
	case StateDatabase::ExpressionToken::lparen:
	  dst << '(';
	  break;
	case StateDatabase::ExpressionToken::rparen:
	  dst << ')';
	  break;
	case StateDatabase::ExpressionToken::none:
	  break;
      }
      break;
    case StateDatabase::ExpressionToken::Variable:
      dst << src.getVariable();
      break;
  }
  return dst;
}

std::istream& operator >> (std::istream& src, StateDatabase::Expression& dst)
{
  StateDatabase::ExpressionToken temp;
  char tempc;

  dst.clear();
  src.unsetf(std::ios::skipws);
  while (src.peek() != '\n') {
    while (src.peek() == ' ' || src.peek() == '\t')
      src >> tempc;
    src >> temp;
    dst.push_back(temp);
  }
  src >> tempc;
  src.setf(std::ios::skipws);
  return src;
}

std::string& operator >> (std::string& src, StateDatabase::Expression& dst)
{
  StateDatabase::ExpressionToken temp;

  dst.clear();
  while (src.length() != 0) {
    while (src[0] == ' ' || src[0] == '\t') {
      src = src.substr(1);
    }
    if (src.length() != 0) {
      src >> temp;
      dst.push_back(temp);
    }
  }
  return src;
}

std::ostream& operator << (std::ostream& dst, const StateDatabase::Expression& src)
{
  if (src.size()) {
    for (unsigned int i = 0; i < src.size() - 1; i++) {
      dst << src[i] << ' ';
    }
    dst << src[src.size() - 1];
  }
  return dst;
}

StateDatabase::Expression StateDatabase::infixToPrefix(const Expression &infix)
{
  Expression postfix, prefix;
  std::stack<ExpressionToken> operators;

  for (Expression::const_iterator i = infix.begin(); i != infix.end(); ++i) {
    if (i->getTokenType() == ExpressionToken::Variable || i->getTokenType() == ExpressionToken::Number) {
      postfix.push_back(*i);
    } else if (i->getTokenType() == ExpressionToken::Oper) {
      if (i->getOperator() == ExpressionToken::lparen) {
	operators.push(*i);
      } else if (i->getOperator() == ExpressionToken::rparen) {
	// unstack operators until a matching ( is found
	while (!operators.empty() && (operators.top().getOperator() != ExpressionToken::lparen)) {
	  postfix.push_back(operators.top()); operators.pop();
	}
	// discard (
	if (!operators.empty()) // handle extra-rparen case
	  operators.pop();
      } else {
	while (!operators.empty() && (operators.top().getPrecedence() < i->getPrecedence()) && (operators.top().getOperator() != ExpressionToken::lparen)) {
	  postfix.push_back(operators.top()); operators.pop();
	}
	operators.push(*i);
      }
    }
  }
  while (!operators.empty()) {
    postfix.push_back(operators.top()); operators.pop();
  }

  for (Expression::reverse_iterator ri = postfix.rbegin(); ri != postfix.rend(); ++ri)
    prefix.push_back(*ri);
  return prefix;
}

float StateDatabase::evaluate(Expression e) const
{
  std::stack<ExpressionToken> evaluationStack;
  ExpressionToken tok, lvalue, rvalue;
  bool unary;

  for (Expression::reverse_iterator i = e.rbegin(); i != e.rend(); ++i) {
    unary = false;
    switch (i->getTokenType()) {
      case ExpressionToken::Number:
	evaluationStack.push(*i);
	break;
      case ExpressionToken::Variable:
	// strip off '$'?
	tok.setNumber(BZDB.eval(i->getVariable()));
	evaluationStack.push(tok);
	break;
      case ExpressionToken::Oper:
	if ((i->getOperator() == ExpressionToken::lparen) ||
	    (i->getOperator() == ExpressionToken::rparen)) {
	    break;  // should not have any parens here, skip them
	}
	if (evaluationStack.empty()) {
	  // syntax error
	  // ugly hack, since gcc 2.95 doesn't have <limits>
	  float NaN;
	  memset(&NaN, 0xff, sizeof(NaN));
	  return NaN;
	}
	// rvalue and lvalue are switched, since we're reversed
	rvalue = evaluationStack.top(); evaluationStack.pop();
	if (evaluationStack.empty()) {
	  unary = true; // syntax error or unary operator
	}
	if (!unary) {
	  lvalue = evaluationStack.top(); evaluationStack.pop();
	}
	switch (i->getOperator()) {
	  case ExpressionToken::add:
	    tok.setNumber(lvalue.getNumber() + rvalue.getNumber());
	    evaluationStack.push(tok);
	    break;
	  case ExpressionToken::subtract:
	    if (unary)
	      tok.setNumber(-rvalue.getNumber());
	    else
	      tok.setNumber(lvalue.getNumber() - rvalue.getNumber());
	    evaluationStack.push(tok);
	    break;
	  case ExpressionToken::multiply:
	    tok.setNumber(lvalue.getNumber() * rvalue.getNumber());
	    evaluationStack.push(tok);
	    break;
	  case ExpressionToken::divide:
	    tok.setNumber(lvalue.getNumber() / rvalue.getNumber());
	    evaluationStack.push(tok);
	    break;
	  case ExpressionToken::power:
	    tok.setNumber(pow(lvalue.getNumber(), rvalue.getNumber()));
	    evaluationStack.push(tok);
	    break;
	default:
	  // lparen and rparen should have been stripped out
	  // throw something here, too
	  break;
	}
	break;
    }
  }
  if (evaluationStack.empty())
    return 0; // yeah we are screwed. TODO, don't let us get this far

  return (float)evaluationStack.top().getNumber();
}


// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
