// CardLinkedList.java
// Kiri Wagstaff, wkiri@cs.cornell.edu
// August 1, 2001

public class CardLinkedList {
    private CardNode head;

		// Default constructor: creates an empty list
    public CardLinkedList() {
        head = null;
    }

		// length: returns the number of nodes in the list.
    public int length() {
        int count = 0;
        CardNode position = head;
        while (position != null) // the final node links to null
        {
            count++;
            position = position.link;  // go to the next node
        }
        return count;
    }

		// add: inserts the new Card to the head of the list
		//      by creating a new CardNode and linking it in front of head.
    public void add(Card newCard) {
        head = new CardNode(newCard, head); 
    }

		// remove: removes the first Card, and returns it.
		// It's okay to return the card itself here, because Cards
		// have no mutator methods, and at any rate, we're done with it.
    public Card remove() {
        if (head != null) {
        		Card c = head.data;
            head = head.link;
            return c;
        } else {
            System.out.println("Deleting from an empty list? Exiting!");
            System.exit(1);
        }
        return null;
    }

		// inList: returns true if the Card is in the list.
    public boolean inList(Card target)
    {
        return (Find(target) != null);
    }

		// Find: returns the first node containing the Card.
		//       If it's not there, returns null.
		//       This method is private so no one else can get access
		//       to the internal Cards of the list.
    private CardNode Find(Card target)
    {
        CardNode position;
        position = head;
        Card dataAtPosition;
        while (position != null) {
            dataAtPosition = position.data;
            if (dataAtPosition.equals(target)) return position;
            position = position.link;
        }
        return null;
    }

		// toString: return a nice String representation of the list.
    public String toString()
    {
        CardNode position = head;
        if (position == null) return "{}";
        String result = "{ " + position.data;
	      position = position.link;
        while (position != null) {
            result += ", " + position.data;
            position = position.link;
        }
        result += " }";
        return result;
    }

		// This class is private, because no one else should ever
		// need to use it.
    private class CardNode {
        private Card data;     // The stored Card
        private CardNode link; // The next CardNode in the list.

        public CardNode() {
            link = null;
            data = null;
        }

				// Create a CardNode with the specified Card inside it,
				// and link it to linkValue. Make a COPY of the Card.
        public CardNode(Card newData, CardNode linkValue) {
            data = new Card(newData.getValue(), newData.getSuit());
            link = linkValue;
        }
    }


		// Now let's add some other fun stuff to make this more like an array:
		// addAt: insert the Card at the specified index (starting index is 0)
		public void addAt(Card newCard, int index) {
			if (index < 0) {
				// Invalid index
				System.out.println("Index must be positive.");
				return;
			}
			// Handle adding at 0 specially
			if (index == 0) {
				// Just use the regular add method
				add(newCard);
				return;
			}
			CardNode position = head;
			int count = 0;
			// Find the right place to insert the new Card
			while (position != null) {
				// Position is at node number "count"
				if (count == index-1) {
					// Create the new card, and make it point to position's link.
					CardNode temp = new CardNode(newCard, position.link);
					// Now make position's node link up with the new node
					position.link = temp;
					// Done!
					return;
				} else {
					// Keep moving down the list
					position = position.link;
					count++;
				}
			}
			// Uh oh - if we made it here, then the index was too big.  Fail!
			System.out.println("Index " + index + " is too big for this list.");
			return;
		}
		
		// removeFrom: get the Card at the specified index and return it
		//             also, delete it from the list
		public Card removeFrom(int index) {
			if (index < 0) {
				// Invalid index
				System.out.println("Index must be positive.");
				return null;
			}
			// Handle removing from 0 specially
			if (index == 0) {
				// Just use the regular remove method
				return remove();
			}
			CardNode position = head;
			int count = 0;
			// Find the right Card
			while (position != null) {
				// Position is at node number "count"
				if (count == index-1) {
					// Make sure this isn't the last thing in the list.
					// If so, index is invalid.
					if (position.link == null) break;
					// Store the one we want
					Card temp = position.link.data;
					// Fix position's link to skip the one we're removing
					position.link = position.link.link;
					// Return this card
					return temp;
				} else {
					// Keep moving down the list
					position = position.link;
					count++;
				}
			}
			// Uh oh - if we made it here, then the index was too big.  Fail!
			System.out.println("Index " + index + " is too big for this list.");
			return null;
		}
		    
    public static void main(String[] args) {
    	// Test the constructor
    	CardLinkedList cll = new CardLinkedList();
    	System.out.println("New empty list: " + cll);
    	
    	// Test the add() method
    	Card c = new Card(3, "hearts");
    	cll.add(c);
    	System.out.println("Adding " + c + " gives " + cll);
    	cll.add(c);
    	System.out.println("Adding " + c + " gives " + cll);
    	Card c2 = new Card(Card.KING, "spades");
    	cll.add(c2);
    	System.out.println("Adding " + c2 + " gives " + cll);

			// Test the addAt() method
			Card c3 = new Card(Card.QUEEN, "clubs");
			cll.addAt(c3, 2);
    	System.out.println("Adding " + c3 + " at index 2 gives " + cll);
			cll.addAt(c3, 5); // should fail!
    	System.out.println("Adding " + c3 + " at index 5 gives " + cll);
			   
			// Test the inList() method
   		System.out.println(c2 + (cll.inList(c2)? " IS " : " ISN'T ")
   											+ "in the list.");
   											
   		// Test the remove() method
    	Card c4 = cll.remove();
    	System.out.println("Got the first element: " + c4 + ", now it's " + cll);
		
			// Test the removeFrom() method
			Card c5 = cll.removeFrom(2);
    	System.out.println("Removing element 2 gives " + c5 + " and " + cll);
			cll.removeFrom(5); // should fail!
    	System.out.println("Removing element 5 gives " + cll);
    }
}