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

using namespace std;
using namespace boost;

/**-------------------------------------------------------------*
   ZAD1 macierz ze sprytnymi wskaźnikami, niepoprawne zwalnianie
   TODO: up, down, left, right
   *--------------------------------------------------------------*/

class Matrix {
    struct Node;
    typedef std::shared_ptr<Node> PNode;
    typedef std::weak_ptr<Node> PWNode;
    struct Node {
        Node(int v = 0) : val_(v)
        {
            cout << "c-tor:" << val_ << endl;
        }
        ~Node() {
            cout << "d-tor:" << val_ << endl;
        }
        int val_;
        PWNode up_, left_;
        PNode down_, right_;
        //PNode up_, down_, left_, right_;
    };
public:
    Matrix(int size)
    {
		std::vector<std::vector<PNode> > tmp; //tablica pomocnicza

        int idx = 0;
        for(int i=0; i<size;++i) {
			tmp.push_back(std::vector<PNode>() );
            for(int j=0;j<size;++j)
				tmp[i].push_back(PNode(new Node(++idx)));
		}

        for(int i=0; i<size;++i)
            for(int j=0;j<size;++j) {
                PNode n = tmp[i][j];
                if(i>0) {
                    n->up_ = tmp[i-1][j];
                }
                if(i<size-1) {
                    n->down_ = tmp[i+1][j];
                }
                if(j>0) {
                    n->left_ = tmp[i][j-1];
                }
                if(j<size-1) {
                    n->right_ = tmp[i][j+1];
                }
            }
        node_ = tmp[0][0];
    }
    ~Matrix() {}

    void debug() {
        for(PWNode r = node_; r.lock() ; r = r.lock()->down_)
            for(PWNode c = r; c.lock() ; c = c.lock()->right_) {
                cout << c.lock()->val_ << "(" << c.lock().use_count() - 1 << "),";
                cout << endl;
            }
    }
private:
    PNode node_;
};

void zad1() {
    Matrix m(3);
    m.debug();
}

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

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;

F f(F&& b) {
    //cout << "f(F&&" << b.i_ << ")" << endl;
    if (b.i_ < 10) {
        throw E(b.i_);
    }
    return F(b.i_-2);
}

F f(F& b) {
    //cout << "f(F&" << b.i_ << ")" << endl;
    if(b.i_ > 2) {
        return f(F(b.i_ / 2));
    }
    return F(b.i_-2);
}


int LICZBA_LITER_NAZWISKA = 5;

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

/**-------------------------------------------------------------*
 * ZAD3 - dziedziczenie wielobazowe                             *
 *--------------------------------------------------------------*/

class B {
    int i_;
public:
    B(int i) : i_(i) {}
    void inc() { ++i_; }
    int get() { return i_; }
};

class D1 : virtual public B {
public:
    D1(int i) : B(i-1) {}
    void inc() { B::inc(); B::inc(); }
};

class D2 : virtual public B {
public:
    D2(int i) : B(i-2) {}
    void inc() { B::inc(); }
};

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


void zad3() {
    M m(3);
    static_cast<D1&>(m).inc();
    cout << m.get() << endl;
}



/**-------------------------------------------------------------*
   ZAD4 wzorzec projektowy kompozyt, produkty
   *--------------------------------------------------------------*/
class Product {
public:
    ~Product() {}
    virtual int getPrice() const = 0;
};

class ProfitConstant : public Product {
public:
    ProfitConstant(int cost, int profit) : cost_(cost), profit_(profit) {}

    virtual int getPrice() const {
        return cost_ + profit_;
    }
private:
    int cost_;
    int profit_;
};

class ProfitPercent : public Product {
public:
    ProfitPercent(int cost, int profit) : cost_(cost), profit_(profit) {}

    virtual int getPrice() const {
        return cost_ + cost_ * profit_ / 100;
    }
private:
    int cost_;
    int profit_;
};

class Composite : public Product {
public:
    Composite(int profit) : profit_(profit) {}

    void add(Product* p) {
        products_.push_back(p);
    }

    virtual int getPrice() const {
        int price = 0;
        for(Product* p : products_) {
            price += p->getPrice();
        }
        return price + profit_;
    }
private:
    int profit_;
    std::vector<Product*> products_;
};

void zad4() {
    ProfitConstant p1(100,1);
    ProfitPercent p2(200,5);
    Composite p3(10);
    p3.add(&p1);
    p3.add(&p2);
    cout << p3.getPrice() << endl;
}

/**-------------------------------------------------------------*
   ZAD5 wyjątki, obsługa przez wizytator różnych błędów
   *--------------------------------------------------------------*/

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 Visitor {
public:
    Counters() : networkErrors_(0), userBreaks_(0), badData_(0) {}

    virtual void visit(NetworkError&) { ++networkErrors_; }
    virtual void visit(UserBreak&) { ++userBreaks_; }
    virtual void visit(BadData&) { ++badData_; }

    int networkErrors_;
    int userBreaks_;
    int badData_;
};

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

Counters counters;

void updateCounters(Counters& counters, BaseException& ex) {
    ex.accept(counters);
}

void zad5() {
    try {
        throw UserBreak();
        //kod ktory moze zglosic wyjatek typu BaseException
    } catch(BaseException& e) {
        updateCounters(counters, e);
    }
    cout << counters;
}

// // /**-------------------------------------------------------------*
// //  * TODO ZAD6 - stałość                                     *
// //  *--------------------------------------------------------------*/

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