// Jedes Programm sollte zu Beginn einen Kommentarblock enthalten,
// in dem angegeben wird, wie das Programm heißt, wer es wann geschrieben hat
// ggf. letzte Änderungen / Änderungshistorie, was das Programm tut, und welche
// Ideen dahinter stecken ... dazu bieten sich die C-Kommentare an, die
// die auskommentierten Zeilen in /* und */ einschließen
/*****************************************************************************
 * Lösen von Umfüllproblemen
 * Autor: TEL11Gr1
 * Datum: WS 11/12
 *
 * Aufgabe: Ziel ist aus einer Eingabe von 3 Gefäßgrößen (z.B. 8, 5 und 3 Einheiten)
 * und einer Startkonfiguration/füllung (z.B. (8,0,0)) und einer Zielsetzung
 * (z.B. (4,4,0)) einen Weg zu finden, durch reines Umfüllen (ggf. Wegleeren) von Gefäßinhalten
 * die Zielkonfiguration zu erreichen und auszugeben. Nebenziel: das Ganze mit
 * möglichst wenig Umfüllvorgängen.
 * 
 * Vorgehen: Bestimme aus der Startkonfiguration iterativ alle möglichen Folgekonfigurationen
 * bis entweder das Ziel gefunden wurde oder keine weiteren Konfigurationen mehr erreichbar sind.
 *
 * Hilfreich, um eine Programmstruktur festzulegen sind entweder Diagramme wie in der VL
 * oder Pseudocode (eher als Gedankenstütze), hier als Bsp. in Pseudocode:
 *
 * Kapazitäten/Start/Ziel eingeben
 * Menge von Konfigurationen initialisieren
 * solange noch nicht alle Konfigurationen auf mögliche Folgekonfigurationen untersucht wurden
 *   wähle eine solche aus
 *   und füge die möglichen Folgekonfigurationen zu einer Menge der Konfigurationen hinzu
 * bis keine solchen Konfigurationen mehr vorliegen oder das Ziel gefunden wurde
 * Lösungsweg ausgeben
 ****************************************************************************/

/*
/*
auszukommentieren
*/ // Kommentar ist bereits hier zu Ende ...
//*/ (nur zur Sicherheit auskommentiert)
#include<iostream>
using namespace std;

//typedef int Datentyp[3]; // Datentyp ist für den Compiler von nun an ein Typ für ein
// Array aus 3 int-Werten
// aufgrund der Problematik Array als Parameter (s.u.) verstecken wir das Array in einem struct
struct Datentyp
{
	int arr[3];
};
// Datentyp ist nun ein Typ (ein Kästchen), der ein Array mit 3 int-Werten enthält

bool operator ==(const Datentyp a, const Datentyp b)
{
	return ((a.arr[0] == b.arr[0]) && (a.arr[1] == b.arr[1]) && (a.arr[2] == b.arr[2]));
	// wertet sich genau dann zu wahr aus, wenn jede der drei Elemente jeweils gleich sind
}

// Ausgabe für selbstdefinierte Datentypen ... (nehmen Sie das einfach als Kochrezept)
ostream& operator << (ostream& os, const Datentyp a)
// wir werden hier für den Parameter os das cout benutzen
{
	os << '(' << a.arr[0] << ',' << a.arr[1] << ',' << a.arr[2] << ')';
	return os; // muss hier noch über return zurückgegeben werden, damit
	// im Anschluss noch andere Werte mit << ... folgen können
}

// analog Eingabe für selbstdefinierte Datentypen
istream& operator >> (istream& is, Datentyp & a)
// wir werden hier für den Parameter is das cin benutzen
{
	is >> a.arr[0] >> a.arr[1] >> a.arr[2];
	return is; // muss hier noch über return zurückgegeben werden, damit
	// im Anschluss noch andere Werte mit >> ... folgen können
}



struct Mengentyp
{
	Datentyp content[100];
	int nrOfValues;
};

// Auftretenes Problem: wird ein Array als Parameter übergeben, dann übergibt C/C++
// nur einen Verweis auf den entsprechenden Speicherbereich. Dies führt ggf. zu
// Problemen bei der Deklaration der Variablen/Parameter (Stichwort const)

// Abhilfe bei Arrays: man verstecke das Array in einem struct (wie oben)

void addToSet(const Datentyp value, Mengentyp & set)
// es wird davon ausgegangen, dass value noch nicht in set vorhanden ist
{
	set.content[set.nrOfValues]=value;
	set.nrOfValues++; // erhöht den Wert der Variablen nrOfValues um 1
	// Problematik, dass das set nur 100 Werte umfassen darf, wird hier zunächst ausgeblendet
}

bool isInSet(const Datentyp value, const Mengentyp set) // testet, ob Wert value in der Menge set ist
{
	int index = 0;
	for ( ; index < set.nrOfValues; index = index+1)
	{
		if (value == set.content[index]) // der Vergleich von zwei selbst definierten
			// Datentypen ist C/C++ nicht bekannt (kann es auch nicht sein)
			// wir müssen dem Compiler mitteilen, was wir hier unter == verstehen wollen (s.o.)
		{
			return true;
		}
	}
	return false;
}

