package de.duehl.basics.collections;

/*
 * Copyright 2019 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import static org.junit.Assert.*;

import java.util.List;

import org.junit.Test;

import de.duehl.basics.exceptions.EmptyStackException;

public class StackTest {

    @Test
    public void create() {
        Stack<String> stack = new Stack<>();
        assertNotNull(stack);
    }

    @Test (expected = EmptyStackException.class)
    public void failOnPopEmptyStack() {
        Stack<String> stack = new Stack<>();
        stack.pop();
    }

    @Test
    public void pushAndPopOneElement() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");

        String actual = stack.pop();
        String expected = "foo";
        assertEquals(expected, actual);
    }

    @Test
    public void pushAndPopTwoElements() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");

        String actual1 = stack.pop();
        String expected1 = "bar";
        assertEquals(expected1, actual1);

        String actual2 = stack.pop();
        String expected2 = "foo";
        assertEquals(expected2, actual2);
    }

    @Test
    public void pushAndPopThreeElements() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");

        String actual1 = stack.pop();
        String expected1 = "baz";
        assertEquals(expected1, actual1);

        String actual2 = stack.pop();
        String expected2 = "bar";
        assertEquals(expected2, actual2);

        String actual3 = stack.pop();
        String expected3 = "foo";
        assertEquals(expected3, actual3);
    }

    @Test (expected = EmptyStackException.class)
    public void pushThreeElementsAndFailOnPopFourElements() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");

        stack.pop();
        stack.pop();
        stack.pop();
        stack.pop();
    }

    @Test
    public void emptyStackIsEmpty() {
        Stack<String> stack = new Stack<>();
        assertTrue(stack.isEmpty());
    }

    @Test
    public void notEmptyStackIsNotEmpty() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        assertFalse(stack.isEmpty());
    }

    @Test
    public void clearedEmptyStackIsEmpty() {
        Stack<String> stack = new Stack<>();
        stack.clear();
        assertTrue(stack.isEmpty());
    }

    @Test
    public void clearedNotEmptyStackIsEmpty() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.clear();
        assertTrue(stack.isEmpty());
    }

    @Test
    public void testToString() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");

        String actual = stack.toString();
        String expected = "Stack [stackElements=[foo, bar, baz]]";
        assertEquals(expected, actual);
    }

    @Test
    public void emptyStackHasSizeZero() {
        Stack<String> stack = new Stack<>();
        assertEquals(0, stack.size());
    }

    @Test
    public void pushThreeElementsAndSizeIsThree() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");

        assertEquals(3, stack.size());
    }

    @Test
    public void pushFiveElementsPopTwoElementsAndSizeIsThree() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");
        stack.push("bla");
        stack.push("fasel");

        stack.pop();
        stack.pop();

        assertEquals(3, stack.size());
    }

    @Test
    public void pushAndGetElementsAsList() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");
        stack.push("bla");
        stack.push("fasel");

        List<String> elements1 = stack.getElementsAsList();

        stack.pop();
        stack.pop();

        List<String> elements2 = stack.getElementsAsList();

        assertEquals(5, elements1.size());
        assertEquals("foo", elements1.get(0));
        assertEquals("bar", elements1.get(1));
        assertEquals("baz", elements1.get(2));
        assertEquals("bla", elements1.get(3));
        assertEquals("fasel", elements1.get(4));

        assertEquals(3, elements2.size());
        assertEquals("foo", elements2.get(0));
        assertEquals("bar", elements2.get(1));
        assertEquals("baz", elements2.get(2));
    }

    @Test
    public void reverse() {
        Stack<String> stack = new Stack<>();
        stack.push("foo");
        stack.push("bar");
        stack.push("baz");
        stack.push("bla");
        stack.push("fasel");

        assertEquals(5, stack.size());

        List<String> elements1 = stack.getElementsAsList();
        assertEquals("foo", elements1.get(0));
        assertEquals("bar", elements1.get(1));
        assertEquals("baz", elements1.get(2));
        assertEquals("bla", elements1.get(3));
        assertEquals("fasel", elements1.get(4));

        stack.reverse();

        assertEquals(5, stack.size());

        List<String> elements2 = stack.getElementsAsList();
        assertEquals("fasel", elements2.get(0));
        assertEquals("bla", elements2.get(1));
        assertEquals("baz", elements2.get(2));
        assertEquals("bar", elements2.get(3));
        assertEquals("foo", elements2.get(4));
    }

    @Test
    public void takeAllElementsFrom() {
        Stack<String> stack1 = new Stack<>();
        stack1.push("foo");
        stack1.push("bar");
        stack1.push("baz");

        Stack<String> stack2 = new Stack<>();
        stack2.push("bla");
        stack2.push("fasel");

        assertEquals(3, stack1.size());
        assertEquals(2, stack2.size());

        stack1.takeAllElementsFrom(stack2);

        assertEquals(5, stack1.size());
        assertEquals(0, stack2.size());

        List<String> elements1 = stack1.getElementsAsList();
        assertEquals("foo", elements1.get(0));
        assertEquals("bar", elements1.get(1));
        assertEquals("baz", elements1.get(2));
        assertEquals("bla", elements1.get(3));
        assertEquals("fasel", elements1.get(4));
    }

    @Test
    public void takeAllReverseElementsFrom() {
        Stack<String> stack1 = new Stack<>();
        stack1.push("foo");
        stack1.push("bar");
        stack1.push("baz");

        Stack<String> stack2 = new Stack<>();
        stack2.push("bla");
        stack2.push("fasel");

        assertEquals(3, stack1.size());
        assertEquals(2, stack2.size());

        stack1.takeAllReverseElementsFrom(stack2);

        assertEquals(5, stack1.size());
        assertEquals(0, stack2.size());

        List<String> elements1 = stack1.getElementsAsList();
        assertEquals("foo", elements1.get(0));
        assertEquals("bar", elements1.get(1));
        assertEquals("baz", elements1.get(2));
        assertEquals("fasel", elements1.get(3));
        assertEquals("bla", elements1.get(4));
    }

}
