package de.duehl.basics.collections;

/*
 * Copyright 2020 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.EmptyQueueException;

public class LimitedQueueTest {

    @Test
    public void create() {
        Queue<String> queue = new LimitedQueue<>(100);
        assertNotNull(queue);
    }

    @Test (expected = EmptyQueueException.class)
    public void failOnTakeFromEmptyQueue() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.take();
    }

    @Test
    public void queueAndTakeOneElement() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");

        String actual = queue.take();
        String expected = "foo";
        assertEquals(expected, actual);
    }

    @Test
    public void queueAndTakeOneElementAnsQueueIsEmpty() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");

        queue.take();
        assertTrue(queue.isEmpty());
    }

    @Test
    public void queueAndTakeTwoElements() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");

        String actual1 = queue.take();
        String expected1 = "foo";
        assertEquals(expected1, actual1);

        String actual2 = queue.take();
        String expected2 = "bar";
        assertEquals(expected2, actual2);
    }

    @Test
    public void queueAndTakeThreeElements() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        String actual1 = queue.take();
        String expected1 = "foo";
        assertEquals(expected1, actual1);

        String actual2 = queue.take();
        String expected2 = "bar";
        assertEquals(expected2, actual2);

        String actual3 = queue.take();
        String expected3 = "baz";
        assertEquals(expected3, actual3);
    }

    @Test
    public void queueAndTakeThreeElementsInQueueWithThreePlaces() {
        Queue<String> queue = new LimitedQueue<>(3);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        String actual1 = queue.take();
        String expected1 = "foo";
        assertEquals(expected1, actual1);

        String actual2 = queue.take();
        String expected2 = "bar";
        assertEquals(expected2, actual2);

        String actual3 = queue.take();
        String expected3 = "baz";
        assertEquals(expected3, actual3);
    }

    @Test (expected = EmptyQueueException.class)
    public void queueThreeElementsAndFailOnTakeFourElements() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        queue.take();
        queue.take();
        queue.take();
        queue.take();
    }

    @Test
    public void emptyQueueIsEmpty() {
        Queue<String> queue = new LimitedQueue<>(100);
        assertTrue(queue.isEmpty());
    }

    @Test
    public void notEmptyQueueIsNotEmpty() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        assertFalse(queue.isEmpty());
    }

    @Test
    public void clearedEmptyQueueIsEmpty() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.clear();
        assertTrue(queue.isEmpty());
    }

    @Test
    public void clearedNotEmptyQueueIsEmpty() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.clear();
        assertTrue(queue.isEmpty());
    }

    @Test
    public void testToString() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        String actual = queue.toString();
        String expected = "Queue [queueElements=[foo, bar, baz]]";
        assertEquals(expected, actual);
    }

    @Test
    public void emptyQueueHasSizeZero() {
        Queue<String> queue = new LimitedQueue<>(100);
        assertEquals(0, queue.size());
    }

    @Test
    public void queueThreeElementsAndSizeIsThree() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

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

    @Test
    public void queueFiveElementsTakeTwoElementsAndSizeIsThree() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");
        queue.queue("bla");
        queue.queue("fasel");

        queue.take();
        queue.take();

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

    @Test
    public void queueAndGetElementsAsList() {
        Queue<String> queue = new LimitedQueue<>(100);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");
        queue.queue("bla");
        queue.queue("fasel");

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

        queue.take();
        queue.take();

        List<String> elements2 = queue.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("baz", elements2.get(0));
        assertEquals("bla", elements2.get(1));
        assertEquals("fasel", elements2.get(2));
    }

    @Test
    public void takeAllElementsFrom() {
        Queue<String> queue1 = new LimitedQueue<>(100);
        queue1.queue("foo");
        queue1.queue("bar");
        queue1.queue("baz");

        Queue<String> queue2 = new LimitedQueue<>(100);
        queue2.queue("bla");
        queue2.queue("fasel");

        assertEquals(3, queue1.size());
        assertEquals(2, queue2.size());

        queue1.takeAllElementsFrom(queue2);

        assertEquals(5, queue1.size());
        assertEquals(0, queue2.size());

        List<String> elements1 = queue1.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 queueMoreElementesThanLimitAndTakeAllElements() {
        Queue<String> queue = new LimitedQueue<>(2);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        String actual1 = queue.take();
        String expected1 = "bar";
        assertEquals(expected1, actual1);

        String actual2 = queue.take();
        String expected2 = "baz";
        assertEquals(expected2, actual2);
    }

    @Test
    public void queueMoreElementesThanLimitAndTakeSizeIsLimitedSize() {
        Queue<String> queue = new LimitedQueue<>(2);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        assertEquals(2, queue.size());
    }

    @Test
    public void queueMoreElementesThanLimitAndTakeLimitedNumberOfElementsAndQueueIsEmpty() {
        Queue<String> queue = new LimitedQueue<>(2);
        queue.queue("foo");
        queue.queue("bar");
        queue.queue("baz");

        queue.take();
        queue.take();
        assertTrue(queue.isEmpty());
    }

}
