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

using namespace std;

/**-------------------------------------------------------------*
   ZAD1 lista ze sprytnymi wskaźnikami, niepoprawne zwalnianie
   *--------------------------------------------------------------*/

class List {
    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_;
    //     //PNode next_, next2_, prev_;
    //     PNode next_, next2_;
    //     PWNode prev_;
    // };
public:
    List() {}
    ~List() {}

    void push_front(int val);
    void debug();
private:
    PNode head_;
    PNode head2_;
};

struct List::Node {
	Node(int v = 0) : val_(v)
	{
		cout << "c-tor:" << val_ << endl;
	}
	~Node() {
		cout << "d-tor:" << val_ << endl;
	}
	int val_;
	//PNode next_, next2_, prev_;
	PNode next_, next2_;
	PWNode prev_;
};

void List::push_front(int val) {
	PNode n(new Node(val));
	n->next_ = head_;
	n->next2_ = head2_;
	if(head_)
		head_->prev_ = n;
	head2_ = head_;
	head_ = n;
}

void List::debug() {
	for(PNode n = head_; n ; n = n->next_)
		cout << n->val_ << "(" << n.use_count() - 1 << "),";
	cout << endl;
}

void zad1() {
    List l;
    l.push_front(3);
    l.push_front(2);
    l.push_front(1);
    l.debug();
}

/**-------------------------------------------------------------*
 * TODO 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 auto_ptr<F> PF;

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


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(pb);
        } catch(E& e){
            cout << e.i_;
        }
        cout << endl;
    }
}

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

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

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

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

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


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

/**-------------------------------------------------------------*
   ZAD4 wzorzec projektowy komenda
   *--------------------------------------------------------------*/
class LightSwitch {
public:
    void lightOn(){ cout << "light on" << endl;}
    void lightOff(){ cout << "light off" << endl;}
};

class FanSwitch {
public:
    void turnUp(){ cout << "fan turn up" << endl; }
    void turnDown(){ cout << "fan turn down" << endl;}
};

class Command {
public:
    virtual void execute() = 0;
};

typedef shared_ptr<Command> PCommand;

class History {
public:
    vector<PCommand> getHistory() { return history_; }
    void add(PCommand cmd) { history_.push_back(cmd); }
private:
    vector<PCommand> history_;
};

typedef shared_ptr<History> PHistory;

class LightOnCommand : public Command {
public:
    LightOnCommand(LightSwitch& light) : light_(light) {}
    virtual void execute() {
        light_.lightOn();
    }
private:
    LightSwitch& light_;
};

class LightOffCommand : public Command {
public:
    LightOffCommand(LightSwitch& light) : light_(light) {}
    virtual void execute() {
        light_.lightOff();
    }
private:
    LightSwitch& light_;
};

class FanUpCommand : public Command {
public:
    FanUpCommand(FanSwitch& fan) : fan_(fan) {}
    virtual void execute() {
        fan_.turnDown();
    }
private:
    FanSwitch& fan_;
};

class FanDownCommand : public Command {
public:
    FanDownCommand(FanSwitch& fan) : fan_(fan) {}
    virtual void execute() {
        fan_.turnDown();
    }
private:
    FanSwitch& fan_;
};

class Button {
public:
    Button(PCommand cmd, PHistory history) : command_(cmd), history_(history) {}

    void click() {
        command_->execute();
        history_->add(command_);
    }
private:
    PCommand command_;
    PHistory history_;
};


void zad4() {
    PHistory history(new History);
    cout << "history size:" << history->getHistory().size() << endl;

    LightSwitch ls1;
    LightSwitch ls2;
    FanSwitch fs1;
    Button button1(PCommand(new LightOnCommand(ls1)), history);
    Button button2(PCommand(new LightOffCommand(ls1)), history);
    Button button3(PCommand(new LightOnCommand(ls2)), history);
    Button button4(PCommand(new LightOffCommand(ls2)), history);
    Button button5(PCommand(new FanUpCommand(fs1)), history);
    Button button6(PCommand(new FanDownCommand(fs1)), history);

    button1.click();
    button1.click();
    button2.click();

    cout << "history size:" << history->getHistory().size() << endl;
}

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

class Visitor;

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

class HourlyEmployee;
class MonthlyEmployee;

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

class HourlyEmployee : public Employee {
public:
    HourlyEmployee(int hours) : hours_(hours) {}
    int getHours() const { return hours_; }
    virtual void accept(Visitor& v) { v.visit(*this); }
private:
    int hours_;
};

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

class CountHours : public Visitor {
public:
    CountHours() : hours_(0) {}

    virtual void visit(HourlyEmployee& he) { hours_ += he.getHours(); }
    virtual void visit(MonthlyEmployee& me) { hours_ += 160; }

    int getHours() const { return hours_; }
private:
    int hours_;
};

int countHours(vector<Employee*> employee) {
	CountHours c;
	for(Employee* e : employee) {
		e->accept(c);
	}
	return c.getHours();
}

void zad5() {
    HourlyEmployee he1(12), he2(8);
    MonthlyEmployee me1;
	vector<Employee*> v;
	v.push_back(&he1);
 	v.push_back(&he2);
	v.push_back(&me1);
    cout << countHours(v) << endl;
}

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