/**
 * Implementation of a Singly-Linked List.
 *
 * @author  __your_name___
 */

import java.util.NoSuchElementException;

public class SLinkedList<E>
{
	// Representation of the list nodes
	private class Node
	{
		E data;          // the data value stored at the node
		Node next;       // the successor of this node

		// creates a node with the given data item and no successor
		Node(E d)
		{
			data = d;
			next = null;
		}
	}

	/**
	 * The first node in the list.
	 */
	private Node head;

	// put comment in Javadoc style
	public SLinkedList()
	{
		head = null;
	}

	// put comment in Javadoc style
	public boolean isEmpty()
	{
		return head == null;
	}

	/**
	 * Adds the given element to the front of the list.
	 *
	 * @param data    the element to add
	 */
	public void addFirst(E data)
	{
		Node node = new Node(data);
		node.next = head;
		head = node;
	}

	/**
	 * Returns the first element in the list.
	 *
	 * @return the first element in the list
	 * @throws NoSuchElementException when the list is empty
	 */
	public E getFirst()
	{
		if (this.isEmpty()) {
			throw new NoSuchElementException();
		}

		return head.data;
	}

	// put comment in Javadoc style
	public void addLast(E data)
	{
		// special case
		if (this.isEmpty()) {
			head = new Node( data );
		}
		else {
			// find last node
			Node curr = head;
			while (curr.next != null) {
				curr = curr.next;
			}

			// attach the new node to the last node
			Node node = new Node(data);
			curr.next = node;
		}
	}

	// put comment in Javadoc style
	public boolean remove(E data)
	{
		Node prevNode = head;   // follows one box/node behind currNode
		Node currNode = head;   // looks for the box/node with item

		// as long as currNode is within the list (points to some node)
		while (currNode != null) {
			if (currNode.data.equals(data)) {
				// special case if removing first node
				if (head == currNode) {
					head = currNode.next;
				}
				else {
					prevNode.next = currNode.next;
				}
				return true;
			}

			// advance the pointers, so they are one box/node apart
			prevNode = currNode;
			currNode = currNode.next;
		}

		return false;
	}

	// put comment in Javadoc style
	public E removeAt(int index)
	{
		// how to handle negative indices?

		Node prevNode = head;   // follows one box/node behind currNode
		Node currNode = head;   // looks for the box/node with given index
		int i = 0;              // starting at first box, i.e. 0

		// as long as currNode within list and box index not reached, advance
		while (currNode != null && i < index) {
			prevNode = currNode;       // move each
			currNode = currNode.next;  // one step
			i++;                       // ahead
		}
		
		// check whether loop stopped because currNode is null (no boxes left, i.e. index too high)
		if ( currNode == null ) {
			throw new IndexOutOfBoundsException();    // stops method, done!
		}

		// if reached here, delete the box
		E data = currNode.data;                  // keep the data
					   
		if ( currNode == head ) {	         // special case for first node
			head = head.next;                // lose 1st node;
		}
		else {
			prevNode.next = currNode.next;   // bypass node
		}

		return data;                             // give back the data
	}

	// put comment in Javadoc style
	public String toStringNext()
	{
		String str = "";
		Node curr = head;

		// add each data item to the result string
		while (curr != null) {
			str = str + curr.data + " ";
			curr = curr.next;
		}

		// remove trailing space and enclose in [ ]
		str = "[" + str.trim() + "]";

		return str;
	}

	// example of recursive method

	public String toString()
	{
		String str = toString( head );   // call private helper recursive method

		// remove trailing space and enclose in [ ]
		str = "[" + str.trim() + "]";

		return str;
	}

	// I compute a string from given node to the end
	private String toString( Node curr )
	{
		if ( curr == null ) {    // if fell of the list, no items
			return "";
		}
		else {
			String strRest = toString( curr.next );     // get string for all to the right
			String result = curr.data + " " + strRest;  // prepend self to rest

			return result;
		}
	}
}