package course;

import java.util.NoSuchElementException;
import java.lang.IllegalStateException;
import java.lang.Iterable;
import java.util.Iterator;

public class CourseDirectory implements Iterable <CourseComparable>{
	
	   private int maxSize;
	    private int currentSize;
	    private CourseComparable [] courseArray;

	    CourseDirectory () {
	        maxSize = 4;  // initial maximum
	        currentSize = 0;
	        courseArray = new CourseComparable[maxSize];
	    }

	    public void add (CourseComparable person) {
	        // check if room in array, and double array size if needed
	        if (currentSize == maxSize) {
	            maxSize = 2*maxSize; 
	            CourseComparable [] temp = new CourseComparable[maxSize];
	            for (int i = 0; i < currentSize; i++)
	                temp[i] = courseArray[i];
	            courseArray = temp;  // update courseArray to the new, larger array
	        }

	        // add person to array, maintaining alphabetical order
	        int i = currentSize;
	        while (i > 0 && (person.compareTo(courseArray[i-1]) > 0)) {
	            courseArray[i] = courseArray[i-1];
	            i--;
	        }
	        courseArray[i] = person;
	        currentSize++;
	    }

	    public void print () {
	        System.out.println ("Directory Listing");
	        for (int i = 0; i < currentSize; i++) 
	            System.out.print(courseArray[i]);
	        System.out.println ("End of Listing");
	    }

	    public CourseComparable lookup (String subject, int number) {
	        int lo = 0;
	        int hi = currentSize;
	        int mid = (hi + lo)/2;
	        CourseComparable desiredItem = new CourseComparable (subject, number, 0, "");
	        while (lo < hi) {
	            if (courseArray[mid].equals(desiredItem))
	                return courseArray[mid];
	            if (courseArray[mid].compareTo(desiredItem) < 0)
	                lo = mid + 1;
	            else hi = mid;
	            mid = (hi + lo)/2;
	        }
	        return null;  // return a null entry if person not found

	    }
	    
	    // iterator to cycle through all courses with given number of credits
	    // by default, cycle through all courses
		public Iterator<CourseComparable> iterator() {
			return new LocalIterator ();
		}
		
		// cycle through courses with given number of credits
		public Iterator<CourseComparable> iterator(int credits) {
			return new LocalIterator (credits);
		}
		
		private class LocalIterator implements Iterator<CourseComparable> {
			private int lastReturned;  // last index returned by iterator
			private int numCredits;    // number of credits for desired courses
				                       // number < 0 indicates all courses desired
			private boolean removeAllowed; // identifies if remove can be called
			
			public LocalIterator () {
				lastReturned = -1;
				numCredits = -1;  
				removeAllowed = false;
			}
			
			public LocalIterator (int credits) {
				lastReturned = -1;
				numCredits = credits;  
				boolean removedAllowed = false;
			}
			
			public boolean hasNext () {
				int startIndex = lastReturned + 1;
				while (startIndex < currentSize) {
					if ((numCredits < 0)
						||(courseArray[startIndex].getCredits() == numCredits))
						return true;
					startIndex ++;
				}
				return false;
			}
			
			public CourseComparable next () {
				lastReturned++;
				while (lastReturned  < currentSize) {
					if ((numCredits < 0)
						||(courseArray[lastReturned].getCredits() == numCredits))
					{
						removeAllowed = true;
						return courseArray[lastReturned];
					}
					lastReturned++;
				}
				throw new NoSuchElementException ();
			}
			
			// remove deletes current element
			// throws exception if called twice before next
			