void printSet(const Mengentyp set) // gibt die Werte der Menge set aus
{
	if (set.nrOfValues == 0)
	{
		cout << "Die Menge ist leer" << endl;
	}
	else
	{
		cout << "Die Menge umfasst die Werte " << set.content[0];
		int index;
		for (index=1; index < set.nrOfValues; index = index+1)
		{
			cout << ", " << set.content[index];
		}
		cout << endl;
	}
}

// Hilfsfunktionen zur Erstellung der Folgekonfigurationen

// Variante 1:
// Datentyp fuellAinBum(const Datentyp konfig, const Datentyp kapaz, const int gef1, const int gef2)
// Ablauf wäre dann: folgeKonf = fuellAinBum(ausgangsKonf,kapaz,0,2); // 0 und 2 nur als Beispiel

// Variante 2:
// void fuellAinBum(const int gef1, const int gef2, const int kap2, int & zgef1, int & zgef2)
// Ablauf wäre dann: fuellAinBum(ausK[0],ausK[2],kapaz[2],zielK[0],zielK[2]);

// Variante 3:
void fuellAinBum(int & gef1, int & gef2, const int kap2)
// Idee: erst Kopie erstellen und dann die Kopie verändern
// Ablauf wäre dann: zielK = ausK; fuellAinBum(zielK[0],zielK[2],kapaz[2]);
{
	if (gef1+gef2 <= kap2) // passt der Inhalt beider Gefäße in das zweite hinein?
	{
		gef2 = gef1 + gef2;
		gef1 = 0;
	}
	else // sonst wird das zweite Gefäß komplett gefüllt
	{
		gef1 = gef1 - (kap2 - gef2);
		gef2 = kap2;
	}
}

void findeFolgeKonfVonA(const Datentyp ausK, const Datentyp kapaz, Mengentyp & set)
{
    Datentyp zielK;
	zielK = ausK; fuellAinBum(zielK.arr[0], zielK.arr[1], kapaz.arr[1]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	zielK = ausK; fuellAinBum(zielK.arr[0], zielK.arr[2], kapaz.arr[2]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	zielK = ausK; fuellAinBum(zielK.arr[1], zielK.arr[0], kapaz.arr[0]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	zielK = ausK; fuellAinBum(zielK.arr[1], zielK.arr[2], kapaz.arr[2]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	zielK = ausK; fuellAinBum(zielK.arr[2], zielK.arr[0], kapaz.arr[0]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	zielK = ausK; fuellAinBum(zielK.arr[2], zielK.arr[1], kapaz.arr[1]);
	if (!isInSet(zielK, set))
	{
		addToSet(zielK, set);
	}
	// Selbst überlegen, ob die Aufzählung der 6 Fälle so sinnvoll ist, oder ob man
	// besser mit for-Schleifen die Fälle aufzählt (und damit die if (!isInSet(.,.))
	// Abfrage nur einmal programmiert)

	// hier noch 3 Fälle, bei denen der Inhalt weggekippt wird -> selbst programmieren
}



int main(void)
{
	// wird nicht mehr gebraucht: Datentyp value; // einzulesener Wert
	Mengentyp set; // Menge mit maximal 100 Werte vom Typ Datentyp
	set.nrOfValues = 0; // zur Sicherheit, damit Funktionen wie printSet ein
	// definiertes Verhalten zeigen
	Datentyp kapaz; // Kapazitäten der drei Gefäße
	Datentyp start; // Startkonfiguration
	Datentyp ziel; // Zielkonfiguration

	// Kapaz./Start/Ziel einlesen
	cout << "Gib ein 3er-Tupel für die Kapazitaeten ein: ";
	cin >> kapaz; // wir benutzen jetzt unseren eigenen >>-Operator
	// vorher: cin >> kapaz.arr[0] >> kapaz.arr[1] >> kapaz.arr[2];

	cout << "Gib ein 3er-Tupel für die Startkonfiguration ein: ";
	cin >> start;

	cout << "Gib ein 3er-Tupel für die Zielkonfiguration ein: ";
	cin >> ziel;

	// Mengenverwaltung initialisieren
    set.content[0] = start;
	set.nrOfValues = 1;

    // ab hier ist es noch der alte Code, der dann zu ersetzen ist ...
/*	while (value.arr[0] != 0)  
	{
		if (!(isInSet(value, set)))
		{
			addToSet(value, set);
			printSet(set);
		}
		else
		{
			cout << value << " ist bereits in der Menge vorhanden" << endl;
		}

		cout << "Gib naechstes 3er-Tupel ein (Abbruch mit (0,.,.)): ";
		cin >> value.arr[0] >> value.arr[1] >> value.arr[2];
	}
*/

	return 0;
}

