//Kolokwium ZPR, 2020.04.28 - 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

#include <string>
#include <stack>
#include <vector>
#include <list>
#include <iostream>
#include <iomanip>
#include <memory>
#include <cassert>

using namespace std;

// ZAD1, 4 pkt
//
// Ponizej przedstawiona lista jednokierunkowa przechowuje liczby calokowite.
// Kazdy element listy ma uchwyt do pierwszego elementu na liscie.
// Obserwujemy nieprawidlowe zwalnianie obiektow, przyklad - kod funkcji zad1().
// Popraw kod.

class List {

    struct Element;
    using PElement = shared_ptr<Element>;
    using PWElement = weak_ptr<Element>;

    struct Element {
        Element(int v) : val_(v) {  }
        ~Element() {  }
        int val_;
        PElement next_;
        PWElement head_;   //slaby wskaznik
    };
public:
    List() {}
    ~List() { }
    void push_back(int v) {
        PElement e(new Element(v));
        if(! head_ ) {
            head_ = e;
        } else {
            PElement prev = head_;
            while( prev->next_ )
                prev = prev->next_;
            prev->next_ = e;
        }
        e->head_ = head_;
    }
private:
    PElement head_;
};

void zad1() {
    List l;
    for(int i=0; i< 10; ++i)
        l.push_back(i);
}

//ZAD2, 7 pkt
//
// Pracownicy etatowi (Staff) oraz studenci (Student) są wynagradzani za całkowitą liczbę przepracowanych godzin w danym okresie, oznaczaną 'h'.
// Dla studenta wynagrodzenie to 13 PLN * h , PLN to Polski Zloty.
// Dla pracownika wynagrodzenia nie płaci się, gdy przepracował mniej niż 10 godzin, w przeciwnym przypadku wynagrodzenie to 15 PLN * h.
// Norma wynosi 100 godzin.
// Pracownik dostaje dodatkowo 5 PLN za każdą ponadnormatywa godzinę (oznacza to, ze za godzine 101 pracownik otrzyma 20 PLN).

// Uzupełnij funkcje 'countSalary'. Wynagrodzenie przechowujemy jako liczba calkowita w groszach.
// Przyklad uzycia to funkcja zad2().


class Visitor;

class Worker {
public:
    Worker(int hours) : hours_(hours) {}
    int getHours() const { return hours_; }

    virtual void accept(Visitor& v) const  = 0;
private:
    int hours_;
};

class Student;
class Staff;

class Visitor {
public:
    virtual ~Visitor() {}
    virtual void visit(const Student&) = 0;
    virtual void visit(const Staff&) = 0;
};

class Student : public Worker {
public:
    Student(int hours) : Worker(hours) {}
    virtual void accept(Visitor& v) const { v.visit(*this); }
};

class Staff : public Worker {
public:
    Staff(int hours) : Worker(hours) {}
    virtual void accept(Visitor& v) const { v.visit(*this); }
};

//zwraca zaplate w groszach, dlatego uzyto liczby calkowitej
using Salary = int;

Salary countSalary(const vector<Worker*> workers) {
    static const int STUDENT_PER_HOUR = 1300;
    static const int STAFF_PER_HOUR = 1500;
    static const int STAFF_BONUS_PER_HOUR = 500;

    struct SalaryCounter : public Visitor {
        SalaryCounter() : salary_(0) {}
        Salary salary_;

        virtual void visit(const Student& s) {
            salary_ += STUDENT_PER_HOUR*s.getHours();
        }
        virtual void visit(const Staff& s) {
            if(s.getHours() < 10)
                return;
            salary_ += STAFF_PER_HOUR * s.getHours();
            if(s.getHours() >= 100)
                salary_ += STAFF_BONUS_PER_HOUR * (s.getHours() - 100);
        }
    } vis;
    for(const Worker* w : workers)
        w->accept(vis);
    return vis.salary_;
}

void zad2() {
    Student s1(5), s2(50), s3(150);
    Staff s4(5), s5(50), s6(150);

    vector<Worker*> v = { &s1, &s2, &s3, &s4, &s5, &s6 };
    Salary salary = countSalary(v);
    cout << salary / 100 << ',' << setw(2) << setfill('0') << salary % 100 << endl;
}


// ZAD3 3 pkt
//
// Zmodyfikuj kod klas B, D1, D2, M, aby otrzymac wydruk, ktory jest Twoim imieniem.
// Uzywamy tablicy IMIE (5 linia w tym pliku).
// Zmiany w kodzie zrodlowym powinny byc najmniejsze z mozliwych
// stosujac odleglosc Levenshteina (odleglosc edycyjna) czyli liczbe dzialan prostych ktorymi sa (1) wstawienie znaku, (2) usuniecie znaku, (3) zamiana znaku
//  *--------------------------------------------------------------
class E : public std::exception { };

