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

using namespace std;

// -------------------------------------------------------------
// ZAD1 drzewo ze sprytnymi wskaznikami
// problem: cykliczna zaleznosc nie pozwala na zwalanianie elementow
//
// rozwiązanie: stosowanie slabych sprytnych wskaznikow aby usunac cykliczna zaleznosc
//-------------------------------------------------------------------------------------

class Tree;
using PTree = shared_ptr<Tree>;
using PWTree = weak_ptr<Tree>;

class Tree {
public:
    ~Tree() { cout << "Tree d-tor" << endl; }
    void addLeft(PTree left) {
        left_ = left;
        left_->up_ = me_.lock();
    }
    void addRight(PTree right) {
        right_ = right;
        right_->up_ = me_.lock();
    }
    //funkcja fabryczna
    static PTree create(const string& name);
private:
    Tree(const string& name) : name_(name) {
        cout << "Tree c-tor" << endl;
    }

    string name_;
    PWTree me_;
    PTree up_;
    PTree left_;
    PTree right_;
};

//correct
// class Tree {
// public:
//  ~Tree() { cout << "Tree d-tor" << endl; }
//  void addLeft(PTree left) {
//      left_ = left;
//      left_->up_ = me_;
//  }
//  void addRight(PTree right) {
//      right_ = right;
//      right_->up_ = me_;
//  }
//  //funkcja fabryczna
//  static PTree create(const string& name);
// private:
//  Tree(const string& name) : name_(name) {
//      cout << "Tree c-tor" << endl;
//  }

//  string name_;
//  PWTree me_;
//  PWTree up_;
//  PTree left_;
//  PTree right_;
// };

//funkcja fabryczna
PTree Tree::create(const string& name) {
    PTree node(new Tree(name) );
    node->me_ = node;
    return node;
}

void zad1() {
    PTree root = Tree::create("root");
    root->addLeft( Tree::create("left") );
}


// -------------------------------------------------------------*
//  *   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 doGet(); }
private:
    virtual int doGet() const { return i_; }
    int i_;
};

class D1 : virtual public B {
public:
    D1(int i) : B(i) {}
private:
    virtual int doGet() const { return 0; }
};
class D2 : virtual public B {
public:
    D2(int i) : B(i) {}
};

class M :  public D1, public D2 {
public:
    M(int i) : B(i), D1(i), D2(i) {}
};

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() % 4 + 4;
    M m(N);
    f(i, m);
}

void zad2() {
    try {
        g(1);
    } 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 - poprawk kod, zagmatwane warunki logiczne
//
//--------------------------------------------------------------

class Foo {
public:
    explicit Foo(int val = 0) : val_(val) {}

    bool isBest() const {
        if(!(val_ >= 0) || !(val_ <= 0))
            return true;
        else if ( (val_ < -1) && (val_ > 1) )
            return true;
        else if ( val_ == 1 )
            return true;
        return false;
    }

    bool isBest2() const { return val_ != 0; }
private:
    int val_;
};


void zad3() {
    cout << Foo(0).isBest() << endl;
    cout << Foo(1).isBest() << endl;
    cout << Foo(-1).isBest() << endl;
}

// ---------------------------------------------------------------------------------*
//  * ZAD4 - uzycie wizytatora                                 *
//  *----------------------------------------------------------------------------------

class Visitor;

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

class Number;
class OperatorAdd;

class Visitor {
public:
  virtual ~Visitor() {}
  virtual void visit(Number&) = 0;
  virtual void visit(OperatorAdd&) = 0;
};

class Number : public Element {
public:
    Number(int val) : val_(val) {}
    int getVal(){ return val_; }
    virtual void accept(Visitor& v) { v.visit(*this); }
private:
    int val_;
};

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


class Calculator : public Visitor {
public:
    virtual void visit(Number& n) {
        stack_.push(n.getVal());
    }
    virtual void visit(OperatorAdd& o) {
        int a = stack_.top();
        stack_.pop();
        int b = stack_.top();
        stack_.pop();
        stack_.push( a + b );
    }
    int value() { return stack_.top(); }
private:
    std::stack<int> stack_;
};

void zad4() {
    using PElement = shared_ptr<Element>;
    vector<PElement> v;
    v.push_back(PElement(new Number(1)));
    v.push_back(PElement(new Number(2)));
    v.push_back(PElement(new OperatorAdd()));
    v.push_back(PElement(new Number(3)));
    v.push_back(PElement(new OperatorAdd()));

    Calculator c;
    for(PElement e : v) {
        e->accept(c);
    }
    cout << c.value() << endl;
}




// -------------------------------------------------------------
//  ZAD5 hierarchiczna struktura obiektow, wojsko
//
//  problem: (1) reprezentacja hierarchicznej struktury obiektow
//           (2) tworzenie glebokiej kopii

// rozwiazanie: kompozyt, prototyp
// --------------------------------------------------------------

class Creature;
using PCreature = shared_ptr<Creature>;

class Creature {
public:
    Creature(const string id) : id_(id) { cout << "c-tor " << id_ << endl; }
    ~Creature() { cout << "d-tor " << id_ << endl; }

    const string& getId() const { return id_; }

    virtual void add(PCreature c) = 0;
    virtual PCreature clone() = 0;
private:
    string id_;
};

class Soldier : public Creature {
public:
    Soldier(const string id) : Creature(id) {}

    virtual PCreature clone() { return PCreature(new Soldier(getId()+" copy"));  }
    virtual void add(PCreature) {}
};

class Leader : public Creature {
public:
    Leader(const string id): Creature(id) {}

    Leader(const Leader& l) : Creature(l.getId()+" copy") {
        for(PCreature s : l.subordinate_) {
            subordinate_.push_back( s->clone() );
        }
    }

    void add(PCreature s) { subordinate_.push_back(s); }

    virtual PCreature clone() { return PCreature(new Leader(*this)); }
private:
    vector<PCreature> subordinate_;
};

void zad5() {
    PCreature s1(new Soldier("s1"));
    PCreature s2(new Soldier("s2"));
    PCreature l1(new Leader("l1"));
    l1->add(s1);
    l1->add(s2);
    PCreature l2(new Leader("l2"));
    l2->add(l1);

    PCreature l3 = l2->clone();
    PCreature l4 = l3->clone();
}




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;
}
