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

//1. Prosze umiescic swoje imie i nazwisko

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

//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 dwukierunkowa przechowuje liczby calokowite.
// Oprocz standardowych wskaznikow na element poprzedni i nastepny, element listy ma uchwyt na element nastepny po nastepnym.
// 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_;
        PElement prev_;
        PElement next2_;
    };
public:
    List() {}
    ~List() { }
    void push_front(int v) {
        PElement e = make_shared<Element>(v);
        if(head_ ) {
            e->next_ = head_;
            head_->prev_ = e;
            e->next2_ = head_->next_;
        }
        head_ = e;
    }
private:
    PElement head_;
};

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

//ZAD2, 6 pkt
//
// Bledy zwiazane z obsluga plikow sa reprezentowane przez klasy FileError, dla tej hierarchii jest dostepny wzorzec wizytatora.
// Obsluga bledow polega na wyswietleniu komunikatu, tak jak pokazano w pliku 'zad2'.
// Zmodyfikuj ta funkcje, aby obsluga bledu nie wymagala wielokrotnych instrukcji 'catch', ktore sa kosztowne.

class Visitor;

class FileError : public std::exception {
public:
    FileError(const string& name) : filename_(name) {}
    virtual void accept(Visitor& v)  = 0;
    const string& getName() const { return filename_; }
private:
    string filename_;
};

class FileReadError;
class FileWriteError;
class FileCreateError;
class FileRemoveError;

class Visitor {
public:
    virtual ~Visitor() {}
    virtual void visit(FileReadError&) = 0;
    virtual void visit(FileWriteError&) = 0;
    virtual void visit(FileCreateError&) = 0;
    virtual void visit(FileRemoveError&) = 0;
};

class FileReadError : public FileError {
public:
    FileReadError(const string& name) : FileError(name) {}
    virtual void accept(Visitor& v) { v.visit(*this); }
};

class FileWriteError : public FileError {
public:
    FileWriteError(const string& name) : FileError(name) {}
  virtual void accept(Visitor& v) { v.visit(*this); }
};

class FileCreateError : public FileError {
public:
    FileCreateError(const string& name) : FileError(name) {}
  virtual void accept(Visitor& v) { v.visit(*this); }
};

class FileRemoveError : public FileError {
public:
    FileRemoveError(const string& name) : FileError(name) {}
  virtual void accept(Visitor& v) { v.visit(*this); }
};

void zad2() {
  try {
    throw FileWriteError("test.txt");
    //kod ktory moze zglosic wyjatek z hierarchii Exception
  } catch(FileReadError& e) {
      cerr << "Error of reading file " << e.getName() << endl;
  } catch(FileWriteError& e) {
      cerr << "Error of writing file " << e.getName() << endl;
  } catch(FileCreateError& e) {
      cerr << "Can not create file " << e.getName() << endl;
  } catch(FileRemoveError& e) {
      cerr << "File " << e.getName() << "can not be removed" << endl;
  }
}


// ZAD3 3 pkt
// Usun niektore skladniki sumy w ostatniej linii funkcji 'zad3', aby wydrukowac liczbe liter Twojego imienia, ktore wpisales do stalej IMIE w 5 linii tego pliku

class B1 {
public:
    virtual int f() { return 0; }
    int g() { return 0; }
};

class B2 : public B1 {
public:
    virtual int f() { return 1; }
    int g() { return 1; }
};

class B3 : public B2 {
public:
    virtual int f() { return 2; }
    int g() { return 2; }
};

class B4 : public B3 {
public:
    virtual int f() { return 4; }
    int g() { return 4; }
};

void zad3() {
    B4 obj;
    B4* b4 = &obj;
    B3* b3 = &obj;
    B2* b2 = &obj;
    B1* b1 = &obj;

    int x1 = b4->f();
    int x2 = b4->g();
    int x3 = b3->f();
    int x4 = b3->g();
    int x5 = b2->f();
    int x6 = b2->g();
    int x7 = b1->f();
    int x8 = b1->g();


    cout << x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 << endl;
}


// ZAD4 5 pkt
// Uzupelnij metody w klasie Book.
// operator przypisania, operator przypisania przenoszacy, destruktor
// byc moze trzeba bedzie uzyc metody 'std::unique_ptr<T,D>::reset' opisanej m.in. na stronie https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
// Test w funkcji zad4()

class Image { //reprezentuje okladke
public:
    Image(const string&) { cout << "c-tor image" << endl; }
    Image(const Image&) { cout << "cc-tor image" << endl; }
    ~Image() { cout << "d-tor image" << endl; }
};

class Book {
public:
    Book(const string& name) : name_(name), image_(new Image(name) ) {  }
    Book(const Book& book) : name_(book.name_), image_(new Image(*image_)) {    }
    Book(Book&& book) : name_(book.name_), image_(std::move(book.image_)) { }

    Book& operator=(const Book& book) {
        return *this;
    }
    Book& operator=(Book&& book) {
        return *this;
    }
    ~Book() {
    }
private:
    string name_;
    unique_ptr<Image> image_;
};

Book f(Book b) {
    return b;
}

void zad4() {
    Book b1("a");
    Book b2 = b1;
    Book b3 = f(b1);
    b2 = b1;
    b3 = f(b1);
}


// ZAD5 7 pkt
//
// Ponizej pokazano fragment kodu gry w warcaby.
// Obiekt typu 'State' reprezentuje stan w grze, tzn. polozenie pionow, damek oraz informacja o tym, kto sie rusza.
// Dostarczamy trzech roznych wersji gry: warcaby wloskie, warcaby niemieckie i warcaby hiszpanskie.
// Wszystkie takie wersje uzywaja planszy 8x8, ale roznia sie mozliwoscia bicia pionem do tylu oraz mozliwoscia ruchu damki o wiele pol.
// Te modyfikacje wplywaja na implementacje metod 'nextMovementsXXX'.
// Dostarczamy takze trzy algorytmy dla gracza: algorytm losowy - wybiera jeden z dostepnych ruchow losowo, algorytm AI - wybiera jeden z dostepnych ruchow
// wykorzystujac algorytmy przeszukiwania drzewa gry oraz algorytm 'Human', ktory o propozycje ruchu pyta gracza.

// (1) Uprosc strukture klas, aby nie powielac kodu.
// (2) Jakiego obiektowego wzorca projektowego uzyles?


/** reprezentuje stan w grze w warcaby (polozenie pionow i informacja o tym, kto sie rusza */
struct State {};
using States = vector<State>;

class Player {
    virtual State nextMove(State s) = 0;
};

//dziewiec klas dla roznych wersji gry i roznych algorytmow znajdowania nastepnego ruchu

class ItalianAIPlayer : public Player {
public:
    ItalianAIPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class GermanAIPlayer : public Player {
public:
    GermanAIPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class SpanishAIPlayer : public Player {
public:
    SpanishAIPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};


class ItalianRandomPlayer : public Player {
public:
    ItalianRandomPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class GermanRandomPlayer : public Player {
public:
    GermanRandomPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class SpanishRandomPlayer : public Player {
public:
    SpanishRandomPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};


class ItalianHumanPlayer : public Player {
public:
    ItalianHumanPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class GermanHumanPlayer : public Player {
public:
    GermanHumanPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};

class SpanishHumanPlayer : public Player {
public:
    SpanishHumanPlayer();
    State nextMove(State s);
private:
    States nextMovementsItalian(State s);
};


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;
}
