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

using namespace std;

// -------------------------------------------------------------*
//  *   ZAD1 lista ze sprytnymi wskaźnikami
//  *--------------------------------------------------------------
class List {
    struct Node;
public:
    List() : head_(nullptr) {}
    ~List();

    void push_front();

private:
    Node* head_;
};

struct List::Node {
    Node() : next_(nullptr), prev_(nullptr)
    {
        cout << "c-tor:" << endl;
    }
    ~Node() {
        cout << "d-tor:" << endl;
    }
    Node* next_;
    Node* prev_;
};

List::~List() {
    Node* n = head_;
    while( n != nullptr ) {
        Node* p = n;
        n = n->next_;
        delete p;
    }
}

void List::push_front() {
    Node* n = new Node();
    n->next_ = head_;
    if(head_)
        head_->prev_ = n;
    head_ = n;
}


class List2 {
    struct Node;
    using PNode = std::shared_ptr<Node>;
    using PWNode = std::weak_ptr<Node>;
public:
    List2() : head_(nullptr) {}
    ~List2();

    void push_front();

private:
    PNode head_;
};

struct List2::Node {
    Node()
    {
        cout << "c-tor:" <<  endl;
    }
    ~Node() {
        cout << "d-tor:" <<  endl;
    }
    PNode next_;
    PWNode prev_;
};

List2::~List2() {
}

void List2::push_front() {
    PNode n = PNode(new Node);
    n->next_ = head_;
    if(head_)
        head_->prev_ = n;
    head_ = n;
}

void zad1() {
    List2 l;
    l.push_front();
    l.push_front();
    l.push_front();
}

// -------------------------------------------------------------*
//  *   ZAD2 podaj wydruk
//  *--------------------------------------------------------------
class E : public std::exception { };

class B {
public:
    B(int i) : i_(i) { cout << i_; }
    virtual ~B() { cout << i_; }
    int get() const { return i_; }
private:
    int i_;
};

class D1 : public B {
public:
    D1(int i) : B(i-1) {}
};
class D2 : public B {
public:
    D2(int i) : B(i+1) {}
};

class M :  public D1, public D2 {
public:
    M(int i) : D1(i), D2(i) {}
    int get() const { return static_cast<const D1*>(this)->get(); }
};

string NAME = "XXXXX";

extern void g(int);

void f(int i, const M& m) {
    if(i <= 0) throw E();
    g(m.get());
}

void g(int i) {
    const int N = NAME.size() % 3 + 4;
    M m(N);
    f(m.get() - i, m);
}

void zad2() {
    try {
        g(2);
    } catch(E&){
    }
    cout << endl;
}

void zad2x() {
    cout << endl;
    for(int i=3;i<15;++i) {
        NAME = std::string(i, 'x');
        cout << "name: " << NAME << ":";
        zad2();
        cout << endl;
    }
}



// ---------------------------------------------------------------------------------*
//  * ZAD3 - uzycie wizytatora                                 *
//  *----------------------------------------------------------------------------------

class Visitor;

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

class MemoryError;
class AccessError;
class InputError;

class Visitor {
public:
  virtual ~Visitor() {}
  virtual void visit(MemoryError&) = 0;
  virtual void visit(AccessError&) = 0;
  virtual void visit(InputError&) = 0;
};

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

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

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

class MsgGenerator : public Visitor {
public:
    MsgGenerator() = default;
    MsgGenerator(const MsgGenerator&) = default;
    MsgGenerator& operator=(const MsgGenerator&) = default;

    virtual void visit(MemoryError&) { msg_ = "Network Error"; }
    virtual void visit(AccessError&) { msg_ = "User Break"; }
    virtual void visit(InputError&) { msg_ = "Bad Data"; }
    string msg_;
};


void zad3() {
  try {
    throw AccessError();
    //kod ktory moze zglosic wyjatek z hierarchii Exception
  // } catch(MemoryError&) {
  //     cerr << "Memory Error" << endl;
  // } catch(AccessError&) {
  //      cerr << "Access Error" << endl;
  // } catch(InputError&) {
  //     cerr << "Input Error" << endl;
  // }
  } catch(Exception& e) {
      MsgGenerator gen;
      e.accept(gen);
      cerr << gen.msg_ << endl;
  }
}


// -------------------------------------------------------------*
//  *   ZAD4 fabryka abstrakcyjna
//  *--------------------------------------------------------------

class Time {
public:
    virtual ~Time() {}

    int getHour();
    int getMinute();
    int getSec();
};

using PTime = std::shared_ptr<Time>;

class Date {
public:
    virtual ~Date() {}
    int getYear();
    int getMonth();
    int getDay();

};

using PDate = std::shared_ptr<Date>;

class BoostTime : public Time {
public:
    BoostTime(int h, int m, int s) { cout << "C-tor BoostTime" << endl; }
};

class ChronoTime : public Time {
public:
    ChronoTime(int h, int m, int s) { cout << "C-tor ChronoTime" << endl; }
};

class BoostDate : public Date {
public:
    BoostDate(int y, int m, int d) { cout << "C-tor BoostDate" << endl; }
};

class ChronoDate : public Date {
public:
    ChronoDate(int y, int m, int d) { cout << "C-tor ChronoDate" << endl; }
};


class AbsFactory {
public:
    virtual PTime time(int h, int m, int s) = 0;
    virtual PDate date(int y, int m, int d) = 0;
};

class BoostDTFactory : public AbsFactory {
public:
    virtual PTime time(int h, int m, int s) {
        return PTime(new BoostTime(h, m, s) );
    }
    virtual PDate date(int y, int m, int d) {
        return PDate(new BoostDate(y, m, d) );
    }
};

class ChronoFactory : public AbsFactory {
public:
    virtual PTime time(int h, int m, int s) {
        return PTime(new ChronoTime(h, m, s) );
    }
    virtual PDate date(int y, int m, int d) {
        return PDate(new ChronoDate(y, m, d) );
    }
};


void zad4() {
    AbsFactory* f = new BoostDTFactory();
    f->date(2018,04,10);
    f->time(20,0,0);
}


/**-------------------------------------------------------------*
 * ZAD5 - rozwiazanie - obserwator                              *
 *--------------------------------------------------------------*/

class Observer {
public:
    virtual ~Observer() {}
    virtual void update() = 0;
};

class MainView {
public:
    void close() {
        for(Observers::iterator i = observers_.begin(); i != observers_.end(); ++i) {
            (*i)->update();
        }
    }
    void reg(Observer* obs) { observers_.push_back(obs); }
private:
    typedef std::vector<Observer*> Observers;
    Observers observers_;
};


class ChildViewA : public Observer {
public:
    ChildViewA(MainView* m) : m_(m) {}
    void open() {
        m_->reg(this);
    }
    virtual void update() {
        close();
    }
    void close() {
        cout << "close child" << endl;
    }
private:
    MainView* m_;
};

class ChildViewB : public Observer {
public:
    ChildViewB(MainView* m) : m_(m) {}
    void open() {
        m_->reg(this);
    }
    virtual void update() {
        close();
    }
    void close() {
        cout << "close child" << endl;
    }
private:
    MainView* m_;
};

void zad5() {
    MainView m;
    ChildViewA ch1(&m);
    ch1.open();
    ChildViewB ch2(&m);
    ch2.open();
    ChildViewA ch3(&m);
    m.close();
}


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