package com.gmscheduler.model;

import com.gmscheduler.model.Person;
import com.gmscheduler.model.Timeframe;
import java.util.LinkedList;
import java.util.ListIterator;

/**
 * A <code>Schedule</code> objects represents a person and a list of their time frames.
 * 
 * @author Peter Deschere
 * @author Ashton Galya
 * @author Rebecca Beale
 * @author Jason Fillo
 * @version 1.3
 * @see com.gmscheduler.model.Person
 * @see com.gmscheduler.model.Timeframe
 */
public class Schedule {
	//default constructor
	public Schedule(){
		this.schedule = new LinkedList<Timeframe>();
		this.person = new Person();
		this.description = new String();
	}
	
	//class constructor
	public Schedule(Person owner){
		this.person = new Person(owner);
		this.schedule = new LinkedList<Timeframe>();		
		this.description = new String();
	}
	
	//class constructor
	public Schedule(Person owner, LinkedList<Timeframe> schedule) {
		this.person = new Person(owner);
		this.schedule = schedule;
		this.description = new String();
		ListIterator ltf = schedule.listIterator();
		//try {
		while (ltf.hasNext()) {
			//do {
			Timeframe t = (Timeframe)ltf.next();
			t.setScheduleid(this.id);
		}
			//} while (ltf.hasNext());
		//} catch (NoSuchElementException e) {
			//do nothing
		//}
	}
	
	//class constructor
	public Schedule(Person owner, Timeframe schedule) {
		this.person = new Person(owner);
		this.schedule = new LinkedList<Timeframe>();
		schedule.setScheduleid(this.id);
		this.schedule.add(schedule);
		this.description = new String();
	}
	
	//copy constructor
	public Schedule(Schedule u) {
		person = u.getPerson();
		this.schedule = new LinkedList<Timeframe>();
		schedule = u.getSchedule();
		if (!(u.getId()==null)) {this.id = new Long(u.getId());}
		this.description = new String(u.getDescription());
	}
	
	/**
	 * Gets the Description of the schedule
	 * 
	 * @return the description of the schedule
	 */
	public String getDescription() {
		return description;
	}
	
	/**
	 * Sets the description
	 * 
	 * @param nd the description of the schedule
	 */
	public void setDescription(String nd) {
		this.description = nd;
	}
	
	/**
	 * Gets the database id of the schedule
	 * 
	 * @return the id of the schedule in the database
	 */
	public Long getId() {
		return id;
	}
	
	/**
	 * Sets the id of the Schedule
	 * 
	 * @param id the schedule database id
	 */
	private void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the owner of the schedule
	 * 
	 * @return the <code>Person</code> who own's the schedule
	 */
	public Person getPerson() {
		return person;
	}
	
	/**
	 * Gets the timeframes of the schedule
	 * 
	 * @return a linked list of the schedule <code>Timeframe</code>s
	 */
	public LinkedList getSchedule() {
		return schedule;
	}
	
	/**
	 * Sets the person
	 * 
	 * @param owner the <code>Person</code> who the schedule belongs to
	 */
	public void setPerson(Person person) {
		this.person = new Person(person);
	}
	
	/**
	 * Sets the schedule from a linked list of timeframes
	 * 
	 * @param schedule a linked list of <code>Timeframe</code>s
	 */
	public void setSchedule(LinkedList<Timeframe> schedule) {
		this.schedule = schedule;
	}

	public String toStringMilitary() {
		return person.toString() + ": " + this.toStringScheduleMilitary();
	}
	
	public String toString() {
		return person.toString() + ": " + this.toStringSchedule();
	}
	
	/**
	 * Gets a string of all the schedules delimited by " | "
	 * 
	 * @return a string concatination of all the schedules
	 */
	public String toStringSchedule() {
		String x = "";
		Timeframe y;
		for(int i = 0; i<schedule.size();i++) {
			y = (Timeframe)schedule.get(i);
			x = x + y.toString() + ", ";
		}
		if (x.length() == 0) { return new String(x);} 
		else{return new String(x.substring(0,x.length()-2));}
	}
	
	/**
	 * Gets a string of all the schedules delimited by " | "
	 * 
	 * @return a string concatination of all the schedules
	 */
	public String toStringScheduleMilitary() {
		String x = "";
		Timeframe y;
		for(int i = 0; i<schedule.size();i++) {
			y = (Timeframe)schedule.get(i);
			x = x + y.toStringMilitary() + ", ";
		}
		if (x.length() == 0) { return new String(x);} 
		else{return new String(x.substring(0,x.length()-2));}
	}

	/**
	 * Calls the <code>Timeframe</code> setScheduleId to update the <code>Timeframe</code>'s
	 * parent id.  Must be called after the schedule is saved and before the Timeframe
	 * is saved.
	 */
	public void updateTimeframeid() {
		Timeframe tf;
		ListIterator tlist = schedule.listIterator();
		while (tlist.hasNext()){
			tf = (Timeframe)tlist.next();
			tf.setScheduleid(this.id);
		}
	}
	
