package com.wrox.algorithms.lists;

import com.wrox.algorithms.iteration.Iterator;
import com.wrox.algorithms.iteration.IteratorOutOfBoundsException;
import junit.framework.TestCase;

public abstract class AbstractListTest extends TestCase {
    protected static final Object VALUE_A = "A";
    protected static final Object VALUE_B = "B";
    protected static final Object VALUE_C = "C";

    protected abstract List createList();

   public void testInsertIntoEmptyList() {
        List list = createList(); // utworzenie pustej listy
        
        assertEquals(0, list.size()); 
        assertTrue(list.isEmpty());

        list.insert(0, VALUE_A);

        assertEquals(1, list.size());
        assertFalse(list.isEmpty());
        assertSame(VALUE_A, list.get(0));
    }

    public void testInsertBetweenElements() {
        List list = createList(); // utworzenie pustej listy
        
        list.insert(0, VALUE_A);  // lewy element
        list.insert(1, VALUE_B);  // prawy element
        list.insert(1, VALUE_C);  // wstawienie między lewy i prawy element
        assertEquals(3, list.size());

        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_C, list.get(1));
        assertSame(VALUE_B, list.get(2));
    }


    public void testInsertBeforeFirstElement() {
        List list = createList(); // utworzenie pustej listy

        list.insert(0, VALUE_A);  // wstawienie na początek
        list.insert(0, VALUE_B);  // wstawienie na początek

        assertEquals(2, list.size());
        assertSame(VALUE_B, list.get(0));
        assertSame(VALUE_A, list.get(1));
    }


    public void testInsertAfterLastElement() {
        List list = createList(); // utworzenie pustej listy

        list.insert(0, VALUE_A);
        list.insert(1, VALUE_B);

        assertEquals(2, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
    }

    public void testInsertOutOfBounds() {
        List list = createList(); // utworzenie pustej listy
        try {
            list.insert(-1, VALUE_A); // próba wstawienia przed początkiem listy
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

        try {
            list.insert(1, VALUE_B); // próba wstawienia poza końcem listy
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }

    public void testAdd() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_C);
        list.add(VALUE_B);

        assertEquals(3, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_C, list.get(1));
        assertSame(VALUE_B, list.get(2));
    }


    public void testSet() {
        List list = createList(); // utworzenie pustej listy
        list.insert(0, VALUE_A);
        assertSame(VALUE_A, list.get(0));

        assertSame(VALUE_A, list.set(0, VALUE_B));
        assertSame(VALUE_B, list.get(0));
    }



    public void testGetOutOfBounds() {
        List list = createList(); // utworzenie pustej listy
        try {
            list.get(-1);
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

        try {
            list.get(0); // lista jest nadal pusta
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

        list.add(VALUE_A);

        try {
            list.get(1); // ostatni element ma indeks 0
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }


    public void testSetOutOfBounds() {
        List list = createList(); // utworzenie pustej listy

        try {
            list.set(-1, VALUE_A);
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

        try {
            list.set(0, VALUE_B);
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

        list.insert(0, VALUE_C);

        try {
            list.set(1, VALUE_C);
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }


    public void testDeleteOnlyElement() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);

        assertEquals(1, list.size());
        assertSame(VALUE_A, list.get(0));

        assertSame(VALUE_A, list.delete(0));

        assertEquals(0, list.size());
    }

    public void testDeleteFirstElement() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_C);

        assertEquals(3, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
        assertSame(VALUE_C, list.get(2));

        assertSame(VALUE_A, list.delete(0));

        assertEquals(2, list.size());
        assertSame(VALUE_B, list.get(0));
        assertSame(VALUE_C, list.get(1));
    }

    public void testDeleteLastElement() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_C);

        assertEquals(3, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
        assertSame(VALUE_C, list.get(2));

        assertSame(VALUE_C, list.delete(2));

        assertEquals(2, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
    }


    public void testDeleteMiddleElement() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_C);
        list.add(VALUE_B);

        assertEquals(3, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_C, list.get(1));
        assertSame(VALUE_B, list.get(2));

        assertSame(VALUE_C, list.delete(1));

        assertEquals(2, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
    }

    public void testDeleteOutOfBounds() {
        List list = createList(); // utworzenie pustej listy

        try {
            list.delete(-1); // ujemny indeks
            fail(); // zachowanie nieoczekiwane
        } catch (IndexOutOfBoundsException e) {
            // zachowanie oczekiwane
        }

    public void testDeleteByValue() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_A);

        assertEquals(3, list.size());
        assertSame(VALUE_A, list.get(0));
        assertSame(VALUE_B, list.get(1));
        assertSame(VALUE_A, list.get(2));

        assertTrue(list.delete(VALUE_A));

        assertEquals(2, list.size());
        assertSame(VALUE_B, list.get(0));
        assertSame(VALUE_A, list.get(1));

        assertTrue(list.delete(VALUE_A));

        assertEquals(1, list.size());
        assertSame(VALUE_B, list.get(0));

        assertFalse(list.delete(VALUE_C));

        assertEquals(1, list.size());
        assertSame(VALUE_B, list.get(0));

        assertTrue(list.delete(VALUE_B));

        assertEquals(0, list.size());
    }

    public void testEmptyIteration() {
        List list = createList(); // utworzenie pustej listy

        Iterator iterator = list.iterator();
        assertTrue(iterator.isDone());

        try {
            iterator.current();
            fail(); // zachowanie nieoczekiwane
        } catch (IteratorOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }

    public void testForwardIteration() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_C);

        Iterator iterator = list.iterator();

        iterator.first();
        assertFalse(iterator.isDone());
        assertSame(VALUE_A, iterator.current());

        iterator.next();
        assertFalse(iterator.isDone());
        assertSame(VALUE_B, iterator.current());

        iterator.next();
        assertFalse(iterator.isDone());
        assertSame(VALUE_C, iterator.current());

        iterator.next();
        assertTrue(iterator.isDone());
        try {
            iterator.current();
            fail(); // zachowanie nieoczekiwane
        } catch (IteratorOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }

    public void testReverseIteration() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_C);

        Iterator iterator = list.iterator();

        iterator.last();
        assertFalse(iterator.isDone());
        assertSame(VALUE_C, iterator.current());

        iterator.previous();
        assertFalse(iterator.isDone());
        assertSame(VALUE_B, iterator.current());

        iterator.previous();
        assertFalse(iterator.isDone());
        assertSame(VALUE_A, iterator.current());

        iterator.previous();
        assertTrue(iterator.isDone());
        try {
            iterator.current();
            fail(); // zachowanie nieoczekiwane
        } catch (IteratorOutOfBoundsException e) {
            // zachowanie oczekiwane
        }
    }

    public void testIndexOf() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_A);

        assertEquals(0, list.indexOf(VALUE_A));
        assertEquals(1, list.indexOf(VALUE_B));
        assertEquals(-1, list.indexOf(VALUE_C));
    }

    public void testContains() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_A);

        assertTrue(list.contains(VALUE_A));
        assertTrue(list.contains(VALUE_B));
        assertFalse(list.contains(VALUE_C));
    }

    public void testClear() {
        List list = createList(); // utworzenie pustej listy

        list.add(VALUE_A);
        list.add(VALUE_B);
        list.add(VALUE_C);

        assertFalse(list.isEmpty());
        assertEquals(3, list.size());


        list.clear();

        assertTrue(list.isEmpty());
        assertEquals(0, list.size());

    }

}





