// This may look like C code, but it is really -*- C++ -*-
/*
 ************************************************************************
 *
 *				Vocabulary service
 *
 * Multilevel/interrelated vocabularies with named slots are at the
 * center of everything
 *
 * Vocabularies are polymorphic: a vocabulary slot may contain a simple
 * data type (int, double, or a string, that is, const char *), a reference
 * to another vocabulary, or just a (sub)vocabulary. Homogeneous vocabulary
 * are those whose slots have exactly the same structure, which is reinforced
 * when new entries are added.
 * Vocabulary of vocabularies contains all full-fledged vocabularies: that
 * is, each vocabulary is inserted in some other (parent) vocabulary,
 * which in many cases is the Vocabulary of vocabularies (note, a
 * vocabulary can be referenced from many vocabulary slots).
 * A vocabulary path is a list of vocabularies to search for a named slot
 * in, in order of appearance in the path.
 *
 * Vocabularies can be referred to from within other vocabularies by
 * special vocabulary items, vocabulary references. A reference either
 * points to a vocabulary (when it's completely "resolved") or just
 * keeps the name of the referred vocabulary, which is to be looked up
 * when needed (lazy reference). The reference may also contain a file
 * name, from which the vocabulary would be loaded when it failed to
 * be located otherwise. If the file name is relative (that is, does
 * not start with '/'), it's prepended by dir names specified in
 * the special env variable VOCPATH.
 *
 * A vocabulary supports the main basic functions: lookup, insert, delete,
 * traverse. Looking up a slot (or a slot value) is the main function,
 * and exists in many varieties.
 * There is a function to look up a slot in the current dictionary, 
 * in all dictionaries in the path (the top dictionary in the path
 * is searched first, the dictionary underneath is search only if the
 * lookup in the top dictionary failed, etc).
 *
 * Path search provides something like system-wide and specific
 * environments/databases (a hierarchy of system/local environments).
 * Previous incarnation of this pacakage was so-called task_env service
 *
 * Note, vocabulary objects and their slots are non-standard C++ objects.
 * There is no public constructor/destructor available for them.
 * When one creates a vocabulary, it is always inserted into the vocabulary
 * of vocabularies, and, at the same time, put at the top of the vocabulary
 * path (which makes it the current vocabulary). When an item is created, its
 * memory is allocated dynamically, and it is inserted automatically into
 * the current vocabulary. So, an item or a vocabulary cannot exist
 * by itself, not being a part of some vocabulary. Therefore, a user
 * is never given a vocabulary or slot object themselves, only references
 * to them.
 *
 * Note, a vocabulary can contain another vocabulary (or a reference to
 * another vocabulary). Subvocabularies are usually treated differently:
 * they are not inserted into the Vocabulary of vocabularies, rather they
 * are present only in the parent vocabulary (and die with it).
 *
 * $Id: voc.h,v 1.3 1998/03/15 23:30:46 oleg Exp oleg $
 *
 ************************************************************************
 */

#ifndef __GNUC__
#pragma once
#endif

#ifndef _voc_h
#define _voc_h 1

#ifdef __GNUC__
#pragma interface
#endif

#include "myenv.h"

//------------------------------------------------------------------------
//	    Data structures that represent the vocabulary
// Note the only operation one can do on Voc Items is to create them
// through a special (non-standard) creator that always creates an
// object on heap and places an item into the current vocabulary.
// So a vocabulary item cannot exist by itself, it can only be in
// some vocabulary

#ifndef MSIPL_IOSTREAM_H
class ostream;				// Opaque i/o classes
class istream;
#endif

class Voc;				// Forward decl of the Vocabulary
class VocRefItem;			// Forward decl of the Vocabulary
class VocPathPrivate;

				// This is an abstract class that describes
				// the most generic item
class GenericVocItem
{
  friend class Voc;
  friend class VocRefItem;
  friend class VocPathPrivate;

public:
  enum Type { Int, Double, String, Vocab, VocRef, Last }; // Type of voc value
  union TypeSet				// Union of all voc types
  { int _int; double _double; const char * _str;  Voc * _voc; };

private:
  GenericVocItem * next;		// Ptr to the next element or 0

				// The following aren't implemented. But
				// since they are private, this makes any
				// (explicit or implicit) assignment impossible
  GenericVocItem(const GenericVocItem& another);
  GenericVocItem& operator=(const GenericVocItem& another);

protected:
  char name[33];			// Name of the item, should not
					// contain "=" or non-ascii chars
  const enum Type val_type;
					// Item value would follow in the
					// derived classes

				// The derived classes must redefine the
				// following functions
  virtual TypeSet get_value(void) const = 0;
  virtual void write_down(ostream& outs) const;

				// Don't allow laymen to construct the item
				// or destroy it
  GenericVocItem(const char * _name, const Type type);
  virtual ~GenericVocItem(void) { assert( next == 0 ); }

