package kosci;

public class KoscTest {

    private static int liczbaTestowZaliczonych = 0;
    private static int liczbaTestowNiezaliczonych = 0;

    public static void main(String[] args) {
        uruchomTest(
                "rzut zwraca wartość od 1 do 6",
                KoscTest::testRzutZwracaWartoscOd1Do6
        );
        uruchomTest(
                "rzut nie zmienia wartości, gdy kość nie jest dostępna",
                KoscTest::testRzutNieZmieniaWartosciGdyKoscNieJestDostepna
        );

        System.out.println();
        System.out.println("Wynik: "
                + liczbaTestowZaliczonych + " zaliczone, "
                + liczbaTestowNiezaliczonych + " niezaliczone.");

        if (liczbaTestowNiezaliczonych > 0) {
            System.exit(1);
        }
    }

    private static void testRzutZwracaWartoscOd1Do6() {
        Kosc kosc = new Kosc(1);

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

            int liczbaOczek = kosc.getLiczbaOczek();

            sprawdzWiekszeLubRowne(
                    liczbaOczek,
                    1,
                    "liczba oczek po rzucie powinna być większa lub równa 1"
            );
            sprawdzMniejszeLubRowne(
                    liczbaOczek,
                    6,
                    "liczba oczek po rzucie powinna być mniejsza lub równa 6"
            );
            sprawdzRowne(
                    "kosc" + liczbaOczek + ".png",
                    kosc.getNazwaPliku(),
                    "nazwa pliku powinna odpowiadać aktualnej liczbie oczek"
            );
        }
    }

    private static void testRzutNieZmieniaWartosciGdyKoscNieJestDostepna() {
        Kosc kosc = new 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 się zmienić po rzucie zablokowaną kością"
            );
            sprawdzRowne(
                    oczekiwanaNazwaPliku,
                    kosc.getNazwaPliku(),
                    "nazwa pliku nie powinna się zmienić po rzucie zablokowaną kością"
            );
        }
    }

    private static void uruchomTest(String nazwaTestu, TestCase testCase) {
        try {
            testCase.uruchom();
            liczbaTestowZaliczonych++;
            System.out.println("[OK] " + nazwaTestu);
        } catch (AssertionError error) {
            liczbaTestowNiezaliczonych++;
            System.err.println("[FAIL] " + nazwaTestu);
            System.err.println("       " + error.getMessage());
        }
    }

    private static void sprawdzWiekszeLubRowne(int aktualna, int oczekiwanaMinimalna, String komunikat) {
        if (aktualna < oczekiwanaMinimalna) {
            throw new AssertionError(komunikat
                    + ". Oczekiwano wartości >= " + oczekiwanaMinimalna
                    + ", otrzymano: " + aktualna + ".");
        }
    }

    private static void sprawdzMniejszeLubRowne(int aktualna, int oczekiwanaMaksymalna, String komunikat) {
        if (aktualna > oczekiwanaMaksymalna) {
            throw new AssertionError(komunikat
                    + ". Oczekiwano wartości <= " + oczekiwanaMaksymalna
                    + ", otrzymano: " + aktualna + ".");
        }
    }

    private static void sprawdzRowne(int oczekiwana, int aktualna, String komunikat) {
        if (oczekiwana != aktualna) {
            throw new AssertionError(komunikat
                    + ". Oczekiwano: " + oczekiwana
                    + ", otrzymano: " + aktualna + ".");
        }
    }

    private static void sprawdzRowne(String oczekiwana, String aktualna, String komunikat) {
        if (!oczekiwana.equals(aktualna)) {
            throw new AssertionError(komunikat
                    + ". Oczekiwano: " + oczekiwana
                    + ", otrzymano: " + aktualna + ".");
        }
    }

    @FunctionalInterface
    private interface TestCase {
        void uruchom();
    }
}