package pl.umk.fizyka.ulamekdemo;

import org.junit.Test;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Random;

import static org.junit.Assert.*;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest
{
    @Test
    public void testKonstruktoraIWłasności()
            throws MianownikUłamkaRównyZero
    {
        //przygotowania (arrange)
        int licznik = 1;
        int mianownik = 2;

        //działanie (act)
        Ułamek u = new Ułamek(licznik, mianownik);
        int actualLicznik = u.getLicznik();
        int actualMianownik = u.getMianownik();

        //weryfikacja (assert)
        assertEquals("Niezgodność w liczniku", licznik, actualLicznik);
        assertEquals("Niezgodność w mianownik", mianownik, actualMianownik);
    }

    @Test
    public void testKonstruktora()
            throws MianownikUłamkaRównyZero, NoSuchFieldException, IllegalAccessException
    {
        //przygotowania (arrange)
        int licznik = 1;
        int mianownik = 2;

        //działanie (act)
        Ułamek u = new Ułamek(licznik, mianownik);

        //weryfikacja (assert)
        Field poleLicznik = Ułamek.class.getDeclaredField("licznik");
        Field poleMianownik = Ułamek.class.getDeclaredField("mianownik");
        poleLicznik.setAccessible(true);
        poleMianownik.setAccessible(true);
        int actualLicznik = poleLicznik.getInt(u);
        int actualMianownik = poleMianownik.getInt(u);

        assertEquals("Niezgodność w liczniku", licznik, actualLicznik);
        assertEquals("Niezgodność w mianownik", mianownik, actualMianownik);
    }

    @Test(expected = MianownikUłamkaRównyZero.class)
    public void TestKonstruktoraWyjątek() throws MianownikUłamkaRównyZero
    {
        Ułamek u = new Ułamek(1, 0);
    }

    @Test
    public void TestStatycznegoPola()
    {
        Ułamek u = Ułamek.POŁOWA;
        assertEquals(1, u.getLicznik());
        assertEquals(2, u.getMianownik());
    }

    @Test
    public void TestMetodyUprość()
    {
        try
        {
            //arrange
            Ułamek u = new Ułamek(4, -2);

            //act
            u.uprość();

            //assert
            assertEquals(-2, u.getLicznik());
            assertEquals(1, u.getMianownik());
        }
        catch(Exception exc)
        {
            return;
        }
    }

    @Test
    public void TestMetodyToDouble()
    {
        try
        {
            Ułamek u = new Ułamek(2, -4);
            assertEquals(-0.5, u.toDouble(), 1E-6);
        }
        catch(Exception exc)
        {
            return;
        }
    }

    @Test
    public void TestMetodyToDoubleLosowe()
    {
        long N = 100000;
        try
        {
            for(long i = 0; i < N; ++i)
            {
                int licznik = losujLiczbęCałkowitą();
                int mianownik = losujLiczbęCałkowitąRóżnąOdZera();
                Ułamek u = new Ułamek(licznik, mianownik);
                assertEquals(licznik/(double)mianownik, u.toDouble(), 1E-6);
            }
        }
        catch(Exception exc)
        {
            return;
        }
    }

    @Test
    public void TestMetodyUprośćLosowe()
    {
        long N = 10;
        try
        {
            for(long i = 0; i < N; i++)
            {
                //arrange
                Ułamek u = new Ułamek(losujLiczbęCałkowitą(), losujLiczbęCałkowitąRóżnąOdZera());
                double wartośćPrzedUproszczeniem = u.toDouble();

                //act
                u.uprość();

                //assert
                assertEquals(wartośćPrzedUproszczeniem, u.toDouble(), 1E-3);
            }
        }
        catch(Exception exc)
        {
            return;
        }
    }

    private Random r = new Random();

    private int losujLiczbęCałkowitą(int maksymalnaBezwzględnaWartość)
    {
        maksymalnaBezwzględnaWartość = Math.abs(maksymalnaBezwzględnaWartość);
        return r.nextInt(maksymalnaBezwzględnaWartość);
    }

    private int losujLiczbęCałkowitą()
    {
        return losujLiczbęCałkowitą(Integer.MAX_VALUE);
    }

    private int losujLiczbęCałkowitąRóżnąOdZera(int maksymalnaBezwzględnaWartość)
    {
        int wartość;
        do
        {
            wartość = losujLiczbęCałkowitą(maksymalnaBezwzględnaWartość);
        }
        while(wartość == 0);
        return wartość;
    }

    private int losujLiczbęCałkowitąRóżnąOdZera()
    {
        return losujLiczbęCałkowitą(Integer.MAX_VALUE);
    }

    @Test
    public void TestDodawanie() throws MianownikUłamkaRównyZero
    {
        Ułamek u1 = new Ułamek(1, 2);
        Ułamek u2 = new Ułamek(2, 3);
        Ułamek u3 = Ułamek.suma(u1,u2);

        assertEquals("licznik", u3.getLicznik(), 7);
        assertEquals("mianownik", u3.getMianownik(),6);
    }

    @Test
    public void TestDodawanieLosowe() throws MianownikUłamkaRównyZero
    {
        int zakres = (int)(Math.sqrt(Integer.MAX_VALUE)/2);
        int N = 1000;
        for(int i = 0; i < N; ++i)
        {
            Ułamek u1 = new Ułamek(losujLiczbęCałkowitą(zakres), losujLiczbęCałkowitąRóżnąOdZera(zakres));
            Ułamek u2 = new Ułamek(losujLiczbęCałkowitą(zakres), losujLiczbęCałkowitąRóżnąOdZera(zakres));
            double d1 = u1.toDouble();
            double d2 = u2.toDouble();

            Ułamek u3 = Ułamek.suma(u1, u2);
            double d3 = u3.toDouble();

            assertEquals(d1+d2, d3, 1E-3);
        }
    }

    @Test
    public void TestSortowania() throws MianownikUłamkaRównyZero {
        //arrange
        Ułamek[] tablica = new Ułamek[100];
        for (int i = 0; i < tablica.length; ++i) {
            tablica[i] = new Ułamek(losujLiczbęCałkowitą(), losujLiczbęCałkowitąRóżnąOdZera());
        }

        //act
        Arrays.sort(tablica);

        Boolean tablicaJestPosortowanaRosnąco = true;
        for (int i = 0; i < tablica.length - 1; ++i)
            if(!tablica[i].isLesserOrEqualTo(tablica[i+1]))
            {
                tablicaJestPosortowanaRosnąco = false;
                break;
            }
        assertTrue(tablicaJestPosortowanaRosnąco);
    }
}