  void catalog(void);		// Add this item to the current vocabulary

				// Read in and parse val_type & name, create
				// and return an item of an appropriate
				// type
  static GenericVocItem * read_in(istream& ins);

public:
  const char * q_name(void) const	{ return name; }
  enum Type    q_type(void) const	{ return val_type; }
  virtual void print(const char * title) const;
};

				// Some elementary voc items
class IntVocItem : public GenericVocItem
{
  friend class GenericVocItem;

  int value;

  IntVocItem(const char * _name, const int _val) :
	  GenericVocItem(_name,GenericVocItem::Int), value(_val) {}
  IntVocItem(const char * _name, istream& ins); // read the value in

				// Standard interface
  virtual TypeSet get_value(void) const
  		{ TypeSet tset; tset._int = value; return tset; }
  virtual void write_down(ostream& outs) const;

  IntVocItem(const IntVocItem&);	// Private and unimplemented: cloning is
  void operator = (const IntVocItem&);	// not allowed

public:
  friend const IntVocItem & new_IntVocItem(const char * _name, const int _val)
	{ IntVocItem& item = * new IntVocItem(_name,_val); item.catalog();
	  return item; }
//  friend const IntVocItem & new_IntVocItem(const char * _name, istream& ins);
  virtual void print(const char * title) const;
};

class DoubleVocItem : public GenericVocItem
{
  friend class GenericVocItem;

  double value;

  DoubleVocItem(const char * _name, const double _val) :
	  GenericVocItem(_name,GenericVocItem::Double), value(_val) {}
  DoubleVocItem(const char * _name, istream& ins); // read the value in

				// Standard interface
  virtual TypeSet get_value(void) const
  		{ TypeSet tset; tset._double = value; return tset; }
  virtual void write_down(ostream& outs) const;

  DoubleVocItem(const DoubleVocItem&);	// Private and unimplemented: cloning is
  void operator = (const DoubleVocItem&);// not allowed

public:
  friend const DoubleVocItem & new_DoubleVocItem(const char * _name,
						 const double _val)
	{ DoubleVocItem& item = * new DoubleVocItem(_name,_val);
	  item.catalog(); return item; }
//  friend const DoubleVocItem & new_DoubleVocItem(const char * _name,
//						 istream& ins);
  virtual void print(const char * title) const;
};

				// String voc item
class StrVocItem : public GenericVocItem
{
  friend class GenericVocItem;

  const char * value;

  StrVocItem(const char * _name, const char * _val);
  StrVocItem(const char * _name, istream& ins); // read the value in
  ~StrVocItem(void);

				// Standard interface
  virtual TypeSet get_value(void) const
  		{ TypeSet tset; tset._str = value; return tset; }
  virtual void write_down(ostream& outs) const;

  StrVocItem(const StrVocItem&);	// Private and unimplemented: cloning is
  void operator = (const StrVocItem&);  // not allowed

public:
  friend const StrVocItem& new_StrVocItem(const char * _name, 
					  const char * _val)
	{ StrVocItem& item = * new StrVocItem(_name,_val); item.catalog();
	  return item; }
  const StrVocItem& new_StrVocItem(const char * _name, istream& ins);
  virtual void print(const char * title) const;
};


				// Vocabulary: a collection of VocItem's
class Voc : public GenericVocItem
{
  friend class GenericVocItem;
  friend class VocRefItem;
  friend class VocPathPrivate;
  
  GenericVocItem * first;
  GenericVocItem * last;

  const char * comment;

  void ok_to_modify(void);		// Make sure it's ok to modify; set
					// the dirty bit if so

					// if the dictionary is homogeneous
  void homogenity_checking(GenericVocItem& item);
					// and non-empty, the item being added
					// should be structurally equivalent
					// to the item on the top of the
					// dictionary


  void VocOfVoc_catalog(void);		// Add this Voc to the VocOfVoc
  void make_current(void);		// Make this vocabulary current

				// Find a slot with the specified name and
				// type. Return 0 if not found
  virtual GenericVocItem * find(const char * _name, const Type type) const;

  Voc(const Voc&);			// Private and unimplemented: cloning is
  void operator = (const Voc&);		// not allowed

protected:
  enum Option { None=0, ReadOnly = 1, Homogeneous = 2, Dirty = 8 } options;
  int check_option(const Option opt) const	{ return options & opt; }
  void set_option(const Option opt)		{ (int&)options |= opt; }
  void reset_option(const Option opt)		{ (int&)options &= ~opt; }

  Voc(const char * name, const char * comment);
  ~Voc(void);

  Voc(istream& ins);				// Load from the istream
  static Voc& load(const char * file_name);	// Load from the file (using
						// VOCPATH) & catalog

  virtual TypeSet get_value(void) const
  		{ TypeSet tset; tset._voc = (Voc *)this; return tset; }
  virtual void write_down(ostream& outs) const;

