import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import org.junit.Test;

import java.util.NoSuchElementException;
import java.util.Random;

/**
 * Une classe pour faire des tests sur la classe SimpleDeque avec JUnit
 * On utilise des String comme type paramétré.
 */
public class TestSimpleDeque {

    /**
     * On ne peut pas ajouter avec addFirst l'élément null 
     * [0.5 point]
     */
    @Test(expected = NullPointerException.class)
    public void addFirstNull() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addFirst(null); // Doit lancer une exception
    }

    /**
     * On ne peut pas ajouter avec addLast l'élément null 
     * [0.5 point]
     */
    @Test(expected = NullPointerException.class)
    public void addLastNull() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addLast(null); // Doit lancer une exception
    }

    /**
     * Un deque sans élément est vide.
     * [0.5 point]
     */
    @Test
    public void addNoneIsEmpty() {
        MinimalDeque<String> d = new SimpleDeque<>();
        assertTrue(d.isEmpty());
    }

    /**
     * Un deque avec 1 élément n'est pas vide.
     * [0.5 point]
     */
    @Test
    public void addOneIsNotEmpty() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addFirst("test");
        assertFalse(d.isEmpty());
    }

    /**
     * Ajouter devant et enlever à la fin respecte l'ordre.
     * [1 point]
     */
    @Test
    public void pasDeResquilleur() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addFirst("meu");
        d.addFirst("zo");
        d.addFirst("bu");
        d.addFirst("ga");

        assertSame("ga", d.removeLast());
        assertSame("bu", d.removeLast());
        assertSame("zo", d.removeLast());
        assertSame("meu", d.removeLast());
    }

    /**
     * Ajouter derrière et enlever au début respecte l'ordre.
     * [1 point]
     */
    @Test
    public void pasDeResquilleurQuantique() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addLast("ga");
        d.addLast("bu");
        d.addLast("zo");
        d.addLast("meu");

        assertSame("ga", d.removeFirst());
        assertSame("bu", d.removeFirst());
        assertSame("zo", d.removeFirst());
        assertSame("meu", d.removeFirst());
    }

    /**
     * On ne peut pas enlever devant un deque vide.
     * [1 point]
     */
    @Test(expected = NoSuchElementException.class)
    public void removeFirstFromEmpty() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.removeFirst();
    }

    /**
     * On ne peut pas enlever derrière un deque vide.
     * [1 point]
     */
    @Test(expected = NoSuchElementException.class)
    public void removeLastFromEmpty() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.removeLast();
    }

    /**
     * Ajouter et enlever un seul élément dans les deux sens donne le même résultat.
     * [2 point]
     */
    @Test
    public void mangerLaBananeParLesDeuxBouts() {
        MinimalDeque<String> d = new SimpleDeque<>();
        d.addFirst("ga");

        assertSame("ga", d.removeFirst());
        d.addLast("ga");
        assertSame("ga", d.removeLast());
    }

    /**
     * Invariant de taille
     * [4 point]
     */
    @Test
    public void invariantTaille() {
        MinimalDeque<String> d = new SimpleDeque<>();
        String[] elements = {"ga", "bu", "zo", "meu"};
        Random random = new Random();
        int N = random.nextInt(901) + 100;

        for (int i = 0; i < N; i++) {
            if (random.nextBoolean()) {
                d.addFirst(elements[random.nextInt(elements.length)]);
            } else {
                d.addLast(elements[random.nextInt(elements.length)]);
            }
        }

        for (int i = 0; i < N; i++) {
            if (random.nextBoolean()) {
                d.removeFirst();
            } else {
                d.removeLast();
            }
        }

        assertTrue(d.isEmpty());
    }
}
