// dodać pytanie o czas spędzony nad projektem

#include <iostream>
#include <iomanip>

#include <memory>
#include <queue>
#include <string>
#include <vector>

#include <type_traits>
#include <algorithm>

#include <boost/variant.hpp>

#include <boost/ref.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/type_traits.hpp>

#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;

string NAME = "Abacki";

//--------ZAD1--------------------------------------------------------------------------
//wyjątek opuszcza wątek i wyścig!
class Thread {
public:
    Thread(const vector<string>& tasks) : tasks_(tasks) {}

    void operator()() {
        try {
            for( string s : tasks_ ) {
                int i = boost::lexical_cast<int>(s);
                cout << "do calculate: " << i << endl;
                // doCalculate(i);
                boost::this_thread::sleep_for( boost::chrono::milliseconds(200));
            }
        } catch(exception&) {
            cout << "error" << endl;
        }
    }
private:
    vector<string> tasks_;
};

void zad1() {
	using Vs = vector<string>;
    Thread t1(Vs({"101", "102", "103", "xxx"}) );
    Thread t2(Vs({"201", "20x", "202", "203"}) );

    try {
        boost::thread thrd1( ref(t1) );
        boost::thread thrd2( ref(t2) );
        thrd1.join();
        thrd2.join();
    } catch(exception&) {
        cout << "error" << endl;
    }
}

// //--------ZAD2--------------------------------------------------------------------------
template<typename T> class Vector {
public:
    typedef unsigned char BUF_ELEMENT;
    static const int BUFFER_SIZE = 100;
    static const int MAX_SIZE = BUFFER_SIZE / sizeof(T);

    Vector() : buffer_(new BUF_ELEMENT[BUFFER_SIZE]), size_(0) {  }
    ~Vector() {
        delete_impl(is_trivially_destructible<T>()); //wykorzystuje trejty
        delete [] buffer_;
    }
    void add(const T& t) {
        new (buffer_ + size_*sizeof(T) ) T(t);
        ++size_;
    }
    void print() const {
        for(int i=0;i<size_;++i) {
            T* t = reinterpret_cast<T*>(buffer_ + i*sizeof(T) );
            cout << i << ":" << *t << endl;
        }
    }
    void printBuf() const {
        for(int i=0;i<BUFFER_SIZE;++i) {
            cout << hex << setfill('0') << setw(2) << static_cast<unsigned int>(buffer_[i]) << " ";
        }
        cout << dec << endl;
    }
private:
    void delete_impl(true_type) {
        cout << "do nothing destructor" << flush;
    }
    void delete_impl(false_type) {
        cout << "loop delete destructor" << flush;
        for(int i=0;i<size_;++i) {
            T* t = reinterpret_cast<T*>(buffer_ + i*sizeof(T) );
            t->~T(); //wola destruktor
        }
    }
private:
    BUF_ELEMENT* buffer_;
    int size_;
};

struct Foo {
    Foo(int i) : i_(i) {}
    ~Foo() { cout << "dtor Foo(" << i_ << ");"; }
    bool operator==(const Foo& f) { return f.i_ == i_;}
    int i_;
};
ostream& operator<<(ostream& os, const Foo& f) { os << "Foo;"; return os; }

void zad2() {
    Vector<int> v;
    v.add(1);
    v.add(2);
    v.add(1);
    v.print();
    v.printBuf();

    Vector<Foo> v2;
    v2.add(Foo(1));
    v2.add(Foo(2));
    v2.print();
    v2.printBuf();
}

// //--------ZAD3--------------------------------------------------------------------------
// podaj wydruk

typedef boost::variant<double,string> V;

int count(const std::vector<V>& v) {

    class Vis : public boost::static_visitor<void> {
    public:
        Vis() : sum_(0) {}
        void operator()(const double& d){ sum_ += static_cast<int>(d); }
        void operator()(const std::string& s) {
            try {
                sum_ += boost::lexical_cast<int>(s);
            } catch(boost::bad_lexical_cast &) {
            }
        }
        int sum_;
    } vis;
    for_each(v.begin(), v.end(), [&](const V& v){apply_visitor(vis, v);});
    return vis.sum_;
}

void zad3() {
    std::vector<V> v;
    v.push_back(1);
    v.push_back("100");
    v.push_back(NAME);
    v.push_back(NAME + "1");
    v.push_back(NAME.size());
    cout << count(v) << flush;
}

void zad3x() {
    for(int i=3;i<15;++i) {
        NAME = std::string(i, 'x');
        cout << "name: " << NAME << ":";
        zad3();
        cout << endl;
    }
}


//--------ZAD4--------------------------------------------------------------------------
//zadanie: podaj wydruk

void zad4() {
    list<int> l;
    l.push_back(0);
    l.push_back(NAME.size()/7);
    l.push_back(NAME.size());
    l.push_back(0);
    transform(l.begin(), l.end(), l.begin(), [](int i){ return i + 1; });
    l.erase( remove(l.begin(), l.end(), 1 ), l.end());
    copy(l.begin(), l.end(), ostream_iterator<int>(cout, ",") );
}

void zad4x() {
    for(int i=3;i<15;++i) {
        NAME = std::string(i, 'x');
        cout << "name: " << NAME << ":";
        zad4();
        cout << endl;
    }
}



// //--------ZAD5--------------------------------------------------------------------------
// podaj liczbę występującą najczęściej
int most_common(const std::vector<int>& numbers) {
    map<int,int> histogram;
    transform(numbers.begin(), numbers.end(), inserter(histogram, histogram.begin()),
              [](int i){ return make_pair(i,0); });
    int max_occur = 0;
    for_each(numbers.begin(), numbers.end(),
             [&](int i){
                 auto pi = histogram.find(i);
                 ++(pi->second);
                 if(pi->second > max_occur)
                     max_occur = pi->second;
             });
    auto pmax = find_if(histogram.begin(), histogram.end(), [=](pair<int,int> v){ return v.second == max_occur; });
    if(pmax != histogram.end())
        return pmax->first;
    else
        return 0;
}


void zad5() {
    cout << most_common(vector<int>{1, 1, 2, 3});
    cout << most_common(vector<int>{1, 1, 2, 3, 2, 2});
    cout << most_common(vector<int>{});

}

int main() {
    cout << "zad1: "; zad1(); cout << endl;
    cout << "zad2: "; zad2(); cout << endl;
    cout << "zad3: "; zad3x(); cout << endl;
    cout << "zad4: "; zad4x(); cout << endl;
    cout << "zad5: "; zad5(); cout << endl;
    return 0;
}

