#ifndef _SYMBOL_HH
#define _SYMBOL_HH

#include <ext/hash_map>
#include <string>
#include <cstring>
#include <fstream>
#include <vector>
#include <iostream>

using namespace std;

using __gnu_cxx::hash_map;
using __gnu_cxx::hash;


// Key comparison for the cstr_hash hash table
struct eqstr
{ 
  bool operator()(const char * s, const char* t) const 
  { return strcmp(s,t)==0; }
};


// Hash table for storing symbols

typedef hash_map<const char*,int,hash<const char*>,eqstr> cstr_hash;

// Symbol table. Provides access to symbols through their index or name.

class Symbols
{
 public:

  Symbols() { add("NULL"); };
  ~Symbols();
  
  void load(const char* filename);

  int operator[](const char* s) { return hash[s]; };
  
  const char* operator[](int i) { return table[i]; };

  void add(const char* c);

  int count() { return table.size(); };

 private:
    
  std::vector<const char*> table;
  cstr_hash hash;

};

//////////////////////////////////////////////////////////////////////

/// Symbol class template. 
/** The template argument determines the symbol space.
    Each space is created with symbol "NULL" with index 0 already in.
*/

template <int space>
class Symbol
{
 public:

  /// Load the contents of the symbol table from file.
  static void define(const char *filename) 
  { defs.load(filename); }
  
  /// Add symbol s.
  /** The string is duplicated.
   */
  static Symbol<space> add(const char* s) { defs.add(s); }
      
  /// Number of symbols.
  static int count() { return defs.count(); };

  /// First symbol.
  static int first() { return 1; }

  /// Last symbol.
  static int last() { return defs.count()+1; }

  /// Last symbol.
  static int index(const char* s) { return defs[s]; }

  /// Just for tests.
  static void print();

  /// 0-argument constructor, default value is 0 ("NULL").
  Symbol() : val(0) {};

  /// Constructing a symbol from its index.
  /** No check is performed.
  */

  Symbol(int v) : val(v) {};
  
  /// Constructing a symbol from its name (string to Symbol conversion).
  /** If s is not a symbol name, the value of 0 ("NULL") is assigned.
  */

  Symbol(const char * s) : val(defs[s]) {};

  Symbol(string s) : val(defs[(char*)s]) {};


  bool empty() const { return val==0; }

  /// Symbol to char* conversion. If symbol is invalid, NULL is returned.
  const char* str() const { return (val>=0 && val<count())?defs[val]:NULL; };

  /// Symbol to int& conversion.
  /** Provides a way to iterate through symbols, eg:
   *  for(Symbol<0> s=1; s; s++ ) ...
      s=0; while(++s) ...
   */

  (operator int)() const { return val; };

  Symbol operator++() {val++; return *this;}

  //  bool operator<(Symbol& s) { return val < s.val; }


 private:
  static Symbols defs;
  int val;
};

template <int space>
void Symbol<space>::print()
{
  for(Symbol i=0; i<count(); ++i)
    cout << (int)i << ": " << (const char*)i << endl;
}

template<int space>
Symbols Symbol<space>::defs;

template<int space>
bool operator<(const Symbol<space>& s, const Symbol<space>& t)
{
  return (int)s < (int)t;
}

#endif
