// Ben Douglass: Tester for COP 3330 Program 4, Spring 2006
// Comments added by Arup Guha

package university;

import java.util.*;
import java.io.*;

public class UniversityMain {
	
	public static TreeSet<Department> dept;
	public static Scanner stdin;

	public static final int STUDENT = 1;
	public static final int FACULTY = 2;
	public static final int STAFF = 3;

	// Throws an exception if the next line read in from fin isn't x.	
	public static void readText(Scanner fin, String x) throws Exception
	{
		String s = fin.nextLine().trim();
		if(s.compareTo(x) != 0)
			throw new Exception("Read " +s + " instead of " + x);
	}
	
	// Throws an Exception if the next token read in from fin isn't x.
	public static void readTextBit(Scanner fin, String x) throws Exception
	{
		String s = fin.next().trim();
		if(s.compareTo(x) != 0)
			throw new Exception("Read(bit) " + s + " instead of " + x);
	}
	
	// Reads in information about a UniversityMember from fin and returns a UniversityMember object.
	// The goal here is to read in information from a file that has stored information about a
	// University database.
	public static UniversityMember readUniversityMember(Scanner fin) throws Exception
	{
		
		String s = fin.nextLine().trim();
		String lname;
		String fname;
		String ssn;
		String addr;
		String title;
		double gpa;
		
		if(s.compareTo("") == 0)
			return null;

		// If the current member is a faculty member, read in the information in the given format.
		else if(s.compareTo("Faculty") == 0)
		{
			readTextBit(fin,"Last");
			readTextBit(fin,"name:");
			lname = fin.nextLine().trim();
			readTextBit(fin,"First");
			readTextBit(fin,"name:");
			fname = fin.nextLine().trim();
			readTextBit(fin,"SSN:");
			ssn = fin.nextLine().trim();
			readTextBit(fin,"Address:");
			addr = fin.nextLine().trim();
			readTextBit(fin,"Title:");
			title = fin.nextLine().trim();
			readText(fin,"---------");
			return new Faculty(fname,lname,ssn,addr,title);

		}

		// If the current member is a suppoer personnel, read in the information in the given format.
		else if(s.compareTo("SupportPersonnel")==0)
		{	
			readTextBit(fin,"Last");
			readTextBit(fin,"name:");
			lname = fin.nextLine().trim();
			readTextBit(fin,"First");
			readTextBit(fin,"name:");
			fname = fin.nextLine().trim();
			readTextBit(fin,"SSN:");
			ssn = fin.nextLine().trim();
			readTextBit(fin,"Address:");
			addr = fin.nextLine().trim();
			readText(fin,"---------");
			return new SupportPersonnel(fname,lname,ssn,addr);
		}

		// Same for a student.
		else if(s.compareTo("Student")==0)
		{	
			readTextBit(fin,"Last");
			readTextBit(fin,"name:");
			lname = fin.nextLine().trim();
			readTextBit(fin,"First");
			readTextBit(fin,"name:");
			fname = fin.nextLine().trim();
			readTextBit(fin,"SSN:");
			ssn = fin.nextLine().trim();
			readTextBit(fin,"Address:");
			addr = fin.nextLine().trim();
			readTextBit(fin,"GPA:");
			gpa = fin.nextDouble();
			fin.nextLine();
			readText(fin,"---------");
			return new Student(fname,lname,ssn,addr,gpa);
		}

		// Otherwise, the type of UniversityMember is incorrect, throw an exception.
		else
			throw new Exception();
		
	}
	
	// Reads in an academic department from fin - the file should store existing university information.
	public static void readAcademicDepartment(Scanner fin) throws Exception
	{
		String name = fin.nextLine().trim();
		AcademicDepartment d = new AcademicDepartment(name);
		dept.add(d);

		// These lines have to be in the input file exactly.
		readText(fin,"===================");
		readText(fin,"Employees");
		readText(fin,"---------");
		
		// Read in all the employees.
		Employee e;
		while((e = (Employee)readUniversityMember(fin)) != null)
			d.addEmployee(e);
		
		// These lines have to be in the input file exactly also at this point.
		readText(fin, "Students");
		readText(fin,"---------");
		
		// Read in all the students.
		Student stu;
		while((stu = (Student)readUniversityMember(fin)) != null)
			d.addStudent(stu);
	}

