//Kolokwium ZPR, 2021.01.19 - sa tutaj 2 polecenia ogolne (linia 3 i linia 8)

//1. Prosze umiescic swoje imie i nazwisko

const char* IMIE = "ROBERT";
const char* NAZWISKO = "NOWAK";

//2. Prosze oznaczyc linie, ktore zostaly zmodyfikowane dodajac krotki komentarz

//3. Proszę zmienić nazwę pliku na zpr_nazwisko.cpp, gdzie nazwisko nie uzywa polskich znaków i proszę plik załadować do MS Teams


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

#include <iostream>
#include <algorithm>
#include <tuple>
#include <optional>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/chrono.hpp>

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

using namespace std;

string NAME = "Abacki";

//--------ZAD1 (5pkt)--------------------------------------------------------------------------
// Uzupełnij implementację klasy Persons, napisz metody: findByName, findByAge, findBySalary oraz sumSalary.
// Uzyj algorytmow z biblioteki standardowej.
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); }
};

class Persons {
public:
    //c-tor, tworzy kolekcje krotek; liczba krotek to minimum z welkosci 3 kontenerow (argumentow funkcji)
    Persons(const vector<string>& names, const vector<int>& ages, const vector<int>& salaries);

    Persons(const Persons& ) = default;
    Persons& operator=(const Persons& ) = default;
    ~Persons() = default;

    //zwraca rekord dla  danego nazwiska, do zrobienia
    optional<Person> findByName(const string& name) const;
    //zwraca rekord dla danego wieku, do zrobienia
    optional<Person> findByAge(int age) const;
    //zwraca sumę zarobków, do zrobienia
    int sumSalary() const;
private:
    vector<Person> persons_;
};

// createPersons - tworzy kolekcje krotek; liczba krotek to minimum z welkosci 3 kontenerow (argumentow funkcji)
Persons::Persons(const vector<string>& names, const vector<int>& ages, const vector<int>& salaries) {
    size_t size = min( min(names.size(), ages.size() ), salaries.size() );
    persons_.reserve(size);
    for(int i = 0; i < size; ++i ) {
        persons_.push_back( Person(names[i], ages[i], salaries[i]) );
    }
};


//zwraca rekord dla  danego nazwiska, do zrobienia
optional<Person> Persons::findByName(const string& name) const {
}

//zwraca rekord dla danego wieku, do zrobienia
optional<Person> Persons::findByAge(int age) const {
}

//zwraca sumę zarobków, do zrobienia
int Persons::sumSalary() const {
    int sum = 0;
    for_each( persons_.begin(), persons_.end(), [&](const Person& p) { sum += p.getSalary(); } );
    return sum;
}

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

    Persons p(names, ages, salaries );
    if( p.findByName("Abacki") )
        cout << "jest Abacki" << endl;
    if( p.findByName("Dabacki") )
        cout << "jest Dabacki" << endl;
    cout << p.sumSalary() << endl;
}


 //--------ZAD2--------------------------------------------------------------------------
//Klasa List przechowuje elementy w buforze wskazywanym przez buffer_. Popraw destruktor tej klasy,
//aby dla typów T, które mają trywialny destruktor zrezygonować z pętli, bo wtedy wystarczy usunąć bufor.
// Możesz wykorzystać trejt std::is_trivially_destructible niżej.

//If a type has a trivial destructor then the destructor has no effect: calls to the destructor can be safely omitted.
// template< class T > struct is_trivially_destructible;
// If T has a trivial destrutor then 'is_trivially_destructible<T>::value' is constant member equal to 'true',
// if T has a trivial destrutor then 'is_trivially_destructible<T>::type' is 'true_type',
// if T does not have a trivial destrutor then 'is_trivially_destructible<T>::value' is constant member equal to 'false'.
// if T does not have a trivial destrutor then 'is_trivially_destructible<T>::type' is 'false_type'.

template<typename T> struct Node {
    Node(const T& v) : value_(v), next_(0L) {}
    T value_;
    Node* next_;
};

template<typename T> class List {
public:
    using BUFFER_ELEMENT = byte;
    static const int BUFFER_SIZE = 100;
    List() : buffer_(new BUFFER_ELEMENT[BUFFER_SIZE]), current_(buffer_), head_(nullptr) {  }

    //tylko ta metoda ma byc poprawiona

    ~List() {
        while( head_ ) {
            Node<T>* next = head_->next_;
            head_->value_.~T(); //wola destruktor
            head_ = next;
        }
        delete [] buffer_;
    }
    void insert(const T& t) {
        Node<T>* node = new(current_) Node<T>(t);
        node->next_ = head_;
        head_ = node;
        current_ += sizeof(Node<T>);
    }

    void print() const {
        Node<T>* node = head_;
        for(int n = 0; node != 0L; ++n) {
            cout << n << ':' << node->value_ << ',';
            node = node->next_;
        }
        cout << endl;
    }

    // ~List() { //wykorzystuje trejty
    //     delete_impl(boost::has_trivial_destructor<T>());
    //     delete [] buffer_;
    // }
private:
    // void delete_impl(boost::true_type) {
    //     cout << "do nothing destructor" << flush;
    // }
    // void delete_impl(boost::false_type) {
    //     cout << "loop delete destructor" << flush;
    //     while( head_ ) {
    //         Node<T>* next = head_->next_;
    //         head_->value_.~T(); //wola destruktor
    //         head_ = next;
    //     }
    // }
private:
    BUFFER_ELEMENT* buffer_;
    BUFFER_ELEMENT* current_;
    Node<T>* head_;
};

