#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

// ==== Observer Pattern Framework ============================================

#include "observer.h"

// ==== Application that uses the Classic Observer Pattern ====================

// == ConcreteSubjects ==

typedef Model<bool> Pin;
typedef Model<short> Register;

// == ConcreteObservers ==

template <class T>
class DecDisplay {
public:
	DecDisplay(Model<T>& m, const string& n):
   	cow(m, this, &DecDisplay::update), name(n) {
	}
private:
	void update() {
		cout<<name<<" = "<<dec<<cow->get()<<endl;
	}
	CallOnWrite<T, DecDisplay<T> > cow;
	string name;
};

typedef DecDisplay<short> RegisterDisplay;
typedef DecDisplay<bool> PinDisplay;

// == Components ==

template <class Func>
class BinaryComponent {
typedef Func::first_argument_type T;
public:
	BinaryComponent(Model<T>& i1, Model<T>& i2, Model<T>& o):
			in1(i1, this, &BinaryComponent::update),
			in2(i2, this, &BinaryComponent::update),
			out(o) {
		update();
	}
private:
	void update() {
		currentValue=func(in1->get(), in2->get());
		if (out.get()!=currentValue)
			out.set(currentValue);
	}
	Func func;
	T currentValue;
	CallOnWrite<T, BinaryComponent<Func> > in1;
	CallOnWrite<T, BinaryComponent<Func> > in2;
	Model<T>& out;
};

template <class Func>
class UnaryComponent {
typedef Func::argument_type T;
public:
	UnaryComponent(Model<T>& i1, Model<T>& o):
			in1(i1, this, &UnaryComponent::update),
			out(o) {
		update();
	}
private:
	void update() {
		currentValue=func(in1->get());
		if (out.get()!=currentValue)
			out.set(currentValue);
	}
	Func func;
	T currentValue;
	CallOnWrite<T, UnaryComponent<Func> > in1;
	Model<T>& out;
};

typedef BinaryComponent<logical_and<bool> > And;
typedef BinaryComponent<logical_or<bool> > Or;
typedef UnaryComponent<logical_not<bool> > Not;
typedef BinaryComponent<plus<short> > Add;

// == Simulator ==

class Simulator {
public:
	Pin pin1, pin2, pin3;
	Register reg1, reg2;
	void run() {
		pin1.set(0); pin2.set(0);
		pin1.set(1); pin2.set(0);
		pin1.set(1); pin2.set(1);
		pin1.set(1); pin2.set(0);
		pin1.set(0); pin2.set(0);
		reg1.set(13);
		reg2.set(15);
	}
};

// == User Interface ==

void main() {
	Simulator s;
	PinDisplay d1(s.pin3, "Pin 3");
	Pin node1, node2, node3, node4;
	Not not1(s.pin1, node1);
	Not not2(s.pin2, node2);
	And and1(node1, s.pin2, node3);
	And and2(node2, s.pin1, node4);
	Or or1(node3, node4, s.pin3);
	Register sum;
	RegisterDisplay d2(sum, "reg1 + reg2");
	Add add1(s.reg1, s.reg2, sum);
	s.run();
#ifdef __TURBOC__
	cout<<"Press a key: "; cin.get();
#endif
}
