#ifndef STABLE_H

#define STABLE_H

// This class implements a 'stringtable'.  It is being released into
// the public domain on September 27, 2000 by John W. Ratcliff
// jratcliff@verant.com for FlipCode.com as part of the 'code of they day'
// project.
//
// What the heck does a 'StringTable' class do for me?  What a StringTable
// is used for is to manage a collection of shared strings across a package.
// It is primarily used for tokens, keywords, asset names, etc.  You do
// *NOT* want to use the StringTable for any cases which could produce
// an infinite number of strings.  This is only for managing a finite list
// of keywords.
//
// The reason you use a string table is so that you can keep track of items
// by ASCII name instead of some non-intutitive and arbritrary id number.
// Normally it is not practical to make a container class who's key is
// an STL string.  First of all there is a memory overhead, and additionally
// there is a serious peformance penalty.  However, with the StringTable
// class, instead you can keep track of a list of items where the key is
// a pointer to the string, instead of the string itself.  Now you can
// compare if 'two strings are equal' simply by comparing if their address
// is the same.  Remember that any container class which uses this pointer as
// a key is *NOT* in alphabetical order!!  It's ordered based on the address
// in memory of the string itself.  But that doesn't matter, because the
// purpose of the container is not to maintain an alphabetical list of
// items but rather to simply locate items *BY NAME* extremely quickly.
//
// Using a stringtable makes it trivial to keep ASCII names and descriptions
// as member variables in all of your classes.  This makes debugging and
// debug output vastly simpler.
//
// Example: Say you contruct a new intance of a Vehicle class.  In it's
// constructor you might have something like:
//
// mObjectName = gStringTable.Get("vehicle");
//
// Where mObjectName is a member variable of type 'const char *' and
// gStringTable is some global instance of a shared string table across
// your application.  Now, you can compare whether or not an object is of
// type 'vehicle' simply by comparing it's name to anybody else's name
// with the two pointers allocated with the same string table.
//
// This is extremely convenient for debugging, logging, etc.
//
// To create an STL map where you access every item *by name* all you
// have to do is:
//
// typedef std::map< const char * , Texture *> TextureMap;
//
// This would create an STL map that would allow you to quickly look
// up any texture by name.  A fairly useful data structure.
//
// Example of how to locate a texture by name:
//
// **NOTE* texname must have been created by the same StringTable
//
// Texture * FindTexture(const char *texname)
// {
//   TextureMap::iterator found; // STL map iterator to find item.
//   found = mTextures.find( texname ); // search red/black tree
//   if ( found != mTextures.end() ) return (*found).second;
//   return 0; // no texture of this name found.
// }
//
// The string table implementation uses STL to maintain the collection
// of strings.  If you change the comparator function in CharPtrLess
// from strcmp to stricmp then all of your strings will be created without
// case sensitivity, which is often the behavior you want for tokens and
// keywords.

#include <string.h>
#include <string>
#include <set>

typedef std::string String;

class CharPtrLess
{
public:
	bool operator()(const char *v1,const char *v2) const
  {
    int v = strcmp(v1,v2);
    if ( v < 0 ) return true;
    return false;
  };
};

typedef std::set< const char *, CharPtrLess > CharPtrSet;

class StringTable
{
public:
  StringTable(void)
  {
  };

  ~StringTable(void)
  {
    CharPtrSet::iterator i;
    for (i=mStrings.begin(); i!=mStrings.end(); i++)
    {
      char *str = (char *)(*i);
      delete str;
    }
  }

  const char * Get(const char *str)
  {
    CharPtrSet::iterator found;
    found = mStrings.find( str );
    if ( found != mStrings.end() ) return (*found);
    int l = strlen(str);
    char *mem = new char[l+1];
    strcpy(mem,str);
    mStrings.insert( mem );
    return mem;
  };

  const char * Get(const String &str)
  {
    return Get( str.c_str() );
  };

private:
  CharPtrSet mStrings;
};

#endif
