//=============================================================================

#include <iostream>
#include <limits>
#include <string>
#include <vector>
using namespace std;

//-----------------------------------------------------------------------------

class Hanoi
{
public:
   Hanoi(int sl);

   void print();
   void play();

private:
   void printTowerLevelSliceExists(const vector<int>& tower, int level);
   void printTowerLevelSliceNotExists();
   void printTowerLevel(const vector<int>& tower, int level);
   void printLevel(int level);
   void printPlate();
   void moveSlice(vector<int>& src, vector<int>& tgt);
   void moveTower(int size, vector<int>& src, vector<int>& tgt, vector<int>& util);

   static bool existASliceInThisLevel(const vector<int>& tower, int level);

   int slices;
   int counter = 1;
   vector<int> tower1;
   vector<int> tower2;
   vector<int> tower3;
};

//-----------------------------------------------------------------------------

Hanoi::Hanoi(int sl)
   : slices(sl)
{
   for (int i = slices; i > 0; --i)
   {
      tower1.push_back(i);
   }
}

//-----------------------------------------------------------------------------

void Hanoi::printTowerLevelSliceExists(const vector<int>& tower, int level)
{
   int slice = tower[level];
   string slice_output(slice, '-');
   int spaces = slices - slice;
   string space_output(spaces, ' ');

   cout << space_output << slice_output << '|' << slice_output << space_output;
}

//-----------------------------------------------------------------------------

void Hanoi::printTowerLevelSliceNotExists()
{
   string space_output(slices, ' ');
   cout << space_output << '|' << space_output;
}

//-----------------------------------------------------------------------------

bool Hanoi::existASliceInThisLevel(const vector<int>& tower, int level)
{
   return level < tower.size();
}

//-----------------------------------------------------------------------------

void Hanoi::printTowerLevel(const vector<int>& tower, int level)
{
   if (existASliceInThisLevel(tower, level))
   {
      printTowerLevelSliceExists(tower, level);
   }
   else
   {
      printTowerLevelSliceNotExists();
   }
}

//-----------------------------------------------------------------------------

void Hanoi::printLevel(int level)
{
   cout << ' ';
   printTowerLevel(tower1, level);
   cout << ' ';
   printTowerLevel(tower2, level);
   cout << ' ';
   printTowerLevel(tower3, level);
   cout << '\n';
}

//-----------------------------------------------------------------------------

void Hanoi::printPlate()
{
   int width = 7 + 6 * slices;
   string output(width, '*');
   cout << output << '\n';
}

//-----------------------------------------------------------------------------

void Hanoi::print()
{
   cout << counter << ".\n";
   for (int level = slices - 1; level >= 0; --level)
   {
      printLevel(level);
   }
   printPlate();
   cout << '\n';
}

//-----------------------------------------------------------------------------

void Hanoi::moveSlice(vector<int>& src, vector<int>& tgt)
{
   ++counter;
   int slice = src.back();
   src.pop_back();
   tgt.push_back(slice);
   print();
}

//-----------------------------------------------------------------------------
// Gedanke:
// Turm 1 => Turm 3
// 1. Turm 1 minus die unterste Platte auf Turm 2
// 2. unterste Platte auf Turm 3
// 3. Teil-Turm von Turm 2 auf Turm 3
// 
void Hanoi::moveTower(int size, vector<int>& src, vector<int>& tgt, vector<int>& util)
{
   if (size == 1)
   {
      moveSlice(src, tgt);
      return;
   }
   moveTower(size - 1, src, util, tgt);
   moveSlice(src, tgt);
   moveTower(size - 1, util, tgt, src);
}

//-----------------------------------------------------------------------------

void Hanoi::play()
{
   moveTower(tower1.size(), tower1, tower3, tower2);
}

//-----------------------------------------------------------------------------

int getSliceCountFromUser()
{
   for (;;)
   {
      cout << "Gib bitte die Anzahl Scheiben ein: ";
      int res;
      cin >> res;
      if (cin.fail() || res < 1 || res > 10)
      {
         cin.clear();
         cin.ignore(numeric_limits<streamsize>::max(), '\n');
         continue;
      }
      return res;
   }
}

//-----------------------------------------------------------------------------

int main()
{
   cout << "Tuerme von Hanoi 2\n\n";

   // Frage User nach Anzahl Scheiben
   int slices = getSliceCountFromUser();

   // Initialisieren von Hanoi
   Hanoi hanoi(slices);

   // Startzustand ausgeben
   hanoi.print();

   // Spiel...
   hanoi.play();
}
