#include <string>
#include <stack>
#include <vector>
#include <list>
#include <iostream>
#include <memory>
#include <cassert>

using namespace std;

/**-------------------------------------------------------------*
 *   ZAD1 lista list ze sprytnymi wskaźnikami           *
 *--------------------------------------------------------------*/
class List {
  struct Node;
  //typedef Node* PNode;
  typedef std::shared_ptr<Node> PNode;
  struct Node {
    Node() { cout << "node c-tor" << endl; }
    ~Node() { cout << "node d-tor" << endl; }
    PNode next_;
  };
public:
  List() {}
  ~List() {
    // while(head_) {
    //  PNode tmp = head_;
    //  head_ = head_->next_;
    //  delete tmp;
    // }
  }
  void push_front() {
    // PNode n = new Node();
    PNode n(new Node());
    n->next_ = head_;
    head_ = n;
  }
private:
  PNode head_;
};

void zad1() {
  List l;
  for(int i =0;i< 10;++i)
    l.push_front();
}


/**---------------------------------------------------------------------------------*
 * ZAD2 - rozwiazanie                                 *
 * problem: sterowanie przy obsłudze wyjątków, sprytne wskazniki, r-value reference *
 *----------------------------------------------------------------------------------*/

class E : public std::exception {
public:
  E(int i) : i_(i) {}
  int i_;
};

struct F {
  F(int i) : i_(i) { }
  virtual ~F() { cout << i_ << ' '; }
  int i_;
};

typedef unique_ptr<F> PF;

void f(PF&& pf) {
  // cout << "f PF&&" << endl;
  if(pf->i_ > 3)
    f( PF(new F(pf->i_ - 3)));
  else
    throw E(pf->i_);
}

void f(const PF& pf) {
  //  cout << "f const PF&" << endl;
  if( pf->i_ > 1)
    f( PF(new F(pf->i_ / 2)));
}


int LICZBA_LITER_NAZWISKA = 5;

void zad2() {
  for(int i=3;i<15;++i) {
    LICZBA_LITER_NAZWISKA = i;
    cout << LICZBA_LITER_NAZWISKA << ": ";
    try {
      PF pb(new F(LICZBA_LITER_NAZWISKA - 1));
      f(pb);
    } catch(E& e){
      cout << e.i_;
    }
    cout << endl;
  }
}

/**-------------------------------------------------------------*
 * ZAD 3 uzycie wizytatora                                      *
 *--------------------------------------------------------------*/

class Visitor;

class BaseException : public std::exception {
public:
  virtual void accept(Visitor& v)  = 0;
};

class NetworkError;
class UserBreak;
class BadData;

class Visitor {
public:
  virtual ~Visitor() {}
  virtual void visit(NetworkError&) = 0;
  virtual void visit(UserBreak&) = 0;
  virtual void visit(BadData&) = 0;
};

class NetworkError : public BaseException {
public:
  virtual void accept(Visitor& v) { v.visit(*this); }
};

class UserBreak : public BaseException {
public:
  virtual void accept(Visitor& v) { v.visit(*this); }
};

class BadData : public BaseException {
public:
  virtual void accept(Visitor& v) { v.visit(*this); }
};

// class Counters {
// public:
//  static Counters& getInstance() {
//    static Counters counters;
//    return counters;
//  }
//   int networkErrors_;
//   int userBreaks_;
//   int badData_;
// private:
//  Counters() : networkErrors_(0), userBreaks_(0), badData_(0) {}
//  Counters(const Counters&) = delete;
//  Counters& operator=(const Counters&) = delete;
// };

class Counters : public Visitor {
public:
  static Counters& getInstance() {
    static Counters counters;
    return counters;
  }
  virtual void visit(NetworkError&) { ++networkErrors_; }
  virtual void visit(UserBreak&) { ++userBreaks_; }
  virtual void visit(BadData&) { ++badData_; }

  int networkErrors_;
  int userBreaks_;
  int badData_;
private:
  Counters() : networkErrors_(0), userBreaks_(0), badData_(0) {}
  Counters(const Counters&) = delete;
  Counters& operator=(const Counters&) = delete;
};

ostream& operator<<(ostream& os, const Counters& c) {
  os << "NetworkErrors:" << c.networkErrors_ << ',';
  os << " UserBreaks:" << c.userBreaks_ << ',';
  os << " BadData:" << c.badData_ << endl;
}

void zad3() {
  try {
    throw UserBreak();
    //kod ktory moze zglosic wyjatek typu BaseException
  // } catch(NetworkError&) {
  //  ++(Counters::getInstance().networkErrors_);
  // } catch(UserBreak&) {
  //  ++(Counters::getInstance().userBreaks_);
  // } catch(BadData&) {
  //  ++(Counters::getInstance().badData_);
  // }
  } catch(BaseException& e) {
    e.accept(Counters::getInstance());
  }
  cout << Counters::getInstance() << endl;
}


/**---------------------------------------------------------------------------------*
 * ZAD4 - rozwiazanie                                 *
 * problem: dziedziczenie wielobazowe, dziedziczenie wirtualne            *
 *----------------------------------------------------------------------------------*/
struct B {
  int g() { return 1; }
};
struct D1 : virtual public B {
  int g() { return 2; }
};
struct D2 : virtual public B {
  virtual int g() { return 3; }
};
struct M : virtual public D1, virtual public D2 {
  virtual int g() { return 4; }
};

void zad4() {
  M m;
  D2* d2 = &m;
  cout << m.g() << d2->g() << static_cast<D1&>(m).g() << static_cast<B&>(m).g() << endl;
}


/**-------------------------------------------------------------*
ZAD5 - prototyp                                                 *
 *--------------------------------------------------------------*/
class Element;
typedef std::shared_ptr<Element> PElement;
class Element {
public:
  Element() {}
  virtual ~Element(){}
  virtual void add(PElement) {}
  virtual PElement clone() = 0;
};

class Leaf : public Element {
public:
  Leaf() : Element() { cout << "c-tor leaf" << endl; }
  Leaf(const Leaf&) { cout << "copy c-tor leaf" << endl; }
  ~Leaf() { cout << "d-tor leaf" << endl; }
  virtual PElement clone() { return PElement(new Leaf(*this)); }
};

class Node : public Element {
public:
  Node() : Element()  { cout << "c-tor node" << endl; }
  Node(const Node& n) {
    cout << "copy c-tor node" << endl;
    for(PElement p : n.ch_) {
      ch_.push_back(p->clone());
    }
  }
  ~Node() { cout << "d-tor node" << endl; }
  void add(PElement el) { ch_.push_back(el); }
  virtual PElement clone() { return PElement(new Node(*this)); }
private:
  std::vector<PElement> ch_;
};

void zad5() {
  PElement tree(new Node());
  PElement n1(new Node());
  n1->add(PElement(new Leaf()));
  n1->add(PElement(new Leaf()));
  tree->add(n1);
  PElement n2(new Node());
  n2->add(PElement(new Leaf()));
  tree->add(n2);
  PElement tree2 = tree->clone();
}



int main() {
  cout << "zad1"<<endl;
  zad1();
  cout << endl;
  cout << "zad2"<<endl;
  zad2();
  cout << endl;
  cout << "zad3"<<endl;
  zad3();
  cout << endl;
  cout << "zad4"<<endl;
  zad4();
  cout << endl;
  cout << "zad5"<<endl;
  zad5();
  cout << endl;
  return 0;
}