	// Reads in an non-academic department from fin - the file should store existing university information.
	public static void readNonAcademicDepartment(Scanner fin) throws Exception
	{
		String name = fin.nextLine().trim();
		NonAcademicDepartment d = new NonAcademicDepartment(name);
		dept.add(d);

		// These lines have to be in the file exactly.
		readText(fin,"===================");
		readText(fin,"Employees");
		readText(fin,"---------");
		
		// Read in all the employees.
		Employee e;
		while((e = (Employee)readUniversityMember(fin)) != null)
			d.addEmployee(e);
	}

	// Read in information about a single department from fin.
	public static void readDepartment(Scanner fin) throws Exception
	{
		String s = fin.next();
		fin.nextLine();

		// This line has to match exactly.
		if(s.compareTo("===================") != 0)
			throw new Exception();
		s = fin.nextLine().trim();

		// Find the matching type of department. If you can't, throw an exception.
		if(s.compareTo("Academic Department") == 0)
			readAcademicDepartment(fin);
		else if(s.compareTo("Non-Academic Department")==0)
			readNonAcademicDepartment(fin);
		else
			throw new Exception();
	}
	
	// Takes care of reading in a whole file storing pre-existing University information.
	public static void readFile()
	{
		Scanner fin = null;

		// Try to find previous information!
		try{
			fin = new Scanner(new File("univdata.txt"));
			dept = new TreeSet<Department>();
			while(fin.hasNext())
				readDepartment(fin);
				
		}
		catch(Exception e)
		{

			// Continue with no existing information here.
			if(e instanceof FileNotFoundException)
				System.out.println("No previous University data found");
			else if(e instanceof ClassCastException)
				System.out.println("Your inheritance hierarchy might be set up incorrectly");
			else
			{
				e.printStackTrace();
				System.out.println("Could not interpret file correctly");
			}
		
			
			dept = new TreeSet<Department>();
		}
		finally
		{
			if(fin != null)
				fin.close();
		}
	}
	
	// Writes an output file of all the University information.
	public static void writeFile()
	{
		PrintStream fout = null;

		// Try opening the file to place the output.
		try
		{
			fout= new PrintStream(new File("univdata.txt"));
			for(Department d: dept)
			{
				fout.println(d);
				fout.println();
			}
		}
		catch(Exception e)
		{
			System.out.println("Error writing file: " + e);
		}
		finally
		{
			if(fout!= null)
				fout.close();
		}
	}
	
	// Reads in an integer from the user - this handles error checking.
	public static int readInt()
	{
		while(true)
		{
			try{
				int choice = stdin.nextInt();
				stdin.nextLine();
				return choice;
			}catch(Exception e){
				System.out.println("Please enter an integer");
				stdin.nextLine();
			}
		}
	}

	
	// Handles error checking for reading in an int inbetween low and high, inclusive.
	public static int readInt(int low, int high)
	{
		while(true)
		{
			try{
				int choice = stdin.nextInt();
				stdin.nextLine();
				if(choice >= low && choice <= high)
					return choice;	
			}catch(Exception e){
				stdin.nextLine();
			}
			System.out.println("Please enter an integer between "+low+" and " + high);
		}
	}

	// Handles reading in the GPA and the associated error checking.
	public static double readGPA()
	{
		while(true)
		{
			try{
				double choice = stdin.nextDouble();
				stdin.nextLine();
				if(choice >= 0 && choice <= 4)
					return choice;	
			}catch(Exception e){
				stdin.nextLine();
			}
			System.out.println("Please enter an number between 0.00 and 4.00");
		}
	}

	// Adds a department to the current object if it's new. academic determines what type of department
	// is added.
	public static void createDepartment(boolean academic)
	{
		System.out.println("Enter a name for the department");
		String name = stdin.nextLine().trim();
		Department depart;
		if(academic)
			depart = new AcademicDepartment(name);
		else
			depart = new NonAcademicDepartment(name);
		if(dept.add(depart))
		{
			System.out.println("The department "+name+" has been added to the university");
		}
		else
		{
			System.out.println("Unable to create department(that name already existed)");
		}
	}
	
