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

using namespace std;

/*-------------------------------------------------------------------------------------*
 *                                                                                     *
 * ZAD1 - rozwiazanie                                                                  *
 *                                                                                     *
 * problem: odwrotne użycie słabych/silnych sprytnych wskaźników                   *
 *-------------------------------------------------------------------------------------*/

    struct Edge;
    using PEdge = shared_ptr<Edge>;
    using PWEdge = weak_ptr<Edge>;

    struct Vertex;
    using PVertex = shared_ptr<Vertex>;
    using PWVertex = weak_ptr<Vertex>;

    struct Edge {
        Edge(PVertex src, PVertex dest) : src_(src), dest_(dest) { cout << "Edge c-tor" << endl; }
        ~Edge() { cout << "Edge d-tor" << endl; }
        PWVertex src_; //PVertex src_;
        PWVertex dest_; //PVertex dest_;
    };

    struct Vertex {
        Vertex(int val) : val_(val) { cout << "Vertex " << val_ << " c-tor" << endl; }
        ~Vertex(){ cout << "Vertex " << val_ << " d-tor " << endl; }
        int val_;
        vector<PEdge> in_; //vector<PWEdge> in_;
        vector<PEdge> out_; //vector<PWEdge> out_;
    };

    PVertex newVertex(int val) {
        return PVertex(new Vertex(val) );
    }

    PEdge newEdge(PVertex src, PVertex dest) {
        PEdge edge(new Edge(src, dest) );
        src->out_.push_back(edge);
        dest->in_.push_back(edge);
        return edge;
    }

void zad1()  {
    PVertex v1 = newVertex(1);
    PVertex v2 = newVertex(2);
    PVertex v3 = newVertex(3);
    newEdge(v1, v2);
    newEdge(v1, v3);
    cout << v1->out_[0]->src_.lock()->val_ << endl;
    cout << v1->out_[0]->dest_.lock()->val_ << endl;
};


// -------------------------------------------------------------*
//  *   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+2) {}
};

class M :  public D1, public D2 {
public:
    M(int i) : D1(i), D2(i) {}
    int get() const { return D1::get() + D2::get(); }
};

string NAME = "XXXXX";

extern void g(int i);

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

void g(int i) {
    const int N = NAME.size() % 3;
    M m(N-i);
    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 - funkcja, ktora zwroci mediane
//
//--------------------------------------------------------------
int mediana(int a, int b, int c) {
    if(a > b) {
        if(b > c)
            return b;
        else if(a > c)
            return c;
        else
            return a;
    } else {
        if(b < c)
            return b;
        else if (a > c)
            return a;
        else
            return c;
    }
}

void zad3() {
    cout << mediana(1,2,3) << endl;
    cout << mediana(1,3,2) << endl;
    cout << mediana(2,1,3) << endl;
    cout << mediana(2,3,1) << endl;
    cout << mediana(3,1,2) << endl;
    cout << mediana(3,2,1) << endl;
}

// ---------------------------------------------------------------------------------*
//  * ZAD4 - uzycie wizytatora, wielometoda                                 *
//  *----------------------------------------------------------------------------------

struct Bitmap {};

class DescrPict;
class DescrText;

class Visitor {
public:
    virtual void visit(const DescrPict& c) = 0;
    virtual void visit (const DescrText& r) = 0;
};

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

class DescrPict : public Descr {
public:
    virtual void accept(Visitor& v) const {
        v.visit(*this);
    }
    const Bitmap& getPicture() const { return bitmap_; }
private:
    Bitmap bitmap_;
};

class DescrText : public Descr {
public:
    virtual void accept(Visitor& v) const {
        v.visit(*this);
    }
    string getText() const { return string(); }
};

double text2textSimilarity(const string& s1, const string& s2) {
    return 1.0;
}

double text2pictSimilarity(const string& s, const Bitmap& b) {
    return 0.5;
}

double pict2pictSimilarity(const Bitmap& b1, const Bitmap& b2) {
    return 0.9;
}

struct VisitorSimilarityPict : public Visitor {
    VisitorSimilarityPict(const DescrPict& dp) : out_(0.0), p_(dp) { }
    virtual void visit(const DescrPict& dp) {
        out_ = pict2pictSimilarity(dp.getPicture(), p_.getPicture());
    }
    virtual void visit(const DescrText& dt) {
        out_ = text2pictSimilarity(dt.getText(), p_.getPicture());
    }
    double out_;
    const DescrPict& p_;
};

struct VisitorSimilarityText : public Visitor {
    VisitorSimilarityText(const DescrText& dt) : out_(0.0), t_(dt) { }
    virtual void visit(const DescrPict& dp) {
        out_ = text2pictSimilarity(t_.getText(), dp.getPicture());
    }
    virtual void visit(const DescrText& dt) {
        out_ = text2textSimilarity(t_.getText(), dt.getText());
    }
    double out_;
    const DescrText& t_;
};


struct VisitorSimilarity : public Visitor {
    VisitorSimilarity(const Descr& d) : out_(0.0), d_(d) {}
    virtual void visit(const DescrPict& dp) {
        VisitorSimilarityPict v(dp);
        d_.accept(v);
        out_ = v.out_;
    }
    virtual void visit(const DescrText& dt) {
        VisitorSimilarityText v(dt);
        d_.accept(v);
        out_ = v.out_;
    }
    double out_;
    const Descr& d_;
};

double similarity(const Descr& d1, const Descr& d2 ) {
    VisitorSimilarity v(d2);
    d1.accept(v);
    return v.out_;
}

void zad4() {
    DescrText t;
    DescrPict p;
    assert( similarity(t,t) == 1.0 );
    assert( similarity(t,p) == 0.5 );
    assert( similarity(p,t) == 0.5 );
    assert( similarity(p,p) == 0.9 );

}




/**-------------------------------------------------------------*
 * ZAD1 - rozwiazanie                                           *
 * problem: makro-komenda (komenda złożona)                     *
 *                                                              *
 * rozwiazanie: wzorzec kompozycji                              *
 *--------------------------------------------------------------*/

class Cmd;
using PCmd = shared_ptr<Cmd>;

class Cmd {
public:
    virtual void operator()() = 0;
    virtual void add(PCmd c) {} //dodaje komendę do kolekcji
    virtual ~Cmd() {}
};

class MacroCmd : public Cmd {
public:
    MacroCmd(){}
    ~MacroCmd() {}
    void add(PCmd c) { commands_.push_back(c); }
    virtual void operator()() {
        for(Commands::iterator i = commands_.begin(); i != commands_.end(); ++i)
            (**i)();
    }
private:
    typedef vector<PCmd> Commands;
    Commands commands_;
};

class Cmd1 : public Cmd {
    virtual void operator()() {
        cout << "Cmd1" << endl;
    }
};

class Cmd2 : public Cmd {
    virtual void operator()() {
        cout << "Cmd2" << endl;
    }
};

void zad5() {
    PCmd m1(new MacroCmd);
    m1->add(PCmd(new Cmd1));
    m1->add(PCmd(new Cmd2));
    m1->add(PCmd(new Cmd2));
    PCmd m2(new MacroCmd);
    m2->add(m1);
    m2->add(m1);
    (*m2)();
}


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