class B {
public:
    virtual char f(char c) { return 'U'+1+1+1; }  //tutaj zmiana
};

class D1 : virtual public B {
public:
    virtual char f(char c) { return 'B'; }
};
class D2 : virtual public B {
public:
    virtual char f(char c) { return 'X'; }
};

class M :  public D1, public D2 {
public:
    virtual char f(char c) { return c; }
};

void zad3() {
    string imie(IMIE);
    M m;
    cout << static_cast<D1*>(&m)->f(imie[0]) << static_cast<D2*>(&m)->f(imie[1]);
    B* b = &m;
    for(int i = 2; i < imie.size(); ++i ) {
        cout << b->f(imie[i]);
    }
}


// ZAD4 4 pkt
//
// Klasa Point ma 14 bledow. Popraw je.
// Test w funkcji zad4()


class Point {
public:
	explicit Point(int x = 0, int y = 0) : x_(x), y_(y) {} //poprawna inicjacja y_
	int getX() const { return x_; } //stalosc
	int getY() const { return y_; } //stalosc

	Point& operator+=(const Point& p) {
		//nie tworzy obiektu tymczasowego
		x_ += p.x_;
		y_ += p.y_; //poprawnie dodaje
		return *this;
	}
	Point operator+(const Point& p) const { //nie zwraca referencji na tymczasowy obiekt
		Point o(*this);
		o += p;
		return o;
	}
	bool operator==(const Point& p) const { return x_ == p.x_ && y_ == p.y_; } //(1)zwraca bool, (2)skladowa przez referencje, (3)poprawiony warunek logiczny
private:
	int x_, y_;
};
std::ostream& operator<<(std::ostream& os, const Point& p) { //argument przez referencje
	os << "(" << p.getX() << ", " << p.getY() << ")";
	return os; //zwraca strumien
}

void zad4() {
	Point w1(1,1), w2(2);
	cout << w1 + w2 << endl; //powinno byc (3, 1)
	assert( w1 + w2 == Point(3,1) );
}


// ZAD5 7 pkt
//
// Ponizej pokazano fragment kodu edytora tekstu, obiekt Text reprezentuje fragment tekstu, zas Enumerate reprezentuje wyliczenie.
// (1) Prosze zmodyfikowac ten kod, aby wyliczenie moglo miec wyliczenie podrzedne
// (2) Prosze zapewnic, aby konstruktor kopiujacy tworzyl gleboka kopie obiektu

class Element;
using PElement = shared_ptr<Element>;

class Element {
public:
    Element() {}
    virtual ~Element(){}
    virtual void add(PElement) = 0;
    virtual PElement clone() = 0;
    virtual void print(std::ostream& os, const string& prefix) = 0;
};

class Text : public Element {
public:
    Text(const string& s) : s_(s) {}
    virtual void add(PElement) {}
    virtual PElement clone() { return PElement(new Text(*this)); }
    virtual void print(std::ostream& os, const string& prefix) { os << prefix << s_ << endl; }
private:
    string s_;
};

class Enumerate : public Element {
public:
    Enumerate() {}
    Enumerate(const Enumerate& en) {
        for(PElement e : en.ch_ ) {
            ch_.push_back( e->clone() );
        }
    }

    void add(PElement el) { ch_.push_back(el); }
    virtual PElement clone() { return PElement(new Enumerate(*this)); }
    virtual void print(std::ostream& os, const string& prefix) {
        int i = 0;
        for(PElement e : ch_) {
            string new_prefix = prefix + to_string(++i) + " ";
            e->print(os, new_prefix);
        }
    }
private:
	std::vector<PElement> ch_;
};

void zad5() {
    PElement e(new Enumerate);
    e->add(PElement(new Text("one")));
    PElement e2(new Enumerate);
    e2->add(PElement(new Text("two one")));
    e2->add(PElement(new Text("two two")));
    e->add(e2);
    e->add(PElement(new Text("three")));

    e->add(e->clone());

    e->print(cout, "");
}

int main() {
    cout << "Kolokwium ZPR, rozwiazania " << IMIE << " " << NAZWISKO << endl;
	cout << "zad1"<<endl;
	zad1();
	cout << "zad2"<<endl;
	zad2();
	cout << "zad3"<<endl;
	zad3();
	cout << "zad4"<<endl;
	zad4();
	cout << "zad5"<<endl;
	zad5();
	cout << endl;
	return 0;
}
