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

#include <iostream>
#include <iomanip>

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

#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/concept_check.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!
using V = boost::variant<int,string>;
using VV = vector<V>;

class Thread {
public:
    Thread(const VV& tasks) : tasks_(tasks) {}

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

void zad1() {
    VV vec;
    vec.push_back(NAME.size());
    vec.push_back(1);
    vec.push_back("5");
    vec.push_back(NAME);
    Thread t1(vec);
    Thread t2(vec);

    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--------------------------------------------------------------------------
//zadanie: podaj wydruk


template <unsigned n> int fun(int k) {
    return n - fun<n-1>(k);
}
template <> int fun<1>(int k) {
    return k;
}

template <> int fun<0>(int k) {
    return 0;
}


void zad3() {
    cout << fun<0>(NAME.size()) << " " << fun<2>(NAME.size()) << " " << fun<5>(NAME.size()) << flush;
}

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


// //--------ZAD4--------------------------------------------------------------------------
// podaj liczbę występującą w kolekcji najmniejszą liczbę razy
int less_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); });
    for_each(numbers.begin(), numbers.end(), [&](int i){ ++histogram[i];});
    int min_occur = std::numeric_limits<int>::max();
    for( auto pi : histogram ) {
        if(pi.second < min_occur) {
            min_occur = pi.second;
        }
    }
    auto pmin = find_if(histogram.begin(), histogram.end(), [=](pair<int,int> v){ return v.second == min_occur; });
    if(pmin != histogram.end())
        return pmin->first;
    else
        return 0;
}


void zad4() {
    cout << less_common(vector<int>{1, 1, 2, 3});
    cout << less_common(vector<int>{1, 1, 2, 3, 2, 2});
    cout << less_common(vector<int>{});

}

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

