///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Copyright (C) 2006 by Intel Coproration and Carnegie Mellon University    //
// Contacts: casey.j.helfrich @ intel.com                                    //
//           bdr @ cs.cmu.edu                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifndef __MONITORED_VALUE_HXX
#define __MONITORED_VALUE_HXX

#include <stdint.h>

#include <iostream>
#include <string>

using namespace std;

void dprdebug_event(string name, uint64_t value, uint8_t event);
uint64_t dprdebug_value(uint64_t value, void* object, string type, uint64_t derived1, uint64_t derived2);

typedef enum {
  dprdebug_CONSTRUCTION = 0,
  dprdebug_DESTRUCTION = 1,
  dprdebug_CASTTOPRIM = 2,
  dprdebug_VALCHANGED = 3
} dprdebug_event_t;

template <class T>
class monitored_value {
public:
  monitored_value(const monitored_value<T> &otherVal);
  monitored_value(string typeStr, string nameStr);
  monitored_value(string typeStr, string nameStr, T val, uint64_t derived1=0, uint64_t derived2=0);
  ~monitored_value();
  operator T() const;

  //protected:
  string type;
  string name;

  T wrappedValue;
  uint64_t currentValueID;

  void dprdebug_logValue(uint64_t derived1=0, uint64_t derived2=0);
  void dprdebug_logEvent(dprdebug_event_t event) const;

  monitored_value<T>& operator++();    // Prefix increment
  monitored_value<T> operator++(int); // Postfix increment
  monitored_value<T>& operator--();    // Prefix decrement
  monitored_value<T> operator--(int); // Postfix decrement
  monitored_value<T>& operator=(const monitored_value<T> &valB); // assignment
};

// Creation from default
template <class T>
monitored_value<T>::monitored_value(string typeStr, string nameStr) :
  type(typeStr), name(nameStr),
  wrappedValue(), currentValueID(0) {
  dprdebug_logValue(0,0);
  dprdebug_logEvent(dprdebug_CONSTRUCTION);
}

// Creation from a raw value
template <class T>
monitored_value<T>::monitored_value(string typeStr, string nameStr, T val, uint64_t derived1/*=0*/, uint64_t derived2/*=0*/) :
  type(typeStr), name(nameStr),
  wrappedValue(val), currentValueID(0) {
  dprdebug_logValue(derived1, derived2);
  dprdebug_logEvent(dprdebug_CONSTRUCTION);
}

// Creation from copy
template <class T>
monitored_value<T>::monitored_value(const monitored_value<T> &otherVal) :
  type(otherVal.type), name("copy("+otherVal.name+")"),
  wrappedValue(otherVal.wrappedValue), currentValueID(0) {
  dprdebug_logValue(otherVal.currentValueID, 0);
  dprdebug_logEvent(dprdebug_CONSTRUCTION);
}

template <class T>
monitored_value<T>::~monitored_value() {
  dprdebug_logEvent(dprdebug_DESTRUCTION);
}

template <class T>
monitored_value<T>::operator T() const {
  dprdebug_logEvent(dprdebug_CASTTOPRIM);
  return wrappedValue;
}

template <class T>
void monitored_value<T>::dprdebug_logValue(uint64_t derived1/*=0*/, uint64_t derived2/*=0*/) {
  /*
  worldPtr->oStart();
  cerr << "Value: { "
       << "id=" << nextAvailableValueID << ", "
       << "objectID=" << this << ", "
       << "type=" << dprdebug_typeName() << ", "
       << "data=" << wrappedValue << ", "
       << "previousValue=" << currentValueID
       << " }\n";
  worldPtr->oEnd();
  */
  currentValueID = dprdebug_value(wrappedValue, this, type, derived1, derived2);
}

template <class T>
void monitored_value<T>::dprdebug_logEvent(dprdebug_event_t event) const {
  dprdebug_event(name, currentValueID, event);
  /*worldPtr->oStart();
  cerr << "Event: { " 
       << "value=" << currentValueID << ", "
       << "tick=" << worldPtr->current_time << ", "
       << "signpost=" << currentSignpost() << ", "
       << "operation=" << event
       << "  }\n";
       worldPtr->oEnd();*/
}

// Operator implementations 

template<class T>
monitored_value<T>& monitored_value<T>::operator++() {
  // Prefix increment
  ++wrappedValue;
  dprdebug_logValue(currentValueID);
  dprdebug_logEvent(dprdebug_VALCHANGED);

  return *this;
}

template<class T>
monitored_value<T> monitored_value<T>::operator++(int) {
  // Postfix increment
  monitored_value<T> temp = *this;
  wrappedValue++;
  dprdebug_logValue(currentValueID);
  dprdebug_logEvent(dprdebug_VALCHANGED);

  return temp;
}

template<class T>
monitored_value<T>& monitored_value<T>::operator--() {
  // Prefix decrement
  --wrappedValue;
  dprdebug_logValue(currentValueID);
  dprdebug_logEvent(dprdebug_VALCHANGED);

  return *this;
}

template <class T>
monitored_value<T> monitored_value<T>::operator--(int) {
  // Postfix decrement
  monitored_value<T> temp = *this;
  wrappedValue--;
  dprdebug_logValue(currentValueID);
  dprdebug_logEvent(dprdebug_VALCHANGED);

  return temp;
}

template<class T>
monitored_value<T>& monitored_value<T>::operator=(const monitored_value<T> &valB) {
  wrappedValue = valB.wrappedValue;
  dprdebug_logValue(valB.currentValueID);
  dprdebug_logEvent(dprdebug_VALCHANGED);

  return *this;
}

template<class T, class U>
bool operator==(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue == valB.wrappedValue;
}
template<class T, class U>
bool operator!=(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue != valB.wrappedValue;
}
template<class T, class U>
bool operator<(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue < valB.wrappedValue;
}
template<class T, class U>
bool operator<=(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue <= valB.wrappedValue;
}
template<class T, class U>
bool operator>(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue > valB.wrappedValue;
}
template<class T, class U>
bool operator>=(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return valA.wrappedValue >= valB.wrappedValue;
}

template<class T, class U>
monitored_value<T> operator+(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return monitored_value<T>(valA.type, "(" + valA.name + "+" + valB.name + ")", valA.wrappedValue+valB.wrappedValue, valA.currentValueID, valB.currentValueID);
}
template<class T, class U>
monitored_value<T> operator-(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return monitored_value<T>(valA.type, "(" + valA.name + "-" + valB.name + ")", valA.wrappedValue-valB.wrappedValue, valA.currentValueID, valB.currentValueID);
}
template<class T, class U>
monitored_value<T> operator*(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return monitored_value<T>(valA.type, "(" + valA.name + "*" + valB.name + ")", valA.wrappedValue*valB.wrappedValue, valA.currentValueID, valB.currentValueID);
}
template<class T, class U>
monitored_value<T> operator/(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return monitored_value<T>(valA.type, "(" + valA.name + "/" + valB.name + ")", valA.wrappedValue/valB.wrappedValue, valA.currentValueID, valB.currentValueID);
}
template<class T, class U>
monitored_value<T> operator%(const monitored_value<T> &valA, const monitored_value<U> &valB) {
  return monitored_value<T>(valA.type, "(" + valA.name + "%" + valB.name + ")", valA.wrappedValue%valB.wrappedValue, valA.currentValueID, valB.currentValueID);
}

#endif