			public void remove() {
				if (removeAllowed)
				{
					// remove most recently removed course
					// slide later items down
					int index = lastReturned;
					currentSize--;
					while (index < currentSize)
					{
						courseArray [index] = courseArray [index+1];
						index++;
					}
					removeAllowed = false;
				}
				else
				{
					throw new IllegalStateException ();
				}
			}

		}
		
	    
	    public static void main (String args [])
	    {
	    	
	    	System.out.println ("Creating a partial directory of Grinnell courses");
	    	
	        // create directory of [mostly CS] courses
	    	CourseDirectory dir = new CourseDirectory ();
	    	dir.add(new CourseComparable ("CSC", 151.01, 4, "Functional Problem Solving"));
	        dir.add(new CourseComparable ("HIS", 225.01, 4, "Native American History, 1941-1865"));
	        dir.add(new CourseComparable ("HIS", 242.01, 4, "The Rise and Fall of the Soviet Union"));
	        dir.add(new CourseComparable ("HIS", 336.01, 4, "The European Metropolis"));
	        dir.add(new CourseComparable ("CSC", 151.02, 4, "Functional Problem Solving"));
	        dir.add(new CourseComparable ("CSC", 161.01, 4, "Imperative Problem Solving and Data Structures"));
	        dir.add(new CourseComparable ("CSC", 161.03, 4, "Imperative Problem Solving and Data Structures"));
	        dir.add(new CourseComparable ("CSC", 205.01, 4, "Computational Linguistics"));
	        dir.add(new CourseComparable ("LIN", 205.01, 4, "Computational Linguistics"));
	        dir.add(new CourseComparable ("CSC", 207.01, 4, "Algorithms and Object-oriented Design"));
	        dir.add(new CourseComparable ("CSC", 213.01, 4, "Operating Systems"));
	        dir.add(new CourseComparable ("CSC", 281.01, 1, "Learning from CS Alumni"));
	        dir.add(new CourseComparable ("CSC", 301.01, 4, "Analysis of Algorithms"));
	        dir.add(new CourseComparable ("LIN", 114.01, 4, "Introduction to General Linguistics"));
	        dir.add(new CourseComparable ("CSC", 214.01, 2, "Computer and Newtork Security"));
	        dir.add(new CourseComparable ("CSC", 232.01, 2, "Human-Computer Interaction"));
	        dir.add(new CourseComparable ("CSC", 321.01, 2, "Software Design Principles and Practices"));
	        dir.add(new CourseComparable ("CSC", 322.01, 2, "Team Software Community Project"));

	    	// print created directory
	        System.out.println ("Course directory listing through print method");
	        dir.print ();
	        
	        // test default behavior of iterator in foreach loop
	        System.out.println ("\nCourse directory listing through default iterator");
	        for (CourseComparable course : dir)
	        	System.out.print (course);
	        System.out.println ("End of Listing");
	        
	        // detailed testing of iterator
	        // default iterator
	        System.out.println ("Testing default iterator");
	        Iterator<CourseComparable> defIt = dir.iterator ();
	        System.out.println ("hasNext at start:" + defIt.hasNext());
	        System.out.println ("hasNext again:   " + defIt.hasNext());
	        System.out.print ("next - 1: " + defIt.next());
	        System.out.print ("next - 2: " + defIt.next());
	        System.out.print ("next - 3: " + defIt.next());
	        System.out.println ("hasNext after 3:" + defIt.hasNext());
	        System.out.println ("hasNext again:  " + defIt.hasNext());
	        while (defIt.hasNext())
		        System.out.print ("next in loop: " + defIt.next());
	        System.out.print ("hasNext at end:  " + defIt.hasNext());
	        try
	        {
	        	defIt.next();
	        }
	        catch (NoSuchElementException e)
	        {
	        	System.out.println ("iterator at end, so next threw exception");
	        }

	        // test iterator for 2-credit courses
	        System.out.println ("Testing iterator with 2-credit courses");
	        Iterator<CourseComparable> it2 = dir.iterator (2);
	        System.out.println ("hasNext at start:" + it2.hasNext());
	        System.out.println ("hasNext again:   " + it2.hasNext());
	        System.out.print ("next - 1: " + it2.next());
	        System.out.println ("hasNext after 1:" + it2.hasNext());
	        System.out.print ("next - 2: " + it2.next());
	        System.out.print ("next - 3: " + it2.next());
	        System.out.println ("hasNext after 3:" + it2.hasNext());
	        System.out.println ("hasNext again:  " + it2.hasNext());
	        while (it2.hasNext())
		        System.out.print ("next in loop: " + it2.next());
	        System.out.print ("hasNext at end:  " + it2.hasNext());
	        try
	        {
	        	it2.next();
	        }
	        catch (NoSuchElementException e)
	        {
	        	System.out.println ("iterator at end, so next threw exception");
	        }
	        
	        // testing remove
	        // test iterator for 2-credit course
	        System.out.println ("Testing iterator with 2-credit courses and remove");
	        Iterator<CourseComparable> itRem = dir.iterator (2);
	        try
	        {
	        	itRem.remove();
	        }
	        catch (IllegalStateException e)
	        {
	        	System.out.println ("iterator next not called, so cannot remove");
	        }

	        System.out.println ("hasNext at start:" + itRem.hasNext());
	        System.out.println ("hasNext again:   " + itRem.hasNext());
	        System.out.print ("next - 1: " + itRem.next());
	        System.out.println ("hasNext after 1:" + itRem.hasNext());
	        System.out.print ("next - 2: " + itRem.next());
	        itRem.remove ();
	        try
	        {
	        	itRem.remove();
	        }
	        catch (IllegalStateException e)
	        {
	        	System.out.println ("iterator next called twice, so cannot remove");
	        }

	        System.out.print ("next - 3: " + itRem.next());
	        itRem.remove ();
	        System.out.println ("hasNext after 3:" + itRem.hasNext());
	        System.out.println ("hasNext again:  " + itRem.hasNext());
	        System.out.print ("hasNext at end:  " + itRem.hasNext());
	        try
	        {
	        	itRem.next();
	        }
	        catch (NoSuchElementException e)
	        {
	        	System.out.println ("iterator at end, so next threw exception");
	        }
	        
	        // check full course directory to confirm correct courses removed
	        // test default behavior of iterator in foreach loop
	        System.out.println ("\nCourse directory listing after remove calls");
	        for (CourseComparable course : dir)
	        	System.out.print (course);
	        System.out.println ("End of Listing");

	        
	    }
	    
	
}
