#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <string>
#include <list>
#include <vector>
using namespace std;

class Place;
class Transition;
class Net;

/**
 * This file is a demonstration of the use and possible implementation
 * of change nets.
 */

class Place
{
  // all the listeners to state changes on this place
  vector<Transition*> output_transitions;
public:
  Place() : output_transitions() {};
  void update_to(Transition*t) {output_transitions.push_back(t);};
  // will update all the output transitions 'changed' state
  void updated();
};

class Transition
{
  bool input_place_changed;
public:
  // will create a transition and add it to the global net
  Transition();
  // will mark this transition as 'possible' executable
  void set_input_place_changed() {input_place_changed=true;};
  // will inform this transition to react on changes at place
  void react_on(Place*p) {p->update_to(this);};
  void react_on(Place&p) {p.update_to(this);};
  // will check the in-state and run if necessary
  void execute();
  virtual void run() = 0;
};

void Place::updated()
{
  for(int i = 0 ; i < output_transitions.size(); i++)
    output_transitions[i]->set_input_place_changed();
};

void Transition::execute()
{
  if (!input_place_changed) return;
  input_place_changed = false;
  run();
};

class Net
{
  vector<Transition*> transitions;
public:
  static Net global;
  void execute()
  {
    while(true)
      for(int i = 0 ; i < transitions.size(); i++)
	transitions[i]->execute();
  };
  void add(Transition & t) {transitions.push_back(&t);};
  void add(Transition * t) {transitions.push_back(t);};
};


Transition::Transition() 
{
  Net::global.add(this);
};

// Convenience vars
template <class T> class var: public Place
{
   T value;
 public:
   var<T>() : Place() {};
   var<T>(T init) : Place(), value(init) {};
   void update_to(var<T> & other);
   operator const T&(void) {return value;};
   operator const T&(void) const {return value;};
   var<T>& operator= (const var<T>& f);
   var<T>& operator= (const T& f);
};

template <class T> var<T>& var<T>::operator= (const var<T>& f)
{
   if (&f==this) return *this;
   if (value==f.value) return *this;
   value=f;
   cout << value << "\n";
   updated();
   return * this;
}

template <class T> var<T>& var<T>::operator= (const T& f)
{
   if (value==f) return *this;
   value=f;
   cout << value << "\n";
   updated();
   return *this;
}

template <class T> class PhantomTransition: public Transition
{
 public:
   var<T> &from;
   var<T> &to;
   PhantomTransition(var<T> & f, var<T> & t) : Transition(), from(f), to(t) {react_on(f);};
   virtual void run() {to=from;};
};

template <class T> void var<T>::update_to(var<T> & other)
{
   new PhantomTransition<T>(*this,other);
};

// Convenience transition
template <class H> class MemberTransition: public Transition
{
 public:
   typedef void (H::*member)();
   member F;
   H& o;
   MemberTransition(H &ob, member i) : o(ob) {F=i;};
   virtual void run() {((o).*(F))();};
};

// Convenience global net
Net Net::global;

// DEMO
class Queue: public Place
{
   list<string> files;
 public:
   void push(string s)
     {
	files.push_back(s);
	updated();
     }
   string pop()
     {
	string s = files.front();
	files.pop_front();
	updated();
	return s;
     }
};

class PrintingQueue
{
   Queue   * waiting;
   var<bool> printing;
 public:
   PrintingQueue(Queue *q);
   void state_changed();
};

PrintingQueue::PrintingQueue(Queue *q)
{
   waiting = q;
   Transition * t = new MemberTransition<PrintingQueue>(*this, &PrintingQueue::state_changed);
   t->react_on(waiting);
   t->react_on(printing);
   printing = false;
}

void PrintingQueue::state_changed()
{
   if (printing) return;
   string s = waiting->pop();
   cout << "Started printing of " << s << "\n";
   printing=true;
}

void demo1()
{
   Queue * q = new Queue();
   PrintingQueue pq1(q);
   PrintingQueue pq2(q);
   q->push("File1");
   q->push("File2");
   
   Net::global.execute();
}

void demo2()
{
   var<int> a(0);
   var<int> b(0);
   var<int> c(0);

   a.update_to(b);
   b.update_to(c);
   c.update_to(a);
   
   a=10;
   
   Net::global.execute();
}

int main(int argc, char* argv[])
{
   demo2();
}