	/**
	 * Adds one more timeframe to the schedule
	 * 
	 * @param newtime a new <code>Timeframe</code> to add to the schedule
	 */
	public void addTimeframe(Timeframe newtime) {
		newtime.setScheduleid(this.id);
		schedule.add(newtime);
	}
	
	/**
	 * Removes one timeframe from the schedule
	 * 
	 * @param t a <code>Timeframe</code> to remove from the schedule
	 */
	public void removeTimeframe(Timeframe t) {
		Timeframe tf;
		boolean done = false;
		ListIterator tlist = schedule.listIterator();
		while (tlist.hasNext() && !done) {
			tf = (Timeframe)tlist.next();
			
			// found a similar timeframe
			if (t.toString().compareTo(tf.toString())==0) {
				tlist.remove();
				done = true;
			}
		}		
	}
	
	/**
	 * Adds one or more timeframes to the schedule
	 * 
	 * @param u a linked list of <code>Timeframe</code>s to add to the schedule
	 */
	public void add(LinkedList<Timeframe> u) {
		ListIterator tlist = u.listIterator();
		while (tlist.hasNext()) {
			this.addTimeframe((Timeframe)tlist.next());
		}
	}
	
	/**
	 * Takes a schedule and removes the timeframes in the given schedule from this schedule
	 * @param outschedule a <code>Schedule</code> that contains busy times to remove from this schedule
	 */
	public void difference(Schedule outschedule) {
		// loop through outschedule list of timeframes and compare
		// them to all timeframes in this, modify in one of 5 ways:
		// 1) out.day<>this.day or (out.start & out.end < start & end) 
		//    or (out.start & out.end > start & end)
		// 2) out.start <= start and out.end > start < end
		// 3) out.start <= start and out.end > end
		// 4) out.start > start and out.end > start > end
		// 5) out.start > start and out.end > start < end
		final LinkedList outlist = outschedule.getSchedule();
		final ListIterator outit = outlist.listIterator();
		Timeframe tempout;
		
		if (outlist.isEmpty()) {return;}

		// iterate once through each out timeframe and compare with
		// each timeframe in this
		do {
			tempout = (Timeframe)outit.next();
			busyTimeframe(tempout);
		}
		while (outit.hasNext());
		
	}
	
	/**
	 * Remove a busy timeframe from this schedules list of timeframes.
	 * 
	 * @param tempout a timeframe to remove from this schedule
	 */
	private void busyTimeframe(Timeframe tempout) {
		final ListIterator thisit = this.schedule.listIterator();
		// compare to all timeframes in this, modify in one of 5 ways:
		// 1) out.day<>this.day or (out.start & out.end < start & end) 
		//    or (out.start & out.end > start & end)
		// 2) out.start <= start and out.end > start < end
		// 3) out.start <= start and out.end > end
		// 4) out.start > start and out.end > start > end
		// 5) out.start > start and out.end > start < end
		
		Timeframe tempin;
		String temp;

		try {
			do {
				// check against above five states
				tempin = (Timeframe)thisit.next();
				if (tempout.getDay().equalsIgnoreCase(tempin.getDay())) {
					//they are same day
					if (tempout.getStartDouble()<=tempin.getStartDouble() 
						&& tempout.getEndDouble()>tempin.getStartDouble()
						&& tempout.getEndDouble()<tempin.getEndDouble()) {
						// case 2 - busy covers begining of this
						tempin.setStart(tempout.getEnd());
						thisit.set(tempin);
						
					} else if (tempout.getStartDouble()<=tempin.getStartDouble() 
							&& tempout.getEndDouble()>=tempin.getEndDouble()) {
							// case 3 - delete this
						thisit.remove();
						
					} else if (tempout.getStartDouble()>tempin.getStartDouble() 
							&& tempout.getEndDouble()>tempin.getStartDouble()
							&& tempout.getEndDouble()>=tempin.getEndDouble()) {
							// case 4  - busy covers end of this
						tempin.setEnd(tempout.getStart());
						
					} else if (tempout.getStartDouble()>tempin.getStartDouble() 
							&& tempout.getEndDouble()>tempin.getStartDouble()
							&& tempout.getEndDouble()<tempin.getEndDouble()) {
							// case 5 - busy is in middle of this
						temp = tempin.getEnd();
						tempin.setEnd(tempout.getStart());
						thisit.set(tempin);
						thisit.add(new Timeframe(tempin.getDay(),tempout.getEnd(),temp));
						//tempin.setStart(tempout.getEnd());
						//tempin.setEnd(temp);
					}
				}
			} while (thisit.hasNext());
		} catch (java.util.NoSuchElementException e) {
			//ignore and move on
		}
	}	
	
	public boolean isEmpty() {
		if (person.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * The owner of the schedule
	 */
	private Person person;
	/**
	 * A list of timeframes
	 */
	private LinkedList<Timeframe> schedule;
	/**
	 * The id of the schedule
	 */
	private Long id;
	/**
	 * The description of the schedule
	 */
	private String description;
}