class Foo {
public:
    Foo(int i) : i_(i) {}
    ~Foo() { cout << "d-tor Foo " << i_ << endl; }
    int i_;
};
std::ostream& operator<<(std::ostream& o, const Foo& f) {
    o << "Foo:" << f.i_ << ";";
    return o;
}

void zad2() {
    List<int> l;
    l.insert(1);
    l.insert(2);
    l.print();
    List<Foo> l2;
    l2.insert(Foo(1));
    l2.insert(Foo(2));
    l2.print();
}

//--------ZAD3--------------------------------------------------------------------------
// W systemie istnieje jeden obiekt typu Queue, do którego wstawia dane jeden wątek (kodu nie zamieszczono). Ilość danych w kolejce
// jest duża i rośnie. Aby temu zapobiec system przeniesiono na platformę wieloprocesorową  i zwiększono ilość wątków przetwarzających
// dane (wykonują one funcję calc_thrd). Niestety nie obserwuje się poprawy. Popraw kod funkcji calc_thrd.

class Data {}; //nie zamieszczono skladowych
typedef std::shared_ptr<Data> PData;

class Queue {
public:
    Queue() {}
    bool empty() const {
        boost::mutex::scoped_lock scoped_lock(m_);
        return q_.empty();
    }
    bool empty_impl() { return q_.empty(); }
    PData front() { //uchwyt do pierwszego elementu
        return q_.front();
    }
    void pop() { //usuwa pierwszy element z kolejki
        q_.pop();
    }
    mutable boost::mutex m_;
    //nie zamieszczono metod do wstawiania elementow
private:
    queue<PData> q_;
public:
    void push(PData data) {
        boost::mutex::scoped_lock scoped_lock(m_);
        q_.push(data);
    }
};

void calculate(const Data& d) { //dlugotrwale obliczenia
    boost::this_thread::sleep_for( boost::chrono::milliseconds(500) );
    cout << "." << flush;
}

void calc_thrd(Queue& q) {
    while(!q.empty()) {
        boost::mutex::scoped_lock l(q.m_);
        calculate(*q.front());
        q.pop();
    }
}

void calc_thrd_better(Queue& q) {
    while(!q.empty()) {
        PData data;
        {
            boost::mutex::scoped_lock l(q.m_);
            if(!q.empty_impl()) {
                data = q.front();
                q.pop();
            }
        }
        //obliczenia poza sekcja krytyczna
        if(data)
            calculate(*data);
    }
}

void zad3() {
     Queue q;
     //symulacja wstawiania do kolejki (pominięto watki dodajace dane)
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));
     q.push(PData(new Data()));

     //tutaj wątki, które pobierają i przetwarzają obiekty PData z kolejki
     boost::thread_group thrds;
     for (int i=0; i < 5; ++i)
         thrds.create_thread(bind(&calc_thrd_better, ref(q)));

     thrds.join_all();
     cout << endl;
}


//--------ZAD4 5pkt--------------------------------------------------------------------------
// utwórz ścieżkę w grafie, aby poniższa funkcja wydrukowała liczbę liter Twojego nazwiska.
// wskazówka - zmodyfikuj jedynie linie w których dostarczamy wektor krawędzi.


void zad4() {
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, int>;

    // wierzchołki mają identyfikatory całkowite od 0 do 4. W wierzchołku jest przechowywana dodatkowa liczba całkowita.
    using Edge = pair<int, int>;
    const vector<Edge> EDGES = { Edge(0,1), Edge(1,2), Edge(2,3), Edge(3,4) };   //zmiana - dla nazwiska o długości 15
    Graph g(EDGES.begin(), EDGES.end(), 5 );               //liczba wierzchołków = 5

    g[0]=0; //wartosci przechowywane w kolejnych wierzcholkach
    g[1]=1;
    g[2]=2;
    g[3]=4;
    g[4]=8;

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

//--------ZAD5 3pkt--------------------------------------------------------------------------
//napisz funcję badającą, czy dwa napisy to anagramy. Anagram to napis utworzony przez przestawianie liter, np. wyrazy KOT i KTO są anagramami.
// Być może będzie przydatny algorytm std::sort, sortuje elementy kolekcji
// template< class Iterator> void sort( Iterator first, Iterator last );

bool anagram(const string& str1, const string& str2) {
    std::string a(str1), b(str2);
    std::sort(a.begin(), a.end() );
    std::sort(b.begin(), b.end() );
    return a == b;
}

void zad5() {
    cout << anagram("kot", "tok") << endl;
    cout << anagram("kot", "kto") << endl;
    cout << anagram("kota", "kot") << endl;
    cout << anagram("kota", "kkot") << endl;
}


// Pytanie 1 (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 2 (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;
}
