import java.util.*;
import java.io.*;

/**
 * Class to test LinkedLoop class
 * This class uses the many of the same commands as the MessageLoopEditor 
 * and does some additional tests
 * Note that the current context is displayed differently than in P2
 * 
 * The tester does not use the commands: ? s l j a f u
 * 
 * @author hasti
 *
 */
public class LinkedLoopTester {
	private static LinkedLoop loop;
	//private static ArrayListLoop loop;
	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);
		
		loop = new LinkedLoop();
		//loop = new ArrayListLoop();
		
		/*
		 * Do a bunch of tests on empty loop
		 */
		if (!(loop instanceof Loop)) {
			error("LinkedLoop doesn't implement Loop interface");
		}
		
		if (loop.size() != 0)
			error("size incorrect for empty loop");
		
		if (!loop.isEmpty())
			error("isEmpty incorrect for empty loop");
		
		try {
			loop.getCurrent();
			error("getCurrent on empty loop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("getCurrent threw " + e.getClass().getName());
		}
		
		try {
			loop.removeCurrent();
			error("removeCurrent on empty loop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("removeCurrent threw " + e.getClass().getName());
		}

		try {
			loop.forward();
			error("forward on empty loop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("forward threw " + e.getClass().getName());
		}
		
		try {
			loop.backward();
			error("backward on empty loop did not throw exception");
		} catch (EmptyLoopException e) {
			// correct result
		} catch (Exception e) {
			error("backward threw " + e.getClass().getName());
		}
		
		try {
			Iterator<String> iter = loop.iterator();
			if (iter.hasNext()) {
				error("Iterator.hasNext on empty loop is true");
			}
			try {
				iter.next();
				error("Iterator.next on empty loop did not throw exception");
			} catch (NoSuchElementException e) {
				// correct result
			} catch (Exception e) {
				error("Iterator.next on emtpy loop threw " + e.getClass().getName());
			}
		} catch (Exception e) {
			error("iterator on empty loop 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': 
			try {
				display();
			} catch (Exception e) {
				System.out.println("unexpected exception while testing iterator: " + e);
			}
				break;
				
			case '>':
			try {
				if (loop.size() == 0)
					System.out.println("no messages");
				else {
					loop.forward();
					currentContext();					
				}
			} catch (Exception e) {
				System.out.println("unexpected exception while testing forward: " + e);
			}
				break;
				
			case '<':
			try {
				if (loop.size() == 0)
					System.out.println("no messages");
				else {
					loop.backward();
					currentContext();	
				}
			} catch (Exception e) {
				System.out.println("unexpected exception while testing backward: " + e);
			}
				break;
				
			case 'r':
			try {
				if (loop.size() == 0) 
					System.out.println("no messages");
				else {
					int beforeSize = loop.size();
					loop.removeCurrent();
					if (loop.size() != beforeSize - 1)
						error("removeCurrent doesn't correctly update size");
					if (loop.size() == 0) {
						System.out.println("no messages");
						if (!loop.isEmpty())
							error("isEmpty incorrect after removing all messages");
					}
					else
						currentContext();	
				}
			} catch (Exception e) {
				System.out.println("unexpected exception while testing removeCurrent: " + e);
			}
				break;
				
			case 'b':
			try {
				msg = extract(input);
				if (msg != null) {
					int beforeSize = loop.size();
					loop.insert(msg);
					if (loop.size() != beforeSize + 1)
						error("insert doesn't correctly update size");
					if (loop.isEmpty())
						error("isEmpty is true after adding an item");
					currentContext();	
				}
				else
					System.out.println("invalid command");
			} catch (Exception e) {
				System.out.println("unexpected exception while testing insert: " + e);
			}
				break;			
				
			case 'q': 
				System.out.println("quit");
				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 (loop.size() < 1) {
			System.out.println("no messages");
			return;
		}
		
		Object current = loop.getCurrent();
		if (!checkCurrent && current instanceof DblListnode) {
			error("getCurrent returns a list node");
			badCurrent = true;
		}
		checkCurrent = true;		
		
		if (loop.size() > 2) {
			loop.backward();
			System.out.println(currString(loop.getCurrent()));
			loop.forward();
		}
		
		System.out.println("*** " + currString(loop.getCurrent()) + " ***");
		
		if (loop.size() > 1) {
			loop.forward();
			System.out.println(currString(loop.getCurrent()));
			loop.backward();
		}
	}

	private static void display() {
		if (loop.size() == 0)
			System.out.println("no messages");
		else {
			Iterator iter = loop.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;
			}
		}
	}

}