  void operator += (GenericVocItem& item);	// Add an item at the top
  GenericVocItem& remove_top(void);		// Remove the item at the top

public:
				// Create an empty dictionary and make it
				// current
  friend Voc& new_Voc(const char * _name, const char * _comment)
	{ Voc& voc = * new Voc(_name,_comment); 
	  voc.VocOfVoc_catalog(); voc.make_current(); return voc; }

				// Read in a dictionary and make it
				// current
  friend Voc& new_Voc(istream& ins)
	{ Voc& voc = * new Voc(ins);
	  voc.VocOfVoc_catalog(); voc.make_current(); return voc; }

				// Create an empty dictionary and insert it
				// into the current vocabulary
  friend Voc& new_subVoc(const char * _name, const char * _comment)
	{ Voc& voc = * new Voc(_name,_comment); 
	  voc.catalog(); return voc; }


  void info(void) const;		// Just give brief info about the voc

					// Deep vocabulary printing
  virtual void print(const char * title) const;

  friend ostream& operator << (ostream& outs, const Voc& voc)
  { voc.write_down(outs); return outs; }

  int q_empty(void) const		{ return first == 0; }
  const GenericVocItem& top(void) const;
  const GenericVocItem& bottom(void) const;
  int count(void) const;		// How many items in a dictionary


				// Retrieving functions, raise an exception if
				// the slot is not found
  const char * find_str(const char * name) const;
  int find_int(const char * name) const;
  double find_double(const char * name) const;
  Voc& find_voc(const char * name) const;

				// Retrieving functions with a default value
				// (which is returned if the slot is not found)
  const char * find_with_default(const char * name, 
				 const char * default_val) const;
  int find_with_default(const char * name, 
				 const int default_val) const;
  double find_with_default(const char * name, 
				 const double default_val) const;
  Voc& find_with_default(const char * name, 
				 Voc& default_val) const;
};

				// Voc reference item - reference to another
				// vocabulary. The vocabulary may already
				// exist, or it can be represented by its
				// file name. It would be loaded when this
				// VocRefItem gets "dereferenced"
class VocRefItem : public GenericVocItem
{
  friend class Voc;
  friend class GenericVocItem;
  friend class VocPathPrivate;

  Voc * value;
  const char * voc_file_name;

  VocRefItem(const Voc& _voc) :		// Refer to an existing vocabulary
	  GenericVocItem(_voc.q_name(),GenericVocItem::VocRef), 
	  value((Voc *)&_voc), voc_file_name(0) {}
  VocRefItem(const char * name);	// Look up the named vocabulary
					// and take its reference

					// Create a proxy
  VocRefItem(const char * name, const char * file_name);

  ~VocRefItem(void);

				// Standard interface
  virtual TypeSet get_value(void) const;	// Loads when necessary
  virtual void write_down(ostream& outs) const;

  VocRefItem(const VocRefItem&);	// Private and unimplemented: cloning is
  void operator = (const VocRefItem&);	// not allowed

public:
  friend const VocRefItem& new_VocRefItem(Voc& _val)
	{ VocRefItem& item = * new VocRefItem(_val); item.catalog();
	  return item; }
  friend const VocRefItem& new_VocRefItem(const char * voc_name)
	{ VocRefItem& item = * new VocRefItem(voc_name); item.catalog();
	  return item; }
  friend const VocRefItem& new_VocProxy(const char * voc_name,
					const char * voc_file_name)
	{ VocRefItem& item = * new VocRefItem(voc_name,voc_file_name);
	  item.catalog(); return item; }
  virtual void print(const char * title) const;
};

void print_all_vocabularies(void);

extern Voc& Dummy_Voc;

class VocPathPrivate;
				// Path of current vocabularies
				// Note the current class provides only access
				// to the path, the path (data) are allocated
				// beforehand and stay this way
class VocPath
{
  friend class VocPathPrivate;

public:
  static void print(void);

				// Retrieving functions, raise an exception if
				// the slot is not found
  static const char * find_str(const char * name);
  static int find_int(const char * name);
  static double find_double(const char * name);
  static Voc& find_voc(const char * name);

				// Retrieving functions with a default value
				// (which is returned if the slot is not found)
  static const char * find_with_default(const char * name, 
				 const char * default_val);
  static int find_with_default(const char * name, 
				 const int default_val);
  static double find_with_default(const char * name, 
				 const double default_val);
  static Voc& find_with_default(const char * name, 
				 Voc& default_val);

				// Find a vocabulary with a specified name
  static void push(const char * name);
				// (in all the vocab in the path) and put it
				// at the top of the stack
  static void pop(void);	// Remove the top (current) vocabulary

};

			// Mark the current state of the VocPath stack
class VocPathMark
{
  const int vocpath_count;
public:
  void back_off(void) const;		// Back off to the marked state of VPath
  VocPathMark(void);			// Note the current state of VocPath
  ~VocPathMark(void)	 { back_off(); }
};

#endif
