#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 LList {
    static const int MAX_SIZE = 3;
    struct Node;
    typedef std::shared_ptr<Node> PNode;
    typedef std::weak_ptr<Node> PWNode;
    struct Node {
        Node() { cout << "node c-tor" << endl; }
        ~Node() { cout << "node d-tor" << endl; }
        PNode next_;
    };
    struct List;
    typedef std::shared_ptr<List> PList;
    typedef std::weak_ptr<List> PWList;
    struct List {
        List() : count_(0) { cout << "list c-tor" << endl; }
        ~List() { cout << "list d-tor" << endl; }
        PNode head_;
        int count_;
        PList next_;

        bool push_front() {
            if(count_ < MAX_SIZE) {
                PNode node = PNode(new Node());
                node->next_ = head_;
                head_ = node;
                ++count_;
                return true;
            }
            return false;
        }
    };
public:
    LList() {}
    ~LList() {
        if( tail_ ) tail_->next_.reset();
    }
    void push_front() {
        if( ! head_ || ! head_->push_front() ) {
            PList l = PList(new List()); //create new List
            l->push_front();

            if( head_ ) {
                l->next_ = head_;
            } else {
                tail_ = l;
            }
            head_ = l;
            tail_->next_ = head_;
        }
    }
private:
    PList head_;
    PList tail_;
};

void zad1() {
    LList 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) { cout << i_ << ' '; }
    virtual ~F() { cout << i_ << ' '; }
    int i_;
};

typedef unique_ptr<F> PF;

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

void f(const PF& pf) {
    if(pf->i_ > 0) {
        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;
        try {
            PF pb(new F(LICZBA_LITER_NAZWISKA));
            f(pb);
        } catch(E& e){
            cout << e.i_;
        }
        cout << endl;
    }
}

/**-------------------------------------------------------------*
ZAD3 - copy on write dla smart_ptr                              *
 *--------------------------------------------------------------*/
//TODO: tresc, patrz kol1zestH, zad1
class Big {
public:
    Big(int i) : i_(i) {
        cout << "c-tor Foo" << endl;
        //bardzo kosztowna konstrukcja obiektu
    }
    Big(const Big& b) : i_(b.i_) {
        cout << "copy c-tor Foo" << endl;
        //bardzo kosztowna konstrukcja obiektu
    }
    int get() const { return i_; }
    void set(int i) { i_ = i; } //zmienia zawartosc obiektu
private:
    int i_;
};

class LazyBig {
public:
  LazyBig(int i) : ptr_(new Big(i) ) {}
  LazyBig(const LazyBig& b) : ptr_(b.ptr_) {}
  int get() const { return ptr_->get(); }
  void set(int i) {
    if(ptr_.unique() ) { ptr_->set(i); }
    else { ptr_ = PBig(new Big(i) ); }
  }
private:
  typedef shared_ptr<Big> PBig;
  PBig ptr_;
};

void zad3() {
    cout << "create first object" << endl;
    LazyBig f(1);
    cout << "create second object" << endl;
    LazyBig f2(f); //nie tworzy kosztownej kopii obiektu
    cout << "access to object" << endl;
    int x = f2.get(); //nie tworzy kosztownej kopii obiektu
    cout << "changing the object" << endl;
    f.set(3); //teraz tworzy kopię
    assert( f2.get() == x );
    cout << "changing the object" << endl;
    f2.set(4); //nie tworzy kosztownej kopii obiektu
}

/**-------------------------------------------------------------*
ZAD4: adapter dla operacji jednoargumentowej w kalkulatorze ONP
*--------------------------------------------------------------*/
typedef double Number;
typedef std::stack<Number> Stack;

//operacja dla kalkulatora w odwrotnej notacji polskiej
class Operation {
public:
    Operation(Stack& st) : stack_(st) {}
    //wykonanie operacji
    void operator()() {
        Number x = stack_.top();
        stack_.pop();
        stack_.push( doCalculate(x) );
    };
private:
    virtual Number doCalculate(Number) = 0;
    Stack& stack_;
};

//zmiana znaku
class SignChange : public Operation {
public:
    SignChange(Stack& st) : Operation(st) {}
private:
    //specyficzna operacja, dodawanie
    virtual Number doCalculate(Number x) {
        return -x;
    }
};

//dodawanie
class Add : public Operation {
public:
    Add(Stack& st) : Operation(st), stack_(st) {}
private:
    //specyficzna operacja, dodawanie
    virtual Number doCalculate(Number x) {
        Number y = stack_.top();
        stack_.pop();
        return x + y;
    }
    Stack& stack_;
};

//test
void zad4() {
    Stack s;
    s.push(1);
    s.push(2);
    Add add(s);
    add();
    std::cout << s.top() << std::flush;
}

/**-------------------------------------------------------------*
 * ZAD 5 uzycie wizytatora                                      *
 *--------------------------------------------------------------*/

class Elf;
class Dwarf;
class Troll;

class Visitor {
public:
    virtual void visit(const Elf&) = 0;
    virtual void visit(const Dwarf&) = 0;
    virtual void visit(const Troll&) = 0;
};

class Creature {
public:
    virtual void accept(Visitor& v) const = 0;
    virtual ~Creature() throw() {}
};
typedef shared_ptr<Creature> PCreature;

enum class ATTITUDE { CONFEDERATE, ENEMY };

class Elf : public Creature {
public:
    Elf() {}
    virtual void accept(Visitor& v) const { v.visit(*this); }
    int getPower() const { return 5; }
    ATTITUDE getAttitude() const { return ATTITUDE::CONFEDERATE; }
};

class Dwarf : public Creature {
public:
    Dwarf() {}
    virtual void accept(Visitor& v) const { v.visit(*this); }
    int getPower() const { return 3; }
};

class Troll : public Creature {
public:
    Troll() {}
    virtual void accept(Visitor& v) const { v.visit(*this); }
    int getPower() const { return 10; }
};

int countBonus(const vector<PCreature>& v) {
    struct BonusCalculator : public Visitor {
        BonusCalculator() : bonus_(0) {}

        virtual void visit(const Elf& e) {
            if(e.getAttitude() == ATTITUDE::ENEMY) {
                bonus_ += e.getPower();
            } else {
                bonus_ -= 2*e.getPower();
            }
        }
        virtual void visit(const Dwarf& d) {
            bonus_ -= 3*d.getPower();
        }
        virtual void visit(const Troll& e) {
            bonus_ += e.getPower();
        }
        int bonus_;
    } calc;

    for(PCreature c : v ) {
        c->accept(calc);
    }
    return calc.bonus_;
}


void zad5() {
    vector<PCreature> v;
    v.push_back(PCreature(new Elf()));
    v.push_back(PCreature(new Dwarf()));
    v.push_back(PCreature(new Troll()));
    v.push_back(PCreature(new Troll()));

    cout << countBonus(v) << endl;
}

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