import java.util.*;
import java.io.*;

/**
 * Class to test LinkedLoop class
 * This class uses the many of the same commands as the ImageLoopEditor 
 * and does some additional tests
 * 
 * @author hasti
 *
 */
public class LinkedLoopTester {
	private static LinkedLoop<String> linkedLoop;
	private static boolean checkCurrent = false, badCurrent = false;
	private static boolean checkIterNext = false, badIterNext = false;
	private static boolean checkHasNext = false;

	public static void main(String[] args) {
		Scanner in = null;
		if (args.length > 1){
			System.err.println("invalid command-line arguments");
			System.exit(1);
		}
		boolean useFile = args.length == 1;
		if (useFile) {
			File inFile = new File(args[0]);
			if (!inFile.exists() || !inFile.canRead()) {
				System.err.println("Problem with input file!");
				System.exit(1);
			}
			try {
				in = new Scanner(inFile);
			} catch (FileNotFoundException e) {
				System.err.println("Problem with input file!");
				System.exit(1);
			}
		}
		else
			in = new Scanner(System.in);

		linkedLoop = new LinkedLoop<String>();

		/*
		 * Do a bunch of tests on empty linkedLoop
		 */
		if (!(linkedLoop instanceof LoopADT)) {
			error("LinkedLoop doesn't implement LoopADT interface");
		}

		if (linkedLoop.size() != 0)
			error("size incorrect for empty linkedLoop");

		if (!linkedLoop.isEmpty())
			error("isEmpty incorrect for empty linkedLoop");
		
		try {
			linkedLoop.getCurrent();
			error("getCurrent on empty linkedLoop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("getCurrent threw " + e.getClass().getName());
		}

		try {
			linkedLoop.removeCurrent();
			error("removeCurrent on empty linkedLoop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("removeCurrent threw " + e.getClass().getName());
		}

		try {
			linkedLoop.next();
		} catch (Exception e) {
			error("next on empty linkedLoop threw an exception");
		}

		try {
			linkedLoop.previous();
		} catch (Exception e) {
			error("previous on empty linkedLoop threw an exception");
		}

		/*
		 * Run the main loop
		 */
		boolean again = true;
		String msg;

		while (again) {
			System.out.print("enter command > ");
			String input = in.nextLine();
			if (useFile)
				System.out.println(input);
			char choice = input.charAt(0);

			switch (choice) {

			case 'd': 
				display();
				break;

			case 'f':
				if (linkedLoop.size() == 0)
					System.out.println("no messages");
				else {
					linkedLoop.next();
					currentContext();					
				}
				break;

			case 'b':
				if (linkedLoop.size() == 0)
					System.out.println("no messages");
				else {
					linkedLoop.previous();
					currentContext();	
				}
				break;

			case 'r':
				if (linkedLoop.size() == 0) 
					System.out.println("no messages");
				else {
					try {
						int beforeSize = linkedLoop.size();
						linkedLoop.removeCurrent();
						if (linkedLoop.size() != beforeSize - 1)
							error("removeCurrent doesn't correctly update size");
						if (linkedLoop.size() == 0)
							System.out.println("no messages");
						else
							currentContext();
					} catch (EmptyLoopException e) {
						error("removeCurrent threw EmptyLoopException");
					} catch (Exception e) {
						error("removeCurrent threw an exception");
					}
				}
				break;

			case 'a':
				msg = extract(input);
				if (msg != null) {	
					int beforeSize = linkedLoop.size();
					linkedLoop.add(msg);
					if (linkedLoop.size() != beforeSize + 1)
						error("add doesn't correctly update size");
					if (linkedLoop.isEmpty()) 
						error("isEmpty incorrect after adding item");
					currentContext();	
				}
				else
					System.out.println("invalid command");
				break;

			case 'x': 
				System.out.println("exit");
				again = false;
				break;

			default:
				System.out.println("invalid command");
			}
		}
		
		if (useFile)
			in.close();
	}
	
	private static void error(String s) {
		System.out.println("Error: " + s);
	}
	
	private static String currString(Object ob) {
		String str = "";
		if (badCurrent)
			str = (String) ((DblListnode)ob).getData();
		else
			str = (String) ob;
		return str;
	}
	
	private static String extract(String s) {
		String msg = null;
		if ( s.length() > 2 && s.charAt(1) == ' ' && s.charAt(2) != ' ' )
			msg = s.substring(2, s.length());
		return msg;
	}
	
	private static void currentContext() {
		if (linkedLoop.size() < 1) {
			System.out.println("no songs");
			return;
		}

		try {
			Object current = linkedLoop.getCurrent();
			if (!checkCurrent && current instanceof DblListnode) {
				error("getCurrent returns a list node");
				badCurrent = true;
			}
			checkCurrent = true;
		} catch (EmptyLoopException e) {
			error("getCurrent threw EmptyLoopException");
		} catch (Exception e) {
			error("getCurrent threw an exception");
		}

		try {
			if (linkedLoop.size() > 2) {
				linkedLoop.previous();
				System.out.println(currString(linkedLoop.getCurrent()));
				linkedLoop.next();
			}
		} catch (EmptyLoopException e) {
			error("getCurrent threw EmptyLoopException");
		} catch (Exception e) {
			error("getCurrent threw an exception");
		}

		try {
			System.out.println("<< " + currString(linkedLoop.getCurrent()) + " >>");
		} catch (EmptyLoopException e) {
			error("getCurrent threw EmptyLoopException");
		} catch (Exception e) {
			error("getCurrent threw an exception");
		}

		try {
			if (linkedLoop.size() > 1) {
				linkedLoop.next();
				System.out.println(currString(linkedLoop.getCurrent()));
				linkedLoop.previous();
			}
		} catch (EmptyLoopException e) {
			error("getCurrent threw EmptyLoopException");
		} catch (Exception e) {
			error("getCurrent threw an exception");
		}
	}

	private static void display() {
		if (linkedLoop.size() == 0)
			System.out.println("no messages");
		
		else {
			Iterator iter = linkedLoop.iterator();
			while (iter.hasNext()) {
				Object ob = iter.next();
				if (!checkIterNext && ob instanceof DblListnode) {
					error("Iterator next returns list node");
					badIterNext = true;
				}
				checkIterNext = true;
				if (badIterNext)
					System.out.println((String) ((DblListnode)ob).getData());
				else 
					System.out.println((String) ob);
			}
			
			if (!checkHasNext) {
				try {
					iter.next();  // expect an exception
					error("Iterator next did not throw exception - check if called hasNext");
				} catch (NoSuchElementException e) {
					// expected result
				} catch (Exception e) {
					error("hasNext threw " + e.getClass().getName());
				}
				checkHasNext = true;
			}
		}
	}

}