#include <iostream>

using namespace std;

/**-------------------------------------------------------------*
 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:
    static Counters& getInstance() { //ta metoda jest poprawna!
        static Counters instance;  //obiekt lokalny statyczny, rodzaj singletona w C++
        return instance;
    }
    int getNetworkError() const { return networkError_; }
    int getUserBreak() const { return userBreak_; }
    int getBadData() const { return badData_; }

    void incNetworkError() { ++networkError_; }
    void incUserBreak() { ++userBreak_; }
    void incBadData() {++badData_; }

    virtual void visit(NetworkError&) { ++networkError_; }
    virtual void visit(UserBreak&) { ++userBreak_; }
    virtual void visit(BadData&) { ++badData_; }
private:
    Counters() : networkError_(0), userBreak_(0), badData_(0) {}

    int networkError_;
    int userBreak_;
    int badData_;
};

ostream& operator<<(ostream& os, const Counters& c) {
    os << "NetworkErrors:" << c.getNetworkError() << ',';
    os << " UserBreaks:" << c.getUserBreak() << ',';
    os << " BadData:" << c.getBadData() << endl;
    return os;
}

int main() {
    try {
        throw UserBreak();
        //kod ktory moze zglosic wyjatek typu BaseException
    }
    catch(NetworkError& e) {
        Counters::getInstance().incNetworkError();
    }
    catch(UserBreak& e) {
        Counters::getInstance().incUserBreak();
    }
    catch(BadData& e) {
        Counters::getInstance().incBadData();
    }
    catch(BaseException& e) {
        e.accept(Counters::getInstance() );
    }
    cout << Counters::getInstance();
    return 0;
}

