package edu.kit.prog1314.shop.util;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * A generic singly linked list implementation.
 * 
 * @author Janis Schötterl-Glausch
 * @author Raphael von der Grün
 * @version 2
 * @param <T>
 *            The type of the elements held in this list.
 */
public class List<T> implements Iterable<T> {
    private Element head = new Element(null, null);

    /**
     * Creates an empty List
     */
    public List() { }

    /**
     * Adds the element to the head of the list.
     * 
     * @param value
     *            The value to add.
     */
    public void add(T value) {
        head.insertAfter(value);
    }

    /**
     * Adds elem to the head of the list unless an equal element is already
     * contained in the list.
     * 
     * @param elem
     *            The element to add/look up.
     * @return The unique element in the list.
     */
    public T addUnique(T elem) {
        for (T other : this) {
            if (elem.equals(other)) {
                return other;
            }
        }
        add(elem);
        return elem;
    }

    @Override
    public Iterator<T> iterator() {
        return new ListIterator();
    }

    @Override
    public String toString() {
        String sep = System.lineSeparator();
        StringBuilder res = new StringBuilder();
        for (T t : this) {
            res.append(t).append(sep);
        }
        if (res.length() > 0) {
            res.delete(res.length() - sep.length(), res.length());
        }
        return res.toString();
    }

    private final class Element {
        private T value;
        private Element next;

        private Element(T value, Element next) {
            this.value = value;
            this.next = next;
        }

        private void insertAfter(T value) {
            Element ins = new Element(value, next);
            next = ins;
        }
    }

    private final class ListIterator implements Iterator<T> {
        private Element current;

        private ListIterator() {
            current = head;
        }

        public boolean hasNext() {
            return current.next != null;
        }

        public T next() {
            if (hasNext()) {
                current = current.next;
                return current.value;
            } else {
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
