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

#include <iostream>
#include <algorithm>
#include <tuple>
#include <thread>
#include <mutex>

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>

using namespace std;


struct Person : public tuple<string,int,int> {
    Person(const string& n, int a, int s) : tuple<string,int,int>(n,a,s) {}

    const string& getName() const { return get<0>(*this); }
    int getAge() const { return get<1>(*this); }
    int getSalary() const { return get<2>(*this); }
};

using Persons = vector<Person>;

string NAME = "Abacki";

//--------ZAD1 (4pkt)--------------------------------------------------------------------------
//
// napisz funkcje: cretePersons, getSalaries oraz operator do wyswietlania Persons.
// uzyj algorytmow, tam, gdzie jest to uzasadnione

// createPersons - tworzy kolekcje krotek; liczba krotek to minimum z welkosci 3 kontenerow (argumentow funkcji)
//
Persons createPersons(const vector<string>& names, const vector<int>& ages, const vector<int>& salaries) {
    vector<Person> out;

    return out;
};

// getSalaries - tworzy kolekcje reprezentujata pensje na podstawie kolekcji krotek
//
vector<int> getSalaries(const vector<Person>& persons) {
    vector<int> out(persons.size());

    return out;
}

// pisze na strumien kolekcje krotek w postaci nazwa przecinek spacja wiek przecinek spacja pensja srednik
// np: Abacki, 20, 1500; Babacki, 19, 1600;
ostream& operator<<(ostream& os, const vector<Person>& p) {

    return os;
}


void zad1() {
    vector<string> names = { "Abacki", "Babacki", "Cabacki" };
    vector<int> ages = { 20, 19, 22, 22};
    vector<int> salaries = { 1500, 1600, 1700 };

    vector<Person> persons = createPersons(names, ages, salaries );

    cout << persons << endl;

    assert( salaries == getSalaries(persons) );
}


//----------Pytanie 1 (1 pkt)-------------------------------------------------------
// Zakladamy poprawna implementacje funkcji createPersons i getSalaries.
// Czy dla wszystkich wartosci 'salaries' asercja w 'zad1' jest prawdziwa?
// Jezeli nie, to daj przyklad.

//Odpowiedz:


// //--------ZAD2 (5pkt)-----------------------------------------------------------------------
// Liczba w kodzie trójkowym jest reprezentowana przez tablice Triple
// zakladamy, ze tablica bedzie maksymalnie 20 cyfrowa
// wtedy konwersja na liczbe naturalna moze byc realizowana wzorem:
// int x = 3^19*a[19] + 3^18*a[18] + ... + 3*a[1] + a[0]
// 3^20 = 3486784401, da sie zapisac na na 'int'

template<size_t N> using Triple = array<int, N>;
static const int TRIPLE_SIZE = 20;

//dostarcz funkcje konwertujaca taki zapis trojkowy na wartosc calkowita
//maksymalnie punktowane rozwiazanie wykorzystuje metaprogramowanie, aby konwersja byla szybka podczas dzialania aplikacji


template<size_t N>
int triple2int(const Triple<N>& a) {
    static_assert( N < TRIPLE_SIZE );

}


void zad2() {
    Triple<1> t1 = {1}; //1
    Triple<3> t2 = {0,1,0}; //3
    Triple<3> t3 = {0,0,2}; //18; pamietaj, ze przy pisaniu zawartosci tablicy najpierw jest a[0], pozniej a[1]
    Triple<4> t4 = {2,0,0,1}; //29

    cout << triple2int(t1) << endl;
    cout << triple2int(t2) << endl;
    cout << triple2int(t3) << endl;
    cout << triple2int(t4) << endl;
}


//--------ZAD3 (5pkt)--------------------------------------------------------------------------
// Popraw kod, aby zlikwidowac wyscig
// Zaznacz zmienione linie (wstaw komentarz //zmiana)

class Singleton {
public:
    static Singleton& getInstance() {
        if(!instance_) {
            lock_guard<mutex> guard(mutex_);
            if(!instance_) {
                instance_ = new Singleton();
            }
        }
        return *instance_;
    }
    void message(const string& msg) {
        ++counter_;
        cout << counter_ << ' ' << msg << endl;
    }
private:
    Singleton() : counter_(0) { }
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
private:
    static mutex mutex_;
    static Singleton* instance_;
    int counter_;
};

Singleton* Singleton::instance_ = nullptr;
mutex Singleton::mutex_;

void thrd_fun(const string& msg) {
    static const int NUM = 30; //how many trays
    for(int i=0;i<NUM;++i)
        Singleton::getInstance().message(msg);
}

void zad3() {
    std::thread thrd1( bind(&thrd_fun, "A") );
    std::thread thrd2( bind(&thrd_fun, "B") );
    std::thread thrd3( bind(&thrd_fun, "C") );


    thrd1.join();
    thrd2.join();
    thrd3.join();
}

//--------ZAD4 5pkt--------------------------------------------------------------------------
// zmodyfikuj w taki sposób topologię grafu, aby wydrukować liczbę liter swojego nazwiska
// wskazówka - zmodyfikuj jedynie linie w których dostarczamy wektor krawędzi.
// wierzchołki mają identyfikatory całkowite od 0 do 19.

void zad4() {
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS>;
    using Edge = pair<int, int>;
    const vector<Edge> EDGES = { Edge(0,1), Edge(1,2) };   //wektor krawędzi
    Graph g(EDGES.begin(), EDGES.end(), 20 );               //liczba wierzchołków = 20

    int sum = 0;
    Graph::vertex_descriptor v = *vertices(g).first;
    while( out_degree(v, g) ) { //if has out edge
        ++sum;
        Graph::edge_descriptor e = *out_edges(v, g).first;
        v = target(e, g);
    }
    cout << sum << endl;
}

//--------ZAD5 3pkt--------------------------------------------------------------------------
// Co zwraca funkcja 'funkcja' pokazana nizej jezeli kolekcja 'words' nie jest pusta ?
// Dla pustej kolekcji funkcja zwraca 0
// Przyklad uzycia oraz poprawne wartosci w 'zad5'


// Odpowiedz: 'funkcja' zwraca
int funkcja(const std::vector<string>& words) {
    using Hist = map<size_t,size_t>;
    using Pair = Hist::value_type;
    Hist hist;
    for_each(words.begin(), words.end(), [&](const string& s){
                                             Hist::iterator it = hist.find(s.size());
                                             if( it != hist.end() )
                                                 ++(it->second);
                                             else
                                                 hist.insert( make_pair(s.size(), 1 ) );
                                         });
    if(! hist.empty() )
        return hist.rbegin()->second;
    else
        return 0;
}

void zad5() {
    cout << funkcja(vector<string>{"ALA", "A", "O", "ELA"}) << endl; //zwraca 2
    cout << funkcja(vector<string>{"ALA", "AL", "A", "L"}) << endl;  //zwraca 1
    cout << funkcja(vector<string>{}) << endl; //zwraca 0
}

// Pytanie 2 (1 pkt)
//Ile godzin w poświęciłeś na przedmiot ZPR (podaj jedną liczbę, która obejmuje obecność na wykładzie, naukę własną i projekt)


// SUMA =
// WYKLAD + NAUKA-WLASNA + KOLOKWIUM =
// PROJEKT =


// Pytanie 3 (1 pkt)
//Zaproponuj zagadnienie, które Twoim zdaniem warto byłoby omówić na przedmiocie ZPR



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