//: C10:ShapeFactory2.cpp
// Z knihy "Myslíme v jazyce C++, část 2" od Bruce Eckela a Chucka Allisona.
// (c) 1995-2004 MindView, Inc. Všechna práva vyhrazena.
// Přečtěte si podmínky používání zdrojových kódů umístěné v souboru 
// 'Licence.txt' dodávaném spolu s balíčkem kódu dostupným na www.MindView.net.
// Polymorfní tovární metody.
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <stdexcept>
#include <cstddef>
#include "../purge.h"
using namespace std;

class Tvar {
public:
  virtual void vykreslit() = 0;
  virtual void smazat() = 0;
  virtual ~Tvar() {}
};

class TovarnaTvaru {
  virtual Tvar* vytvorit() = 0;
  static map<string, TovarnaTvaru*> tovarny;
public:
  virtual ~TovarnaTvaru() {}
  friend class InicializatorTovarnyTvaru;
  class ChybneVytvoreniTvaru : public logic_error {
  public:
    ChybneVytvoreniTvaru(string typ)
    : logic_error("Nelze vytvorit typ " + typ) {}
  };
  static Tvar*
  vytvoritTvar(const string& id) throw(ChybneVytvoreniTvaru) {
    if(tovarny.find(id) != tovarny.end())
      return tovarny[id]->vytvorit();
    else
      throw ChybneVytvoreniTvaru(id);
  }
};

// Definovat statický objekt:
map<string, TovarnaTvaru*> TovarnaTvaru::tovarny;

class Kruh : public Tvar {
  Kruh() {} // Soukromý konstruktor
  friend class InicializatorTovarnyTvaru;
  class Tovarna;
  friend class Tovarna;
  class Tovarna : public TovarnaTvaru {
  public:
    Tvar* vytvorit() { return new Kruh; }
    friend class InicializatorTovarnyTvaru;
  };
public:
  void vykreslit() { cout << "Kruh::vykreslit" << endl; }
  void smazat() { cout << "Kruh::smazat" << endl; }
  ~Kruh() { cout << "Kruh::~Kruh" << endl; }
};

class Ctverec : public Tvar {
  Ctverec() {}
  friend class InicializatorTovarnyTvaru;
  class Tovarna;
  friend class Tovarna;
  class Tovarna : public TovarnaTvaru {
  public:
    Tvar* vytvorit() { return new Ctverec; }
    friend class InicializatorTovarnyTvaru;
  };
public:
  void vykreslit() { cout << "Ctverec::vykreslit" << endl; }
  void smazat() { cout << "Ctverec::smazat" << endl; }
  ~Ctverec() { cout << "Ctverec::~Ctverec" << endl; }
};

// Jedináček inicializující objekty TovarnaTvaru:
class InicializatorTovarnyTvaru {
  static InicializatorTovarnyTvaru si;
  InicializatorTovarnyTvaru() {
    TovarnaTvaru::tovarny["Kruh"]= new Kruh::Tovarna;
    TovarnaTvaru::tovarny["Ctverec"]= new Ctverec::Tovarna;
  }
  ~InicializatorTovarnyTvaru() {
    map<string, TovarnaTvaru*>::iterator it =
      TovarnaTvaru::tovarny.begin();
    while(it != TovarnaTvaru::tovarny.end())
      delete it++->second;
  }
};

// definice statických členů
InicializatorTovarnyTvaru InicializatorTovarnyTvaru::si;

char* sl[] = { "Kruh", "Ctverec", "Ctverec",
  "Kruh", "Kruh", "Kruh", "Ctverec" };

int main() {
  vector<Tvar*> tvary;
  try {
    for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
      tvary.push_back(TovarnaTvaru::vytvoritTvar(sl[i]));
  } catch(TovarnaTvaru::ChybneVytvoreniTvaru e) {
    cout << e.what() << endl;
    return EXIT_FAILURE;
  }
  for(size_t i = 0; i < tvary.size(); i++) {
    tvary[i]->vykreslit();
    tvary[i]->smazat();
  }
  purge(tvary);
} ///:~