	// Creates a person of one of the following types: STUDENT, FACULTY or STAFF.
	public static void createPerson(int type)
	{
		String lname;
		String fname;
		String ssn;
		String addr;
		String title="";
		double gpa=0;
		
		UniversityMember guy = null;
		
		// Error checking, need a department before you add anyone.
		if(dept.size() == 0)
		{
			System.out.println("You can't add people to the university because it has no departments");
			return;
		}
		
		// Get all the requisite information.
		System.out.println("Enter the person's last name");
		lname = stdin.nextLine();
		System.out.println("Enter the person's first name");
		fname = stdin.nextLine();
		System.out.println("Enter the person's social security number");
		ssn = stdin.nextLine();
		System.out.println("Enter the person's address");
		addr = stdin.nextLine();
			
		// Ask the extra questions based on the type of person.	
		if(type == FACULTY)
		{
			System.out.println("Enter the person's academic title");
			title = stdin.nextLine();
		}
		if(type == STUDENT)
		{
			System.out.println("Enter the person's grade point average");
			gpa = readGPA();
		}

		// Add a faculty member.
		if(type == FACULTY)
		{
			guy = new Faculty(fname,lname,ssn,addr,title);
			System.out.println(((Faculty)guy).getTitle() + " " + guy.getFirstName() + " " + guy.getLastName());
			System.out.println("with social security number " + guy.getSocialSecurity());
			System.out.println("living at " + guy.getAddress());
			System.out.println("Is this the faculty member you want to add?");
		}

		// Or a student.
		else if(type == STUDENT)
		{
			guy = new Student(fname,lname,ssn,addr,gpa);
			System.out.println(guy.getFirstName() + " " + guy.getLastName());
			System.out.println("with social security number " + guy.getSocialSecurity());
			System.out.println("living at " + guy.getAddress());
			System.out.printf("and gpa %.2f%n",((Student)guy).getGpa());
			System.out.println("Is this the student you want to add?");
		}

		// Or a staff member.
		else if(type == STAFF)
		{	guy = new SupportPersonnel(fname,lname,ssn,addr);
		
			System.out.println(guy.getFirstName() + " " + guy.getLastName());
			System.out.println("with social security number " + guy.getSocialSecurity());
			System.out.println("living at " + guy.getAddress());
			System.out.println("Is this the employee you want to add?");
		}

		// There aren't any other types of people!
		else
		{
			throw new RuntimeException("Invalid university member type");
		}

		System.out.println("(1) Yes or (2) No");
		int choice = readInt(1,2);

		// Quit if they don't want to add the person!
		if(choice == 2)
			return;
		
		Department[] d = new Department[0];
		d = dept.toArray(d);
		
		// Find the department the person should be added.
		System.out.println("Which department would you like to add the person to?");
		for(int i=0;i<d.length;i++)
		{
			System.out.println((i+1) + ") "+d[i].getName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		choice = readInt(0,d.length);

		// Let them opt out again!
		if(choice == 0)
			return;

		// Adjust for the off-by-one listing previously.
		choice--;

		// Deal with adding a student.
		if(type == STUDENT)
		{
			// Can't add a student to a NonAcademicDepartment.
			if(!(d[choice] instanceof AcademicDepartment))
			{
				System.out.println("Unable to add student(students can only be added to academic departments)");
				return;
			}

			// This is the regular case.
			if( ((AcademicDepartment)d[choice]).addStudent((Student)guy))
			{
				System.out.println("Student successfully added");
				System.out.println(guy);
				return;
			}
			else
			{
				System.out.println("Unable to add student(that student was already in the department)");
				return;
			}
				
		}
		
		// Adding a non-student.
		if(d[choice].addEmployee((Employee)guy))
		{
			System.out.println("Employee successfully added");
			System.out.println(guy);
		}
		else
		{
			System.out.println("Unable to add employee(that employee was already in the department)");
		}
		
	}
	
	// Removes a department of the user's choice.
	public static void removeDepartment()
	{
		Department[] d = new Department[0];
		d = dept.toArray(d);
		System.out.println("Which department would you like to remove?");
		for(int i=0;i<d.length;i++)
		{
			System.out.println((i+1) + ") "+d[i].getName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		int choice = readInt(0,d.length);
		if(choice == 0)
			return;
		choice--;
		dept.remove(d[choice]);
	}
	
	// Removes an Employee of the user's choice.
	public static void removeEmployee()
	{
		Department[] d = new Department[0];
		d = dept.toArray(d);
		System.out.println("Which department would you like to remove an employee from?");
		for(int i=0;i<d.length;i++)
		{
			System.out.println((i+1) + ") "+d[i].getName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		int choice = readInt(0,d.length);
		if(choice == 0)
			return;
		choice--;
		
		
		
		Employee[] s = new Employee[0];
		s = d[choice].getEmployees().toArray(s);
		
		if(s.length == 0){
			System.out.println("That department has no employees");
			return;
		}

		// List out the choices of employees to remove.
		System.out.println("Which Employee would you like to remove?");
		for(int i=0;i<s.length;i++)
		{
			System.out.println((i+1) + ") "+s[i].getLastName() + ", " + s[i].getFirstName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		int choice2 = readInt(0,s.length);
		if(choice2 == 0)
			return;
		choice2--;

		// Remove the appropriate employee.
		d[choice].removeEmployee(s[choice2]);
	}
	
	// Same deal as above: remove the student the user desires to remove.
	public static void removeStudent()
	{
		Department[] d = new Department[0];
		d = dept.toArray(d);
		System.out.println("Which department would you like to remove a student from?");
		for(int i=0;i<d.length;i++)
		{
			System.out.println((i+1) + ") "+d[i].getName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		int choice = readInt(0,d.length);
		if(choice == 0)
			return;
		choice--;
		
		if(!(d[choice] instanceof AcademicDepartment)){
			System.out.println("That department has no students");
			return;
		}
		
		Student[] s = new Student[0];
		s = ((AcademicDepartment)d[choice]).getStudents().toArray(s);
		
		if(s.length == 0){
			System.out.println("That department has no students");
			return;
		}
		System.out.println("Which student would you like to remove?");
		for(int i=0;i<s.length;i++)
		{
			System.out.println((i+1) + ") "+s[i].getLastName() + ", " + s[i].getFirstName());
		}
		System.out.println();
		System.out.println("0) Never mind");
		int choice2 = readInt(0,s.length);
		if(choice2 == 0)
			return;
		choice2--;
		
		((AcademicDepartment)d[choice]).removeStudent(s[choice2]);
	}
	
	// Print out the menu of main choices and read this in from the user.
	public static int getChoice()
	{
		int choice;
		
		System.out.println("These are your options:");
		System.out.println("1) Create a new academic department");
		System.out.println("2) Create a new non-academic department");
		System.out.println("3) Create a new student");
		System.out.println("4) Create a new support employee");
		System.out.println("5) Create a new faculty member");
		System.out.println("6) Remove a student");
		System.out.println("7) Remove an employee");
		System.out.println("8) Remove a department");
		System.out.println("9) Display the entire university");
		System.out.println("0) Save and quit");
		System.out.println("Make your choice");
		choice = readInt(0,9);
		if(choice == 1)
			createDepartment(true);
		else if(choice == 2)
			createDepartment(false);
		else if(choice == 3)
			createPerson(STUDENT);
		else if(choice == 4)
			createPerson(STAFF);
		else if(choice == 5)
			createPerson(FACULTY);
		else if(choice == 6)
			removeStudent();
		else if(choice == 7)
			removeEmployee();
		else if(choice == 8)
			removeDepartment();
			
		else if(choice == 9)
		{
			if(dept.size() == 0)
				System.out.println("There are no departments in the University");
			for(Department d : dept)
			{
				System.out.println(d);
				System.out.println();
			}
		}
		
		
		
		return choice;
	}
	
	// Run everything!
	public static void main(String[] args)
	{
		System.out.println("Welcome to the University tester");
		readFile();
		int choice=-1;
		stdin = new Scanner(System.in);
		do{
			choice = getChoice();
		}while(choice != 0);
		
		writeFile();
	}
}
