
#include <cstdlib>
#include <exception>
#include <iostream>
#include <string>

#define main aplikacja_main
#include "main.cpp"
#undef main

using namespace std;

class TestException : public exception {
private:
    string message;

public:
    explicit TestException(string message) : message(std::move(message)) {}

    const char* what() const noexcept override {
        return message.c_str();
    }
};

class KoscTest {
private:
    int liczbaTestowZaliczonych = 0;
    int liczbaTestowNiezaliczonych = 0;

public:
    int uruchom() {
        uruchomTest(
            "rzut zwraca wartosc od 1 do 6",
            []() { testRzutZwracaWartoscOd1Do6(); }
        );
        uruchomTest(
            "rzut nie zmienia wartosci, gdy kosc nie jest dostepna",
            []() { testRzutNieZmieniaWartosciGdyKoscNieJestDostepna(); }
        );

        cout << endl;
        cout << "Wynik: "
             << liczbaTestowZaliczonych << " zaliczone, "
             << liczbaTestowNiezaliczonych << " niezaliczone." << endl;

        return liczbaTestowNiezaliczonych == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
    }

private:
    static void testRzutZwracaWartoscOd1Do6() {
        Kosc kosc(1);

        for (int i = 0; i < 100; i++) {
            kosc.rzut();

            int liczbaOczek = kosc.getLiczbaOczek();

            sprawdzWiekszeLubRowne(
                liczbaOczek,
                1,
                "liczba oczek po rzucie powinna byc wieksza lub rowna 1"
            );
            sprawdzMniejszeLubRowne(
                liczbaOczek,
                6,
                "liczba oczek po rzucie powinna byc mniejsza lub rowna 6"
            );
            sprawdzRowne(
                "kosc" + to_string(liczbaOczek) + ".png",
                kosc.getNazwaPliku(),
                "nazwa pliku powinna odpowiadac aktualnej liczbie oczek"
            );
        }
    }

    static void testRzutNieZmieniaWartosciGdyKoscNieJestDostepna() {
        Kosc kosc(4);
        kosc.blokuj();

        int oczekiwanaLiczbaOczek = kosc.getLiczbaOczek();
        string oczekiwanaNazwaPliku = kosc.getNazwaPliku();

        for (int i = 0; i < 100; i++) {
            kosc.rzut();

            sprawdzRowne(
                oczekiwanaLiczbaOczek,
                kosc.getLiczbaOczek(),
                "liczba oczek nie powinna sie zmienic po rzucie zablokowana koscia"
            );
            sprawdzRowne(
                oczekiwanaNazwaPliku,
                kosc.getNazwaPliku(),
                "nazwa pliku nie powinna sie zmienic po rzucie zablokowana koscia"
            );
        }
    }

    template <typename TestCase>
    void uruchomTest(const string& nazwaTestu, TestCase testCase) {
        try {
            testCase();
            liczbaTestowZaliczonych++;
            cout << "[OK] " << nazwaTestu << endl;
        } catch (const exception& exception) {
            liczbaTestowNiezaliczonych++;
            cerr << "[FAIL] " << nazwaTestu << endl;
            cerr << "       " << exception.what() << endl;
        } catch (...) {
            liczbaTestowNiezaliczonych++;
            cerr << "[FAIL] " << nazwaTestu << endl;
            cerr << "       Wystapil nieznany blad." << endl;
        }
    }

    static void sprawdzWiekszeLubRowne(int aktualna, int oczekiwanaMinimalna, const string& komunikat) {
        if (aktualna < oczekiwanaMinimalna) {
            throw TestException(
                komunikat
                + ". Oczekiwano wartosci >= " + to_string(oczekiwanaMinimalna)
                + ", otrzymano: " + to_string(aktualna) + "."
            );
        }
    }

    static void sprawdzMniejszeLubRowne(int aktualna, int oczekiwanaMaksymalna, const string& komunikat) {
        if (aktualna > oczekiwanaMaksymalna) {
            throw TestException(
                komunikat
                + ". Oczekiwano wartosci <= " + to_string(oczekiwanaMaksymalna)
                + ", otrzymano: " + to_string(aktualna) + "."
            );
        }
    }

    static void sprawdzRowne(int oczekiwana, int aktualna, const string& komunikat) {
        if (oczekiwana != aktualna) {
            throw TestException(
                komunikat
                + ". Oczekiwano: " + to_string(oczekiwana)
                + ", otrzymano: " + to_string(aktualna) + "."
            );
        }
    }

    static void sprawdzRowne(const string& oczekiwana, const string& aktualna, const string& komunikat) {
        if (oczekiwana != aktualna) {
            throw TestException(
                komunikat
                + ". Oczekiwano: " + oczekiwana
                + ", otrzymano: " + aktualna + "."
            );
        }
    }
};

int main() {
    KoscTest test;
    return test.uruchom();
}
