// 2. Version von shapes.C mit templates

#include <math.h>
#include <iostream.h>

template <class Object> class Collection {
    public:
	virtual void operator += (Object&) {};	// Einfügeoperation
	virtual void operator -- () {};		// Löschoperation

	// Um hier eine Löschoperation -= (Object sub) implementieren zu
	// können, müßte ein virtual boolean Object::operator == (Object)
	// existieren und in alle Klassen eingebaut werden. Da für unseren
	// Testlauf kein Löschen gefordert und die Funktionalität der 
	// Löschoperation in der Aufgabenstellung offen gelassen wurde, 
	// ist ein unäres --, welches ein willkürlich festgelegtes Element
	// löscht, einfacher zu implementieren.

	virtual void iteration () {};		// Initialisator für:
	virtual Object* next () { return NULL; };
	// nur ein Iterator, keine verschachtelten
};

template <class Object> struct Link { Object * element; Link * next; };

// In List werden nur Objektreferenzen eingetragen, nicht aber das Objekt
// kopiert. Wie man das besser macht, habe ich noch nicht herausgefunden.

template <class Object> class List: public Collection<Object> {
	Link<Object> * first, * second, * iterator;
    public:
	List () {				// Konstruktor
		first = iterator = NULL;
	}
	void operator += (Object& add) {
		second = new Link<Object>; 
		second -> element = & add;
		second -> next = first;
		first = second;
	}
	void iteration () {
		iterator = first;
	}
	Object* next () { register Object * value = NULL;
		if (iterator) {
			value = iterator -> element;
			iterator = iterator -> next;
		}
		else iterator = first; return value;
	}
	void operator -- () {
		delete first -> element;
		second = first -> next;
		delete first;
		first = second;
	}	// liefert SIGSEGV, wenn auf leere List angewandt
};

struct Vector {					// Vektorgrafik im R²
	double X, Y; 
	Vector (double x = 0, double y = 0) {X = x; Y  =   y;	}
	void operator *= (double r)	{ X *= r;   Y *= r;	}
	void operator += (Vector v)	{ X += v.X; Y += v.Y;	}
	void operator -= (Vector v)	{ X -= v.X; Y -= v.Y;	}
	void rotate (double angle);
};

void Vector::rotate (double angle) {
	angle *= PI / 180;
	double	C = cos (angle), 
		S = sin (angle), 
		Z = X * C - Y * S; 
	Y = X * S + Y * C;
	X = Z;
}

const Vector Null (0,0);

ostream & operator << (ostream & out, Vector V) {
	out << " (" << V.X << "," << V.Y << ") "; return out;
}

class Shape {
    protected:
	Vector Center;
    public:
	virtual void center (Vector& result) { result = Center; }
	virtual void recenter (Vector where = Null) { Center = where; }
	virtual void rotate (double angle = 90) { angle = 0; }
	virtual void draw () { cerr << "draw?" << endl; }
};

class Circle: public Shape {
	double Diameter;
    public:
	Circle (Vector C = Null, double D = 1) { Center = C; Diameter = D; }
	void draw () {
		cout << "circle at" << Center;
		cout << "diameter " << Diameter << endl;
	}
};		// Ausgabe der Grafik im pic-Format

class Rectangle: public Shape {
	double Width, Height, Incline;
    public:
	Rectangle (Vector C = Null, double W = 1, double H = 1, double I = 0) 
		{ Center = C; Width = W; Height = H; Incline = I; }
	void rotate (double angle = 90) { Incline += angle; }
	void draw ();
};

const char to[] = "to";
void Rectangle::draw () {

	Vector East (Width / 2, 0); East.rotate (Incline); 
	Vector North (0, Height / 2); North.rotate (Incline);		
	Vector Northeast = North; Northeast += East; 
	Vector Northwest = North; Northwest -= East;
	Vector Southwest = Center; Southwest -= Northeast;
	Vector Southeast = Center; Southeast -= Northwest;
	Northeast += Center; Northwest += Center;
	cout << "line from" << Northeast << to << Northwest << to;
	cout << Southwest << to << Southeast << to << Northeast << endl;
}

class Graphics: public Shape, public List <Shape> {

	// Hilfsvariablen :
	Vector C;
	Shape *S;

    public:
	void center (Vector & result) {
		int count = 0; result = Null;
		for (iteration(); S = next(); ++ count) {
			S->center (C); result += C;
		}
		if (count) result *= 1.0 / count;
	}
	// Es war nicht definiert, was unter Zentrum einer Menge von Formen
	// zu verstehen sein soll, ich berechne hier den Schwerpunkt unter
	// der Annahme, alle Elemente seien gleich schwer. Genau genommen,
	// bräuchte man hier noch ein virtual double Shape::weight(). Das
	// spare ich mir mal.

	void recenter (Vector where = Null) {
		center (Center); where -= Center;
		for (iteration (); S = next();) {
			S -> center (C); C += where; S -> recenter (C);
		}
	}
	// Die Methoden rotate und recenter funktionieren nur dann
	// ordnungsgemäß, wenn keine Figuren mehrfach eingetragen sind.

	void rotate (double angle = 90) {
		center (Center);
		for (iteration (); S = next (); ) {
			S -> center (C); C -= Center;	// um den Schwerpunkt
			C.rotate (angle); S -> rotate (angle);
			C += Center; S -> recenter (C);
		}
	}
	void draw () { for (iteration (); S = next(); S -> draw ()); }
};


// Testgraphik, etwas chaotisch

int main () {

	// Zwei Nackommastellen, keine Exponentialdarstellung, bitte:

	cout.setf (ios::fixed); cout.precision (2); cout << ".PS\n";

	Rectangle	
		SquareInch, SmallSquare (Null, 0.4, 0.4), 
		Tower (Vector (0.7, 4), 1, 6),	
		Board (Vector (2,0.75), 5, 0.2),
		Decline (Vector (3), 3, 0, -20);

	Circle	O (Null, 0.1), 
		Ball (Vector (3, 2.5), 3);

	Graphics G, H;		G += H;

	G += Tower;	H += SmallSquare;	G += Ball;
	G += Board;	H += SquareInch;	G += Decline;

	H += O; G.draw(); H --;

	Vector V; G.center (V); V += Vector (-1, 0.25); 
	G.recenter (V); G.draw();

	cout << "circle invis \"X\" at" << V << endl;	// makiert Schwerpunkt

	G.rotate (-1); G.draw (); G.rotate (-1); G.draw (); 
	G.rotate (-1); G.draw (); G.rotate (-1); G.draw (); 

	cout << ".PE\n"; return 0;

	// Es funktioniert alles: Kreise, Quadrate, einfügen, löschen
	// Graph mit Teilgraphelement, verschieben (und damit implizit
	// auch die Schwerpunktberechnung) und rotieren.
	// Vergleiche Makefile für den korrekten Aufruf mit Bildausgabe.
}
