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

using namespace std;

class Soldier;
class Commander;

class Visitor {
public:
    virtual void visit(const Soldier& s) = 0;
    virtual void visit(const Commander& c) = 0;
    virtual ~Visitor() {}
};

class Creature;
using PtrCreature = unique_ptr<Creature>;

//kompozyt i prototyp
class Creature {
public:
	Creature(const string& name) : name_(name) { cout << "c-tor " << name_ << endl; }
	virtual ~Creature() { cout << "d-tor " << name_ << endl; }

	const string& getName() const { return name_; }
	virtual PtrCreature clone() const = 0;
	virtual void accept(Visitor& v) const = 0;
	virtual void addSubordinate(PtrCreature) { }
private:
	string name_;
};

using VecCreature = vector<PtrCreature>;

//klasa prosta, wzorzec kompozyt
class Soldier : public Creature {
public:
	Soldier(const string& name) : Creature(name) {}
	Soldier(const Soldier& s) : Creature(s.getName()) {}

	PtrCreature clone() const override { return PtrCreature(new Soldier(*this)); }
	void accept(Visitor& v) const override { v.visit(*this); }

	void addSubordinate(PtrCreature) override { }
};

//klasa zlozona, wzorzec kompozyt
class Commander : public Creature {
public:
	Commander(const string& name) : Creature(name) {}
	Commander(const Commander& cmd) : Creature(cmd.getName()) {
		for(const PtrCreature& c : cmd.subordinates_ ) {
			subordinates_.push_back( c->clone() );
		}
	}

	PtrCreature clone() const override { return PtrCreature(new Commander(*this)); }
	void accept(Visitor& v) const override { v.visit(*this); }

	void addSubordinate(PtrCreature s) override { subordinates_.push_back(std::move(s)); }
	const VecCreature& getSubordinates() const { return subordinates_; }
private:
	VecCreature subordinates_; //podlegli
};


class CountSoldiers : public Visitor {
public:
	CountSoldiers() : num_(0) {}
	void visit(const Soldier&) override { ++num_; }
	void visit(const Commander& c) override {
		for(const PtrCreature& c : c.getSubordinates() ) {
			c->accept(*this);
		}
	}
	int getNoSoldiers() const { return num_; }
private:
	int num_;
};

int countSoldiers(const VecCreature& v) {
	CountSoldiers vis;
	for(const PtrCreature& c : v)
		c->accept(vis);
	return vis.getNoSoldiers();
}


// // stary - niepoprawny kod
// class Creature;
// using PtrCreature = unique_ptr<Creature>;

// class Creature {
// public:
//     virtual ~Creature() = 0;
//     virtual PtrCreature clone() const = 0;
// };

// class Soldier : public Creature {
// public:
//     Soldier(const string& name) : name_(name) {}
//     Soldier(const Soldier& s) : name_(s.getName()) {}
//     virtual ~Soldier() {}

//     virtual PtrCreature clone() const { return PtrCreature(new Soldier(*this)); }

//     const string& getName() const { return name_; }
// private:
//     string name_;
// };

// class Commander : public Creature {
// public:
//     Commander(const string& name) : name_(name) {}
//     Commander(const Commander& cmd) : name_(cmd.getName()) {
//         for(const PtrCreature& soldier : cmd.subordinates_ ) { //kopiuje osoby podlegle
//             subordinates_.push_back( soldier->clone() );
//         }
//     }
//     virtual ~Commander() { }

//     virtual PtrCreature clone() const { return PtrCreature(new Commander(*this)); }

//     const string& getName() const { return name_; }

//     virtual void addSubordinate(const string& name) { subordinates_.push_back(PtrCreature(new Soldier(name))); }
//     const vector<PtrCreature>& getSubordinates() const { return subordinates_; }
// private:
//     string name_;
//     vector<PtrCreature> subordinates_; //podlegli
// };


int main() {
    PtrCreature c = PtrCreature(new Commander("Abacki"));
    c->addSubordinate(PtrCreature(new Soldier("Xsinski")));
    c->addSubordinate(PtrCreature(new Soldier("Ygrek")));
    PtrCreature b = PtrCreature(new Commander("Babacki"));
    b->addSubordinate(PtrCreature(new Soldier("Zet")));
    c->addSubordinate(move(b));
    PtrCreature c2 = PtrCreature(new Commander("Cabacki"));
    c2->addSubordinate(PtrCreature(new Soldier("Szwejk")));

    VecCreature v;
    v.push_back(move(c));
    v.push_back(move(c2));

    cout << countSoldiers(v) << endl;

    return 0;
}
