/*************************************************************************/
/*                                                                       */
/*                                                                       */
/*  File      : Value.hpp                                                */
/*  Author    : Volker H. Simonis  (simonis@informatik.uni-tuebingen.de) */
/*  Copyright : Volker H. Simonis  (simonis@informatik.uni-tuebingen.de) */
/*  Date      : Wed Oct 21 12:11:50 MEST 1998                            */
/*              Sat Jan 23 01:17:39 CET 1999                             */
/*                                                                       */
/*                                                                       */
/*************************************************************************/

#include <string>
#include <iostream>
#include <map>
#include <hash_map>
#include <typeinfo>

using namespace std;

namespace wsi {

class Value;

class Incompatible_Type_Exception {
private:
  string errorType;
public:
  Incompatible_Type_Exception(const string& s) { errorType = s; }
  string getError() const { return errorType; }
};

struct VTable {
  const type_info* myType;
  typedef void(Value::*FuncPointer)(void);
  FuncPointer fp_DELETE;
  typedef void(Value::*ClonePointer)(Value*) const;
  ClonePointer fp_CLONE;
  typedef ostream&(Value::*PrintPointer)(ostream&) const;
  PrintPointer fp_PRINT;
  VTable(const type_info* ti, FuncPointer fP, ClonePointer cP, PrintPointer pP)
    : myType(ti), fp_DELETE(fP), fp_CLONE(cP), fp_PRINT(pP) {}
};

template<class T> 
struct Spec_VTable : public VTable {
  Spec_VTable() : VTable(&typeid(T), 
		  &(Value::template deleteValue<T>),
		  &(Value::template cloneValue<T>), 
		  &(Value::template printValue<T>)) {}
};
 
class Value {
template<class T> friend class Spec_VTable;
private:
  VTable *vtable;
  enum Action { SET, GET, DELETE };
  template <class T> T& value(T t = T(), Action action = GET) 
    throw (Incompatible_Type_Exception&);
  template <class T> void deleteValue();
  template <class T> void cloneValue(Value*) const;
  template <class T> ostream& printValue(ostream&) const;
public:
  Value();                            // Default constructor 
  Value(const Value&);                // Copy constructor
  template <class T> Value(const T&); // Generic constructor
  ~Value() { if (vtable != NULL) (this->*(vtable->fp_DELETE))(); }

  const type_info& typeId() const throw (bad_typeid&);
  template <class T> void setValue(const T &t) { value(t, SET); }
  template <class T> T& getValue() const throw (Incompatible_Type_Exception&){ 
    return const_cast<Value*>(this)->template value<T>();
  }
  template <class T> operator T() const throw (Incompatible_Type_Exception&) { 
    return const_cast<Value*>(this)->template value<T>(); // const_cast is safe
  }
  //template <class T> operator T&() throw (Incompatible_Type_Exception&) { 
  //  return value<T>();
  //}
  template <class T> T& operator=(const T &t) { return value(t, SET); }
  template <class T> T* operator&() { return &value<T>(); }
  Value& operator=(const Value&);

  friend ostream& operator<<(ostream&, const Value&);
};

ostream& operator<<(ostream& os, const Value& e) {
  if (e.vtable) return (e.*e.vtable->fp_PRINT)(os);
  else return (os << "nil");
}

// Default constructor 
inline Value::Value() : vtable(NULL) { 
#ifdef DEBUG
    cerr << "Value::Value() (" << this << ")\n"; 
#endif
}

// Copy constructor
inline Value::Value(const Value &val) : vtable(NULL) {
#ifdef DEBUG
  cerr << "Assigning Value(" << &val.fp_CLONE << "::" << &val 
       << ") to Value(" << &this->fp_CLONE << "::" << this << ")\n";
#endif
  if (val.vtable != NULL) {
    (val.*val.vtable->fp_CLONE)(this);
  }
}

// Generic constructor
template <class T> 
inline Value::Value(const T &t) : vtable(NULL) {
#ifdef DEBUG
  cerr << "Value::Value(" << typeid(t).name() << ") (" << this << ")\n";
#endif
  setValue(t);
}

const type_info& Value::typeId() const throw (bad_typeid&) { 
  if (vtable) return *(vtable->myType);
  else throw bad_typeid();
}

template <class T> 
inline void Value::deleteValue() { 
#ifdef DEBUG
  cerr << "Deleteing Value for type " << typeid(T).name() << " (" << this 
       << ")\n";
#endif
  T t = T();
  value(t, DELETE);
}

template <class T> 
inline void Value::cloneValue(Value *val) const { 
#ifdef DEBUG
  cerr << "Cloning for type " << typeid(T).name() << " (" << this << ")\n";
#endif
  // const_cast<> is safe here, because we implicitly call only 'value(T, GET)'
  val->template value<T>(const_cast<Value*>(this)->template value<T>(), SET);
  // Alternative 2 :
  // val->value<T>(const_cast<Value*>(this)->value<T>(), SET); // StrStr C.13.6
  // Alternative 3 :
  //  T t = T();
  //  val->value(const_cast<Value*>(this)->value(t, GET), SET);
}

template <class T> 
ostream& Value::printValue(ostream& os) const {
  return os << const_cast<Value*>(this)->template value<T>();
  //  T t = T();
  //  return os << const_cast<Value*>(this)->value(t, GET);
}

inline Value& Value::operator=(const Value &val) { 
#ifdef DEBUG
  cerr << "Assigning Value(" << &val.fp_CLONE << "::" << &val 
       << ") to Value(" << &this->fp_CLONE << "::" << this << ")\n";
#endif
  if (this != &val) {
    if (val.vtable != NULL) {
      (val.*val.vtable->fp_CLONE)(this);
      // this syntax means : dereference the member function pointer fp_CLONE
      //                     of object 'val', for the object 'val' itself.
      // ATTENTION !!! 'val.*fp_CLONE' means dereference fp_CLONE of 'this' for
      //                     the object 'val', what in general will be false,
      //                     since fp_CLONE of 'this' can hold another type.
    }
    else {  // if we were assigned a bare Value object
      if (vtable) (this->*(vtable->fp_DELETE))();
      vtable = NULL;
    }
  }
  return *this; 
}

struct ValueHash {
  inline size_t operator()(Value *v) const { return (long)v; }
};

template <class T> inline 
T& Value::value(T t, Action action) throw (Incompatible_Type_Exception&) { 
  //static map<Value*, T > values;
  static Spec_VTable<T> s_vtable;
  static hash_map<Value*, T, ValueHash > values(1000000);
  static T ret;           // only to avoid compiler warnings about returning a 
  switch(action) {        //                     reference to a local variable
    case SET :  { 
      values[this] = t;
#ifdef DEBUG
      cerr << "Assigned type " << typeid(T).name() << " to fp_CLONE ("
	   << &fp_CLONE << "::" << this << ")\n";
#endif
      if (vtable != NULL && vtable != &s_vtable) (this->*vtable->fp_DELETE)();
      vtable = &s_vtable;
      return ret;
    }
    case DELETE : { // only called by destructor
      if (values.count(this)) values.erase(this); 
      return ret; 
    } 
    case GET : { 
      if (values.count(this)) return values[this]; 
      else throw Incompatible_Type_Exception(typeid(T).name());
    }
  }
  return ret; // only to avoid compiler warnings
}

} // namespace wsi

