<?php
/*********************************************************************************
 * The contents of this file are subject to the TimeTrex Public License Version
 * 1.1.0 ("License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.TimeTrex.com/TPL
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * All copies of the Covered Code must include on each user interface screen:
 *    (i) the "Powered by TimeTrex" logo and
 *    (ii) the TimeTrex copyright notice
 * in the same form as they appear in the distribution.  See full license for
 * requirements.
 *
 * The Original Code is: TimeTrex Open Source
 * The Initial Developer of the Original Code is TimeTrex Payroll Services
 * Portions created by TimeTrex are Copyright (C) 2004-2007 TimeTrex Payroll Services;
 * All Rights Reserved.
 *
 ********************************************************************************/
/*
 * $Revision: 2158 $
 * $Id: ScheduleFactory.class.php 2158 2008-09-15 23:26:15Z ipso $
 * $Date: 2008-09-15 16:26:15 -0700 (Mon, 15 Sep 2008) $
 */

/**
 * @package Module_Schedule
 */
class ScheduleFactory extends Factory {
	protected $table = 'schedule';
	protected $pk_sequence_name = 'schedule_id_seq'; //PK Sequence name

	protected $user_date_obj = NULL;
	protected $schedule_policy_obj = NULL;
	protected $absence_policy_obj = NULL;

	function _getFactoryOptions( $name ) {

		$retval = NULL;
		switch( $name ) {
			case 'status':
				$retval = array(
										10 => TTi18n::gettext('Working'),
										20 => TTi18n::gettext('Absent')
									);
				break;
		}

		return $retval;
	}

	function getSchedulePolicyObject() {
		if ( is_object($this->schedule_policy_obj) ) {
			return $this->schedule_policy_obj;
		} else {
			$splf = new SchedulePolicyListFactory();
			$splf->getById( $this->getSchedulePolicyID() );
			if ( $splf->getRecordCount() > 0 ) {
				$this->schedule_policy_obj = $splf->getCurrent();
				return $this->schedule_policy_obj;
			}

			return FALSE;
		}
	}

	function getAbsencePolicyObject() {
		if ( is_object($this->absence_policy_obj) ) {
			return $this->absence_policy_obj;
		} else {
			$aplf = new AbsencePolicyListFactory();
			$aplf->getById( $this->getAbsencePolicyID() );
			if ( $aplf->getRecordCount() > 0 ) {
				$this->absence_policy_obj = $aplf->getCurrent();
			}

			return $this->absence_policy_obj;
		}
	}

	function getUserDateObject() {
		if ( is_object($this->user_date_obj) ) {
			return $this->user_date_obj;
		} else {
			$udlf = new UserDateListFactory();
			$udlf->getById( $this->getUserDateID() );
			if ( $udlf->getRecordCount() > 0 ) {
				$this->user_date_obj = $udlf->getCurrent();
				return $this->user_date_obj;
			}

			return FALSE;
		}
	}

	function findUserDate($user_id, $epoch) {
		//Get pay period start/continuous time
		//FIXME: Add proper schedule support for new_day_trigger_time.
		/*
		$ppslf = new PayPeriodScheduleListFactory();
		$ppslf->getByUserId( $user_id );
		if ( $ppslf->getRecordCount() == 1 ) {
			$pps_obj = $ppslf->getCurrent();
			Debug::Text(' Pay Period Schedule Maximum Shift Time: '. $pps_obj->getMaximumShiftTime(), __FILE__, __LINE__, __METHOD__,10);

			$plf = new PunchListFactory();
			$plf->getFirstPunchByUserIDAndEpoch( $user_id, $epoch, $pps_obj->getMaximumShiftTime() );

			if ( $plf->getRecordCount() > 0 ) {
				$p_obj = $plf->getCurrent();
				if ( ( $epoch - $p_obj->getTimeStamp() ) <= $pps_obj->getMaximumShiftTime() ) {
					$user_date_id = $p_obj->getPunchControlObject()->getUserDateID();
					Debug::text(' User Date ID found: '. $user_date_id, __FILE__, __LINE__, __METHOD__,10);

					$this->setUserDateID( $user_date_id, TRUE );
					return TRUE;
				}
			}

		}
		*/

		return $this->setUserDate( $user_id, $epoch );
	}

	function setUserDate($user_id, $date) {
		$user_date_id = UserDateFactory::findOrInsertUserDate( $user_id, $date);
		Debug::text(' User Date ID: '. $user_date_id, __FILE__, __LINE__, __METHOD__,10);
		if ( $user_date_id != '' ) {
			$this->setUserDateID( $user_date_id );
			return TRUE;
		}
		Debug::text(' No User Date ID found', __FILE__, __LINE__, __METHOD__,10);

		return FALSE;
	}

	function getUserDateID() {
		if ( isset($this->data['user_date_id']) ) {
			return $this->data['user_date_id'];
		}

		return FALSE;
	}
	function setUserDateID($id, $skip_check = FALSE ) {
		$id = trim($id);

		$udlf = new UserDateListFactory();

		if (  $skip_check == TRUE
				OR
				$this->Validator->isResultSetWithRows(	'user_date',
														$udlf->getByID($id),
														TTi18n::gettext('Invalid User/Date. Pay Period may be locked')
														) ) {
			$this->data['user_date_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getStatus() {
		if ( isset($this->data['status_id']) ) {
			return $this->data['status_id'];
		}

		return FALSE;
	}
	function setStatus($status) {
		$status = trim($status);

		$key = Option::getByValue($status, $this->getOptions('status') );
		if ($key !== FALSE) {
			$status = $key;
		}

		if ( $this->Validator->inArrayKey(	'status',
											$status,
											TTi18n::gettext('Incorrect Status'),
											$this->getOptions('status')) ) {

			$this->data['status_id'] = $status;

			return FALSE;
		}

		return FALSE;
	}

	function getStartTime( $raw = FALSE ) {
		if ( isset($this->data['start_time']) ) {
			return TTDate::strtotime( $this->data['start_time'] );
			/*
			if ( $raw === TRUE) {
				return $this->data['start_time'];
			} else {
				return TTDate::strtotime( $this->data['start_time'] );
			}
			*/
		}

		return FALSE;
	}
	function setStartTime($epoch) {
		$epoch = trim($epoch);

		if 	(	$this->Validator->isDate(		'start_time',
												$epoch,
												TTi18n::gettext('Incorrect start time'))

			) {

			$this->data['start_time'] = $epoch;

			return TRUE;
		}

		return FALSE;
	}

	function getEndTime( $raw = FALSE ) {
		if ( isset($this->data['end_time']) ) {
			return TTDate::strtotime( $this->data['end_time'] );
			/*
			if ( $raw === TRUE) {
				return $this->data['end_time'];
			} else {
				return TTDate::strtotime( $this->data['end_time'] );
			}
			*/
		}

		return FALSE;
	}
	function setEndTime($epoch) {
		$epoch = trim($epoch);

		if 	(	$this->Validator->isDate(		'end_time',
												$epoch,
												TTi18n::gettext('Incorrect end time'))

			) {

			$this->data['end_time'] = $epoch;

			return TRUE;
		}

		return FALSE;
	}


	function calcTotalTime() {
		if ( $this->getSchedulePolicyObject() != FALSE ) {
			if ( $this->getSchedulePolicyObject()->getMealPolicyObject() != FALSE ) {
				if ( $this->getSchedulePolicyObject()->getMealPolicyObject()->getType() == 10
						OR $this->getSchedulePolicyObject()->getMealPolicyObject()->getType() == 20 ) {
					$total_time = ( $this->getEndTime() - $this->getStartTime() );

					if ( $total_time > $this->getSchedulePolicyObject()->getMealPolicyObject()->getTriggerTime() ) {
						$total_time -= $this->getSchedulePolicyObject()->getMealPolicyObject()->getAmount();
					}

					return $total_time;
				}
			}
		}

		$total_time = ( $this->getEndTime() - $this->getStartTime() );

		return $total_time;
	}

	function getTotalTime() {
		if ( isset($this->data['total_time']) ) {
			return (int)$this->data['total_time'];
		}
		return FALSE;
	}
	function setTotalTime($int) {
		$int = (int)$int;

		if 	(	$this->Validator->isNumeric(		'total_time',
													$int,
													TTi18n::gettext('Incorrect total time')) ) {
			$this->data['total_time'] = $int;

			return TRUE;
		}

		return FALSE;
	}


	function getSchedulePolicyID() {
		if ( isset($this->data['schedule_policy_id']) ) {
			return $this->data['schedule_policy_id'];
		}

		return FALSE;
	}
	function setSchedulePolicyID($id) {
		$id = trim($id);

		if ( $id == '' OR empty($id) ) {
			$id = NULL;
		}

		$splf = new SchedulePolicyListFactory();

		if ( $id == NULL
				OR
				$this->Validator->isResultSetWithRows(	'schedule_policy',
														$splf->getByID($id),
														TTi18n::gettext('Schedule Policy is invalid')
													) ) {

			$this->data['schedule_policy_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getAbsencePolicyID() {
		if ( isset($this->data['absence_policy_id']) ) {
			return $this->data['absence_policy_id'];
		}

		return FALSE;
	}
	function setAbsencePolicyID($id) {
		$id = trim($id);

		if ( $id == '' OR empty($id) ) {
			$id = NULL;
		}

		$aplf = new AbsencePolicyListFactory();

		if (	$id == NULL
				OR
				$this->Validator->isResultSetWithRows(	'absence_policy',
														$aplf->getByID($id),
														TTi18n::gettext('Invalid Absence Policy ID')
														) ) {
			$this->data['absence_policy_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getBranch() {
		if ( isset($this->data['branch_id']) ) {
			return $this->data['branch_id'];
		}

		return FALSE;
	}
	function setBranch($id) {
		$id = trim($id);

		$blf = new BranchListFactory();

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'branch',
														$blf->getByID($id),
														TTi18n::gettext('Branch does not exist')
														) ) {
			$this->data['branch_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getDepartment() {
		if ( isset($this->data['department_id']) ) {
			return $this->data['department_id'];
		}

		return FALSE;
	}
	function setDepartment($id) {
		$id = trim($id);

		$dlf = new DepartmentListFactory();

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'department',
														$dlf->getByID($id),
														TTi18n::gettext('Department does not exist')
														) ) {
			$this->data['department_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getJob() {
		if ( isset($this->data['job_id']) ) {
			return $this->data['job_id'];
		}

		return FALSE;
	}
	function setJob($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = 0;
		}

		if ( getTTProductEdition() == TT_PRODUCT_PROFESSIONAL ) {
			$jlf = new JobListFactory();
		}

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'job',
														$jlf->getByID($id),
														TTi18n::gettext('Job does not exist')
														) ) {
			$this->data['job_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getJobItem() {
		if ( isset($this->data['job_item_id']) ) {
			return $this->data['job_item_id'];
		}

		return FALSE;
	}
	function setJobItem($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = NULL;
		}

		if ( getTTProductEdition() == TT_PRODUCT_PROFESSIONAL ) {
			$jilf = new JobItemListFactory();
		}

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'job_item',
														$jilf->getByID($id),
														TTi18n::gettext('Job Item does not exist')
														) ) {
			$this->data['job_item_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function inSchedule( $epoch ) {
		if ( $epoch >= $this->getStartTime() AND $epoch <= $this->getEndTime() ) {
			Debug::text('aWithin Schedule: '. $epoch, __FILE__, __LINE__, __METHOD__,10);

			return TRUE;
		} elseif ( $this->inStartWindow( $epoch ) OR $this->inStopWindow( $epoch) )  {
			Debug::text('bWithin Schedule: '. $epoch, __FILE__, __LINE__, __METHOD__,10);

			return TRUE;
		}

		return FALSE;
	}

	function inStartWindow( $epoch ) {
		Debug::text(' Epoch: '. $epoch, __FILE__, __LINE__, __METHOD__,10);

		if ( $epoch == '' ) {
			return FALSE;
		}

		if (	$this->getSchedulePolicyObject() !== FALSE
				AND
				(
					$epoch >= ( $this->getStartTime() - $this->getSchedulePolicyObject()->getStartStopWindow() )
					AND
					$epoch <= ( $this->getStartTime() + $this->getSchedulePolicyObject()->getStartStopWindow() )
				)
			) {
			Debug::text(' Within Start/Stop window: '. $this->getSchedulePolicyObject()->getStartStopWindow() , __FILE__, __LINE__, __METHOD__,10);

			return TRUE;
		} else {
			Debug::text(' NOT Within Start/Stop window.', __FILE__, __LINE__, __METHOD__,10);
			return FALSE;
		}

		return FALSE;
	}

	function inStopWindow( $epoch ) {
		Debug::text(' Epoch: '. $epoch, __FILE__, __LINE__, __METHOD__,10);

		if ( $epoch == '' ) {
			return FALSE;
		}

		if (	$this->getSchedulePolicyObject() !== FALSE
				AND
				(
					$epoch >= ( $this->getEndTime() - $this->getSchedulePolicyObject()->getStartStopWindow() )
					AND
					$epoch <= ( $this->getEndTime() + $this->getSchedulePolicyObject()->getStartStopWindow() )
				)

			) {
			Debug::text(' Within Start/Stop window: '. $this->getSchedulePolicyObject()->getStartStopWindow() , __FILE__, __LINE__, __METHOD__,10);

			return TRUE;
		} else {
			Debug::text(' NOT Within Start/Stop window.', __FILE__, __LINE__, __METHOD__,10);
			return FALSE;
		}

		return FALSE;
	}

	function mergeScheduleArray($schedule_shifts, $recurring_schedule_shifts) {
		//Debug::text('Merging Schedule, and Recurring Schedule Shifts: ', __FILE__, __LINE__, __METHOD__, 10);

		$ret_arr = $schedule_shifts;

		//Debug::Arr($schedule_shifts, '(c) Schedule Shifts: ', __FILE__, __LINE__, __METHOD__, 10);

		if ( is_array($recurring_schedule_shifts) AND count($recurring_schedule_shifts) > 0 ) {
			foreach( $recurring_schedule_shifts as $date_stamp => $day_shifts_arr ) {
				//Debug::text('----------------------------------', __FILE__, __LINE__, __METHOD__, 10);
				//Debug::text('Date Stamp: '. TTDate::getDate('DATE+TIME', $date_stamp). ' Epoch: '. $date_stamp , __FILE__, __LINE__, __METHOD__, 10);
				//Debug::Arr($schedule_shifts[$date_stamp], 'Date Arr: ', __FILE__, __LINE__, __METHOD__, 10);
				foreach( $day_shifts_arr as $key => $shift_arr ) {

					if ( isset($ret_arr[$date_stamp]) ) {
						//Debug::text('Already Schedule Shift on this day: '. TTDate::getDate('DATE', $date_stamp) , __FILE__, __LINE__, __METHOD__, 10);

						//Loop through each shift on this day, and check for overlaps
						//Only include the recurring shift if ALL times DO NOT overlap
						$overlap = 0;
						foreach( $ret_arr[$date_stamp] as $tmp_shift_arr ) {
							if ( TTDate::isTimeOverLap( $shift_arr['start_time'], $shift_arr['end_time'], $tmp_shift_arr['start_time'], $tmp_shift_arr['end_time']) ) {
								//Debug::text('Times OverLap: '. TTDate::getDate('DATE+TIME', $shift_arr['start_time']) , __FILE__, __LINE__, __METHOD__, 10);
								$overlap++;
							} else {
								//Debug::text('Times DO NOT OverLap: '. TTDate::getDate('DATE+TIME', $shift_arr['start_time']) , __FILE__, __LINE__, __METHOD__, 10);
							}
						}

						if ( $overlap == 0 ) {
							//Debug::text('NO Times OverLap, using recurring schedule: '. TTDate::getDate('DATE+TIME', $shift_arr['start_time']) , __FILE__, __LINE__, __METHOD__, 10);
							$ret_arr[$date_stamp][] = $shift_arr;
						}
					} else {
						//Debug::text('No Schedule Shift on this day: '. TTDate::getDate('DATE', $date_stamp) , __FILE__, __LINE__, __METHOD__, 10);
						$ret_arr[$date_stamp][] = $shift_arr;
					}
				}
			}
		}

		return $ret_arr;
	}

	function getScheduleArray( $user_ids, $start_date, $end_date ) {
		global $current_user, $current_user_prefs;

		if ( $user_ids == '' OR ( is_array($user_ids) AND count($user_ids) == 0 ) ) {
			return FALSE;
		}

		if ( $start_date == '' ) {
			return FALSE;
		}

		if ( $end_date == '' ) {
			return FALSE;
		}

		//Convert to array for one user_id
		if ( !is_array($user_ids) ) {
			$user_ids = array($user_ids);
		}

		$slf = new ScheduleListFactory();
		$rsclf = new RecurringScheduleControlListFactory();
		$ulf = new UserListFactory();
		$hlf = new HolidayListFactory();

		$blf = new BranchListFactory();
		$branch_options = $blf->getByCompanyIdArray( $current_user->getCompany(), FALSE );

		$dlf = new DepartmentListFactory();
		$department_options = $dlf->getByCompanyIdArray( $current_user->getCompany(), FALSE );

		foreach( $user_ids as $user_id ) {
			Debug::text('User ID: '. $user_id , __FILE__, __LINE__, __METHOD__, 10);
			$ulf->getByIdAndCompanyId( $user_id, $current_user->getCompany() );
			if ( $ulf->getRecordCount() > 0 ) {
				$user_obj = $ulf->getCurrent();

				$user_obj_prefs = $user_obj->getUserPreferenceObject();
				if ( is_object( $user_obj_prefs ) ) {
					$user_obj_prefs->setTimeZonePreferences();
				} else {
					//Use system timezone.
					TTDate::setTimeZone();
				}
			} else {
				Debug::text('No User record, skipping...', __FILE__, __LINE__, __METHOD__, 10);
				continue;
			}

			//Grab all schedule rows in this time period.
			$slf->getByUserIdAndStartDateAndEndDate( $user_id, $start_date, $end_date );
			if ( $slf->getRecordCount() > 0 ) {
				foreach( $slf as $s_obj ) {
					//Debug::text('Schedule ID: '. $s_obj->getId() .' Row: '. $s_obj->getStartTime(), __FILE__, __LINE__, __METHOD__, 10);
					//$schedule_shifts[TTDate::getBeginDayEpoch($s_obj->getStartTime())][] = array(

					if ( is_object($s_obj->getAbsencePolicyObject()) ) {
						$absence_policy_name = (string)$s_obj->getAbsencePolicyObject()->getName();
					} else {
						$absence_policy_name = 'N/A';
					}

					$schedule_shifts[TTDate::getISODateStamp($s_obj->getStartTime())][] = array(
														'id' => $s_obj->getID(),
														'user_id' => $user_id,
														'user_full_name' => $user_obj->getFullName(),
														'first_name' => $user_obj->getFirstName(),
														'last_name' => $user_obj->getLastName(),
														'status_id' => $s_obj->getStatus(),
														'start_time' => $s_obj->getStartTime(),
														'end_time' => $s_obj->getEndTime(),
														'total_time' => $s_obj->getTotalTime(),
														'schedule_policy_id' => $s_obj->getSchedulePolicyID(),
														'absence_policy_id' => $s_obj->getAbsencePolicyID(),
														'absence_policy' => $absence_policy_name,
														'branch_id' => $s_obj->getBranch(),
														'branch' => Option::getByKey($s_obj->getBranch(), $branch_options, NULL ),
														'department_id' => $s_obj->getDepartment(),
														'department' =>  Option::getByKey($s_obj->getDepartment(), $department_options, NULL ),
													);
					unset($absence_policy_name);
				}

				//Debug::Arr($schedule_shifts, 'Schedule Shifts: ', __FILE__, __LINE__, __METHOD__, 10);
			} else {
				$schedule_shifts = array();
			}

			//Recurring schedules should honor hire/termination dates automatically.
			if ( $user_obj->getHireDate() != '' AND $user_obj->getHireDate() > $start_date ) {
				Debug::text('Hire date is after schedule start date! Hire Date: '. $user_obj->getHireDate(), __FILE__, __LINE__, __METHOD__, 10);
				$rs_start_date = $user_obj->getHireDate();
			} else {
				$rs_start_date = $start_date;
			}

			if ( $user_obj->getTerminationDate() != '' AND $user_obj->getTerminationDate() < $end_date ) {
				Debug::text('Termination date is before schedule end date! Termination Date: '. $user_obj->getTerminationDate(), __FILE__, __LINE__, __METHOD__, 10);
				$rs_end_date = $user_obj->getTerminationDate();
			} else {
				$rs_end_date = $end_date;
			}
			//Debug::text('Recurring Schedule Start Date: '. TTDate::getDate('DATE+TIME', $rs_start_date) .' End Date: '. TTDate::getDate('DATE+TIME', $rs_end_date), __FILE__, __LINE__, __METHOD__, 10);

			//Grab Recurring Schedule data for this time period.
			$rsclf->getByUserIDAndStartDateAndEndDate( $user_id, $rs_start_date, $rs_end_date );
			if ( $rsclf->getRecordCount() > 0 ) {
				Debug::text('Found Recurring Schedule for this time period: ', __FILE__, __LINE__, __METHOD__, 10);

				//Get all holidays in this schedule date range.
				$hlf->getByPolicyGroupUserIdAndStartDateAndEndDate( $user_id, $rs_start_date, $rs_end_date );
				if ( $hlf->getRecordCount() > 0 ) {
					foreach( $hlf as $h_obj ) {
						if ( is_object( $h_obj->getHolidayPolicyObject() ) AND is_object( $h_obj->getHolidayPolicyObject()->getAbsencePolicyObject() ) ) {
							$holiday_data[TTDate::getISODateStamp($h_obj->getDateStamp())] = array('status_id' => (int)$h_obj->getHolidayPolicyObject()->getDefaultScheduleStatus(), 'absence_policy_id' => $h_obj->getHolidayPolicyObject()->getAbsencePolicyID(), 'absence_policy' => $h_obj->getHolidayPolicyObject()->getAbsencePolicyObject()->getName() );
						} else {
							$holiday_data[TTDate::getISODateStamp($h_obj->getDateStamp())] = array('status_id' => 10 ); //Working
						}
					}
				}

				$recurring_schedule_shifts = array();
				foreach( $rsclf as $rsc_obj ) {
					Debug::text('Recurring Schedule ID: '. $rsc_obj->getID() , __FILE__, __LINE__, __METHOD__, 10);
					$tmp_recurring_schedule_shifts = $rsc_obj->getShiftsByStartDateAndEndDate( $rs_start_date, $rs_end_date );

					if ( is_array($tmp_recurring_schedule_shifts) ) {
						foreach( $tmp_recurring_schedule_shifts as $day_epoch => $day_tmp_recurring_schedule_shift ) {
							if ( is_array($day_tmp_recurring_schedule_shift) ) {
								foreach( $day_tmp_recurring_schedule_shift as $key => $dummy ) {
									$tmp_recurring_schedule_shifts[$day_epoch][$key]['user_id'] = $user_id;
									$tmp_recurring_schedule_shifts[$day_epoch][$key]['user_full_name'] = $user_obj->getFullName();

									if ( isset($holiday_data[$day_epoch]) ) {
										//We have to assume they are eligible, because we really won't know
										//if they will have worked enough days or not. We could assume they
										//work whatever their schedule is, but chances are they will be eligible then anyways.
										$tmp_recurring_schedule_shifts[$day_epoch][$key]['status_id'] = $holiday_data[$day_epoch]['status_id'];
										if ( isset($holiday_data[$day_epoch]['absence_policy_id']) ) {
											$tmp_recurring_schedule_shifts[$day_epoch][$key]['absence_policy_id'] = $holiday_data[$day_epoch]['absence_policy_id'];
											$tmp_recurring_schedule_shifts[$day_epoch][$key]['absence_policy'] = $holiday_data[$day_epoch]['absence_policy'];
										}
									}

									//Default branch
									if ( $tmp_recurring_schedule_shifts[$day_epoch][$key]['branch_id'] == -1 ) {
										$tmp_recurring_schedule_shifts[$day_epoch][$key]['branch'] = Option::getByKey($user_obj->getDefaultBranch(), $branch_options, NULL );
									} else {
										$tmp_recurring_schedule_shifts[$day_epoch][$key]['branch'] = Option::getByKey($tmp_recurring_schedule_shifts[$day_epoch][$key]['branch_id'], $branch_options, NULL );
									}

									//Default department
									if ( $tmp_recurring_schedule_shifts[$day_epoch][$key]['department_id'] == -1 ) {
										$tmp_recurring_schedule_shifts[$day_epoch][$key]['department'] = Option::getByKey($user_obj->getDefaultDepartment(), $department_options, NULL );
									} else {
										//$tmp_recurring_schedule_shifts[$day_epoch][$key]['department'] = $department_options[$tmp_recurring_schedule_shifts[$day_epoch][$key]['department_id']];
										$tmp_recurring_schedule_shifts[$day_epoch][$key]['department'] = Option::getByKey($tmp_recurring_schedule_shifts[$day_epoch][$key]['department_id'], $department_options, NULL );
									}
								}
							}

							unset($holiday_obj);
						}
					}
					//print_r($tmp_recurring_schedule_shifts);
					//Merge recurring schedules.
					$recurring_schedule_shifts = $this->mergeScheduleArray( $recurring_schedule_shifts, $tmp_recurring_schedule_shifts);
				}
			} else {
				Debug::text('DID NOT find Recurring Schedule for this time period: ', __FILE__, __LINE__, __METHOD__, 10);
				$recurring_schedule_shifts = array();
			}

			//Debug::Arr($recurring_schedule_shifts, 'Recurring Schedule Shifts: ', __FILE__, __LINE__, __METHOD__, 10);
		}

		//Debug::Arr($schedule_shifts, '(b) Schedule Shifts: ', __FILE__, __LINE__, __METHOD__, 10);

		if ( isset($schedule_shifts) AND isset($recurring_schedule_shifts) ) {
			//Merge Schedule Shifts and Recurring Schedule Shifts.
			$shifts = $this->mergeScheduleArray( $schedule_shifts, $recurring_schedule_shifts);
		}
		//Debug::Arr($shifts, 'All Shifts: ', __FILE__, __LINE__, __METHOD__, 10);

		//Return timezone back to currently logged in users timezone.
		$current_user_prefs->setTimeZonePreferences();

		if ( isset($shifts) ) {
			return $shifts;
		}

		return FALSE;
	}

	function getEnableReCalculateDay() {
		if ( isset($this->recalc_day) ) {
			return $this->recalc_day;
		}

		return FALSE;
	}
	function setEnableReCalculateDay($bool) {
		$this->recalc_day = $bool;

		return TRUE;
	}

	function handleDayBoundary() {
		Debug::Arr($this->getStartTime(), 'Start Time: '. TTDate::getDate('DATE+TIME', $this->getStartTime()), __FILE__, __LINE__, __METHOD__, 10);
		Debug::Arr($this->getEndTime(), 'End Time: '. TTDate::getDate('DATE+TIME', $this->getEndTime()), __FILE__, __LINE__, __METHOD__, 10);

		//This used to be done in Validate, but needs to be done in preSave too.
		if ( $this->getEndTime() < $this->getStartTime() ) {
			Debug::Text('EndTime spans midnight boundary! Increase by 24hrs ', __FILE__, __LINE__, __METHOD__,10);
			$this->setEndTime( $this->getEndTime() + 86400 ); //End time spans midnight, add 24hrs.
		}

		return TRUE;
	}

	//Write all the schedules shifts for a given week.
	function writeWeekSchedule( $pdf, $cell_width, $week_date_stamps, $max_week_data, $left_margin, $group_schedule, $start_week_day = 0, $bottom_border = FALSE) {
		$week_of_year = TTDate::getWeek( strtotime($week_date_stamps[0]), $start_week_day);
		//Debug::Text('Max Week Shifts: '. (int)$max_week_data[$week_of_year]['shift'], __FILE__, __LINE__, __METHOD__,10);
		//Debug::Text('Max Week Branches: '. count($max_week_data[$week_of_year]['branch']), __FILE__, __LINE__, __METHOD__,10);
		//Debug::Text('Max Week Departments: '. count($max_week_data[$week_of_year]['department']), __FILE__, __LINE__, __METHOD__,10);
		Debug::Text('Week Of Year: '. $week_of_year, __FILE__, __LINE__, __METHOD__,10);
		Debug::Arr($max_week_data, 'max_week_data: ', __FILE__, __LINE__, __METHOD__,10);

		$week_data_array = NULL;

		if ( !isset($max_week_data[$week_of_year]['labels']) ) {
			$max_week_data[$week_of_year]['labels'] = 0;
		}

		if ( $group_schedule == TRUE ) {
			$min_rows_multiplier = 2;
		} else {
			$min_rows_multiplier = 1;
		}

		if ( isset($max_week_data[$week_of_year]['shift']) ) {
			$min_rows_per_day = ($max_week_data[$week_of_year]['shift']*$min_rows_multiplier) + $max_week_data[$week_of_year]['labels'];
			Debug::Text('Shift Total: '. $max_week_data[$week_of_year]['shift'], __FILE__, __LINE__, __METHOD__,10);
		} else {
			$min_rows_per_day = $min_rows_multiplier + $max_week_data[$week_of_year]['labels'];
		}
		Debug::Text('aMin Rows Per Day: '. $min_rows_per_day .' Labels: '. $max_week_data[$week_of_year]['labels'], __FILE__, __LINE__, __METHOD__,10);
		//print_r($this->schedule_shifts);

		//Prepare data so we can write it out line by line, left to right.
		$shift_counter = 0;
		foreach( $week_date_stamps as $week_date_stamp ) {
			Debug::Text('Week Date Stamp: ('.$week_date_stamp.')'. TTDate::getDate('DATE+TIME', strtotime($week_date_stamp)), __FILE__, __LINE__, __METHOD__,10);

			$rows_per_day = 0;
			if ( isset($this->schedule_shifts[$week_date_stamp]) ) {
				foreach( $this->schedule_shifts[$week_date_stamp] as $branch => $department_schedule_shifts ) {
					if ( $branch != '--' ) {
						$tmp_week_data_array[$week_date_stamp][] = array('type' => 'branch', 'date_stamp' => $week_date_stamp, 'label' => $branch );
						$rows_per_day++;
					}

					foreach( $department_schedule_shifts as $department => $tmp_schedule_shifts ) {
						if ( $department != '--' ) {
							$tmp_week_data_array[$week_date_stamp][] = array('type' => 'department', 'label' => $department );
							$rows_per_day++;
						}

						foreach( $tmp_schedule_shifts as $schedule_shift ) {
							if ( $group_schedule == TRUE ) {
								$tmp_week_data_array[$week_date_stamp][] = array('type' => 'user_name', 'label' => $schedule_shift['user_full_name'], 'shift' => $shift_counter );
								if ( $schedule_shift['status_id'] == 10 ) {
									$tmp_week_data_array[$week_date_stamp][] = array('type' => 'shift', 'label' => TTDate::getDate('TIME', $schedule_shift['start_time'] ) .' - '. TTDate::getDate('TIME', $schedule_shift['end_time'] ), 'shift' => $shift_counter );
								} else {
									$tmp_week_data_array[$week_date_stamp][] = array('type' => 'absence', 'label' => $schedule_shift['absence_policy'], 'shift' => $shift_counter );
								}
								$rows_per_day += 2;
							} else {
								if ( $schedule_shift['status_id'] == 10 ) {
									$tmp_week_data_array[$week_date_stamp][] = array('type' => 'shift', 'label' => TTDate::getDate('TIME', $schedule_shift['start_time'] ) .' - '. TTDate::getDate('TIME', $schedule_shift['end_time'] ), 'shift' => $shift_counter );
								} else {
									$tmp_week_data_array[$week_date_stamp][] = array('type' => 'absence', 'label' => $schedule_shift['absence_policy'], 'shift' => $shift_counter );
								}
								$rows_per_day++;
							}
							$shift_counter++;
						}
					}
				}
			}

			if ( $rows_per_day < $min_rows_per_day ) {
				for($z=$rows_per_day; $z < $min_rows_per_day; $z++) {
					$tmp_week_data_array[$week_date_stamp][] = array('type' => 'blank', 'label' => NULL );
				}
			}
		}
		//print_r($tmp_week_data_array);

		for($x=0; $x < $min_rows_per_day; $x++ ) {
			foreach( $week_date_stamps as $week_date_stamp ) {
				if ( isset($tmp_week_data_array[$week_date_stamp][0]) ) {
					$week_data_array[] = $tmp_week_data_array[$week_date_stamp][0];
					array_shift($tmp_week_data_array[$week_date_stamp]);
				}
			}
		}
		unset($tmp_week_data_array);
		//print_r($week_data_array);

		//Render PDF here
		$border = 'LR';
		$i=0;
		$total_cells = count($week_data_array);

		foreach( $week_data_array as $key => $data ) {
			if ( $i % 7 == 0 ) {
				$pdf->Ln();
			}

			$pdf->setTextColor(0,0,0); //Black
			switch( $data['type'] ) {
				case 'branch':
					$pdf->setFillColor(200,200,200);
					$pdf->SetFont('freesans','B',8);
					break;
				case 'department':
					$pdf->setFillColor(220,220,220);
					$pdf->SetFont('freesans','B',8);
					break;
				case 'user_name':
					if ( $data['shift'] % 2 == 0 ) {
						$pdf->setFillColor(240,240,240);
					} else {
						$pdf->setFillColor(255,255,255);
					}
					$pdf->SetFont('freesans','B',8);
					break;
				case 'shift':
					if ( $data['shift'] % 2 == 0 ) {
						$pdf->setFillColor(240,240,240);
					} else {
						$pdf->setFillColor(255,255,255);
					}
					$pdf->SetFont('freesans','',8);
					break;
				case 'absence':
					$pdf->setTextColor(255,0,0);
					if ( $data['shift'] % 2 == 0 ) {
						$pdf->setFillColor(240,240,240);
					} else {
						$pdf->setFillColor(255,255,255);
					}
					$pdf->SetFont('freesans','I',8);
					break;
				case 'blank':
					$pdf->setFillColor(255,255,255);
					$pdf->SetFont('freesans','',8);
					break;
			}

			if ( $bottom_border == TRUE AND $i >= ($total_cells-7) ) {
				$border = 'LRB';
			}

			$pdf->Cell($cell_width, 15, $data['label'], $border, 0, 'C', 1);

			$i++;
		}

		$pdf->Ln();

		return TRUE;
	}

	function getSchedule( $company_id, $user_ids, $start_date, $end_date, $start_week_day = 0, $group_schedule = FALSE ) {
		//Individual is one schedule per employee, or all on one schedule.
		if (!is_array($user_ids) ) {
			return FALSE;
		}

		$current_epoch = time();

		//Debug::Text('Start Date: '. TTDate::getDate('DATE', $start_date) .' End Date: '. TTDate::getDate('DATE', $end_date) , __FILE__, __LINE__, __METHOD__,10);
		Debug::text(' Start Date: '. TTDate::getDate('DATE+TIME', $start_date) .' End Date: '. TTDate::getDate('DATE+TIME', $end_date) .' Start Week Day: '. $start_week_day, __FILE__, __LINE__, __METHOD__,10);

		$pdf = new TTPDF('L', 'pt', 'Letter');

		$left_margin = 20;
		$top_margin = 20;
		$pdf->setMargins($left_margin,$top_margin);
		$pdf->SetAutoPageBreak(TRUE, 30);
		//$pdf->SetAutoPageBreak(FALSE);
		$pdf->SetFont('freesans','',10);

		$border = 0;
		$adjust_x = 0;
		$adjust_y = 0;

		if ( $group_schedule == FALSE ) {
			$valid_schedules = 0;
			foreach( $user_ids as $user_id ) {
				Debug::text(' User Id: '. $user_id, __FILE__, __LINE__, __METHOD__,10);

				$sf = new ScheduleFactory();
				$raw_schedule_shifts = $sf->getScheduleArray( $user_id, $start_date, $end_date);
				if ( is_array($raw_schedule_shifts) ) {
					foreach( $raw_schedule_shifts as $day_epoch => $day_schedule_shifts ) {
						foreach ( $day_schedule_shifts as $day_schedule_shift ) {
							//Debug::Arr($day_schedule_shift, 'aDay Schedule Shift: ', __FILE__, __LINE__, __METHOD__,10);
							$tmp_schedule_shifts[$day_epoch][$day_schedule_shift['branch']][$day_schedule_shift['department']][] = $day_schedule_shift;

							if ( isset($schedule_shift_totals[$day_epoch]['total_shifts']) ) {
								$schedule_shift_totals[$day_epoch]['total_shifts']++;
							} else {
								$schedule_shift_totals[$day_epoch]['total_shifts'] = 1;
							}

							//$week_of_year = TTDate::getWeek( strtotime($day_epoch) );
							$week_of_year = TTDate::getWeek( strtotime($day_epoch), $start_week_day );
							if ( !isset($schedule_shift_totals[$day_epoch]['labels']) ) {
								$schedule_shift_totals[$day_epoch]['labels'] = 0;
							}
							if ( $day_schedule_shift['branch'] != '--'
									AND !isset($schedule_shift_totals[$day_epoch]['branch'][$day_schedule_shift['branch']]) ) {
								$schedule_shift_totals[$day_epoch]['branch'][$day_schedule_shift['branch']] = TRUE;
								$schedule_shift_totals[$day_epoch]['labels']++;
							}
							if ( $day_schedule_shift['department'] != '--'
									AND !isset($schedule_shift_totals[$day_epoch]['department'][$day_schedule_shift['branch']][$day_schedule_shift['department']]) ) {
								$schedule_shift_totals[$day_epoch]['department'][$day_schedule_shift['branch']][$day_schedule_shift['department']] = TRUE;
								$schedule_shift_totals[$day_epoch]['labels']++;
							}

							if ( !isset($max_week_data[$week_of_year]['shift']) ) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to 0', __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = 1;
								$max_week_data[$week_of_year]['labels'] = 0;
							}

							if ( isset($max_week_data[$week_of_year]['shift'])
									AND ($schedule_shift_totals[$day_epoch]['total_shifts']+$schedule_shift_totals[$day_epoch]['labels']) > ($max_week_data[$week_of_year]['shift']+$max_week_data[$week_of_year]['labels']) ) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to: '.  $schedule_shift_totals[$day_epoch]['total_shifts'] .' Labels: '. $schedule_shift_totals[$day_epoch]['labels'], __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = $schedule_shift_totals[$day_epoch]['total_shifts'];
								$max_week_data[$week_of_year]['labels'] = $schedule_shift_totals[$day_epoch]['labels'];
							}

							//Debug::Arr($schedule_shift_totals, ' Schedule Shift Totals: ', __FILE__, __LINE__, __METHOD__,10);
							//Debug::Arr($max_week_data, ' zMaxWeekData: ', __FILE__, __LINE__, __METHOD__,10);
						}
					}
				}
				unset($raw_schedule_shifts, $day_epoch, $day_schedule_shifts, $day_schedule_shift, $schedule_shift_totals);

				if ( isset($tmp_schedule_shifts) ) {
					//Sort Branches/Departments first
					foreach ( $tmp_schedule_shifts as $day_epoch => $day_tmp_schedule_shift ) {
						ksort($day_tmp_schedule_shift);
						$tmp_schedule_shifts[$day_epoch] = $day_tmp_schedule_shift;

						foreach ( $day_tmp_schedule_shift as $branch => $department_schedule_shifts ) {
							ksort($tmp_schedule_shifts[$day_epoch][$branch]);
						}
					}

					//Sort each department by start time.
					foreach ( $tmp_schedule_shifts as $day_epoch => $day_tmp_schedule_shift ) {
						foreach ( $day_tmp_schedule_shift as $branch => $department_schedule_shifts ) {
							foreach ( $department_schedule_shifts as $department => $department_schedule_shift ) {
								$department_schedule_shift = Sort::multiSort( $department_schedule_shift, 'start_time' );

								$this->schedule_shifts[$day_epoch][$branch][$department] = $department_schedule_shift;
							}
						}
					}
				}
				unset($day_tmp_schedule_shift, $department_schedule_shifts, $department_schedule_shift, $tmp_schedule_shifts, $branch, $department);

				$calendar_array = TTDate::getCalendarArray($start_date, $end_date, $start_week_day );
				//var_dump($calendar_array);

				if ( !is_array($calendar_array) OR !isset($this->schedule_shifts) OR !is_array($this->schedule_shifts) ) {
					continue; //Skip to next user.
				}

				$ulf = new UserListFactory();
				$ulf->getByIdAndCompanyId( $user_id, $company_id );
				if ( $ulf->getRecordCount() != 1 ) {
					continue;
				} else {
					$user_obj = $ulf->getCurrent();

					$pdf->AddPage();

					$pdf->setXY( 670, $top_margin);
					$pdf->SetFont('freesans','',10);
					$pdf->Cell(100,15, TTDate::getDate('DATE+TIME', $current_epoch ), $border, 0, 'R');

					$pdf->setXY( $left_margin, $top_margin);
					$pdf->SetFont('freesans','B',25);
					$pdf->Cell(0,25, $user_obj->getFullName(). ' - '. TTi18n::getText('Schedule'), $border, 0, 'C');
					$pdf->Ln();
				}

				$pdf->SetFont('freesans','B',16);
				$pdf->Cell(0,15, TTDate::getDate('DATE', $start_date) .' - '. TTDate::getDate('DATE', $end_date), $border, 0, 'C');
				//$pdf->Ln();
				$pdf->Ln();
				$pdf->Ln();

				$pdf->SetFont('freesans','',8);

				$cell_width = floor(($pdf->GetPageWidth()-($left_margin*2))/7);
				$cell_height = 100;

				$i=0;
				$total_days = count($calendar_array)-1;
				$boader = 1;
				foreach( $calendar_array as $calendar ) {
					if ( $i == 0 ) {
						//Calendar Header
						$pdf->SetFont('freesans','B',8);
						$calendar_header = TTDate::getDayOfWeekArrayByStartWeekDay( $start_week_day );

						foreach( $calendar_header as $header_name ) {
							$pdf->Cell($cell_width,15,$header_name, 1, 0, 'C');
						}

						$pdf->Ln();
						unset($calendar_header, $header_name);
					}

					$month_name = NULL;
					if ( $i == 0 OR $calendar['isNewMonth'] == TRUE ) {
						$month_name = $calendar['month_name'];
					}

					if ( ($i > 0 AND $i % 7 == 0) ) {
						$this->writeWeekSchedule( $pdf, $cell_width, $week_date_stamps, $max_week_data, $left_margin, $group_schedule, $start_week_day);
						unset($week_date_stamps);
					}

					$pdf->SetFont('freesans','B',8);
					$pdf->Cell($cell_width/2, 15, $month_name, 'LT', 0, 'L');
					$pdf->Cell($cell_width/2, 15, $calendar['day_of_month'], 'RT', 0, 'R');

					$week_date_stamps[] = $calendar['date_stamp'];

					$i++;
				}

				$this->writeWeekSchedule( $pdf, $cell_width, $week_date_stamps, $max_week_data, $left_margin, $group_schedule, $start_week_day, TRUE);

				unset($this->schedule_shifts, $calendar_array, $week_date_stamps, $max_week_data);

				$valid_schedules++;
			}
		} else {
			$valid_schedules = 1;
			foreach( $user_ids as $user_id ) {
				Debug::text(' User Id: '. $user_id, __FILE__, __LINE__, __METHOD__,10);
				$sf = new ScheduleFactory();
				$raw_schedule_shifts = $sf->getScheduleArray( $user_id, $start_date, $end_date);
				if ( is_array($raw_schedule_shifts) ) {
					foreach( $raw_schedule_shifts as $day_epoch => $day_schedule_shifts ) {
						foreach ( $day_schedule_shifts as $day_schedule_shift ) {
							//Debug::Arr($day_schedule_shift, 'bDay Schedule Shift: ', __FILE__, __LINE__, __METHOD__,10);
							$tmp_schedule_shifts[$day_epoch][$day_schedule_shift['branch']][$day_schedule_shift['department']][] = $day_schedule_shift;

							if ( isset($schedule_shift_totals[$day_epoch]['total_shifts']) ) {
								$schedule_shift_totals[$day_epoch]['total_shifts']++;
							} else {
								$schedule_shift_totals[$day_epoch]['total_shifts'] = 1;
							}

							//$week_of_year = TTDate::getWeek( strtotime($day_epoch) );
							$week_of_year = TTDate::getWeek( strtotime($day_epoch), $start_week_day );
							Debug::text(' Date: '. TTDate::getDate('DATE', strtotime($day_epoch)) .' Week: '. $week_of_year .' TMP: '. TTDate::getWeek( strtotime('20070721'), $start_week_day ), __FILE__, __LINE__, __METHOD__,10);
							if ( !isset($schedule_shift_totals[$day_epoch]['labels']) ) {
								$schedule_shift_totals[$day_epoch]['labels'] = 0;
							}
							if ( $day_schedule_shift['branch'] != '--'
									AND !isset($schedule_shift_totals[$day_epoch]['branch'][$day_schedule_shift['branch']]) ) {
								$schedule_shift_totals[$day_epoch]['branch'][$day_schedule_shift['branch']] = TRUE;
								$schedule_shift_totals[$day_epoch]['labels']++;
							}
							if ( $day_schedule_shift['department'] != '--'
									AND !isset($schedule_shift_totals[$day_epoch]['department'][$day_schedule_shift['branch']][$day_schedule_shift['department']]) ) {
								$schedule_shift_totals[$day_epoch]['department'][$day_schedule_shift['branch']][$day_schedule_shift['department']] = TRUE;
								$schedule_shift_totals[$day_epoch]['labels']++;
							}

							if ( !isset($max_week_data[$week_of_year]['shift']) ) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to 0', __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = 1;
								$max_week_data[$week_of_year]['labels'] = 0;
							}

							if ( isset($max_week_data[$week_of_year]['shift'])
									AND ($schedule_shift_totals[$day_epoch]['total_shifts']+$schedule_shift_totals[$day_epoch]['labels']) > ($max_week_data[$week_of_year]['shift']+$max_week_data[$week_of_year]['labels']) ) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to: '.  $schedule_shift_totals[$day_epoch]['total_shifts'] .' Labels: '. $schedule_shift_totals[$day_epoch]['labels'], __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = $schedule_shift_totals[$day_epoch]['total_shifts'];
								$max_week_data[$week_of_year]['labels'] = $schedule_shift_totals[$day_epoch]['labels'];
							}
/*
							if ( isset($max_week_data[$week_of_year]['shift'])
									AND ($schedule_shift_totals[$day_epoch]['total_shifts']+$schedule_shift_totals[$day_epoch]['labels']) > ($max_week_data[$week_of_year]['shift']+$max_week_data[$week_of_year]['labels']) ) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to: '.  $schedule_shift_totals[$day_epoch]['total_shifts'] .' Labels: '. $schedule_shift_totals[$day_epoch]['labels'], __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = $schedule_shift_totals[$day_epoch]['total_shifts'];
								$max_week_data[$week_of_year]['labels'] = $schedule_shift_totals[$day_epoch]['labels'];
							} elseif (!isset($max_week_data[$week_of_year]['shift'])) {
								Debug::text('Date: '. $day_epoch .' Week: '. $week_of_year .' Setting Max Week shift to 0', __FILE__, __LINE__, __METHOD__,10);
								$max_week_data[$week_of_year]['shift'] = 1;
								$max_week_data[$week_of_year]['labels'] = 0;
							}
*/
						}
					}
				}
			}
			//print_r($tmp_schedule_shifts);
			//print_r($max_week_data);

			if ( isset($tmp_schedule_shifts) ) {
				//Sort Branches/Departments first
				foreach ( $tmp_schedule_shifts as $day_epoch => $day_tmp_schedule_shift ) {
					ksort($day_tmp_schedule_shift);
					$tmp_schedule_shifts[$day_epoch] = $day_tmp_schedule_shift;

					foreach ( $day_tmp_schedule_shift as $branch => $department_schedule_shifts ) {
						ksort($tmp_schedule_shifts[$day_epoch][$branch]);
					}
				}

				//Sort each department by start time.
				foreach ( $tmp_schedule_shifts as $day_epoch => $day_tmp_schedule_shift ) {
					foreach ( $day_tmp_schedule_shift as $branch => $department_schedule_shifts ) {
						foreach ( $department_schedule_shifts as $department => $department_schedule_shift ) {
							$sort = new arr_multisort();
							$sort->setArray($department_schedule_shift);
							$sort->addColumn('start_time', 1);
							$department_schedule_shift = $sort->Sort();
							unset($sort);

							$this->schedule_shifts[$day_epoch][$branch][$department] = $department_schedule_shift;
						}
					}
				}
			}
			//print_r($this->schedule_shifts);

			$calendar_array = TTDate::getCalendarArray($start_date, $end_date, $start_week_day );
			//var_dump($calendar_array);

			if ( !is_array($calendar_array) OR !isset($this->schedule_shifts) OR !is_array($this->schedule_shifts) ) {
				return FALSE;
			}

			$pdf->AddPage();

			$pdf->setXY( 670, $top_margin);
			$pdf->SetFont('freesans','',10);
			$pdf->Cell(100,15, TTDate::getDate('DATE+TIME', $current_epoch ), $border, 0, 'R');

			$pdf->setXY( $left_margin, $top_margin);

			$pdf->SetFont('freesans','B',25);
			$pdf->Cell(0,25,'Employee Schedule', $border, 0, 'C');
			$pdf->Ln();

			$pdf->SetFont('freesans','B',10);
			$pdf->Cell(0,15, TTDate::getDate('DATE', $start_date) .' - '. TTDate::getDate('DATE', $end_date), $border, 0, 'C');
			$pdf->Ln();
			$pdf->Ln();

			$pdf->SetFont('freesans','',8);

			$cell_width = floor(($pdf->GetPageWidth()-($left_margin*2))/7);
			$cell_height = 100;

			$i=0;
			$total_days = count($calendar_array)-1;
			$boader = 1;
			foreach( $calendar_array as $calendar ) {
				if ( $i == 0 ) {
					//Calendar Header
					$pdf->SetFont('freesans','B',8);
					$calendar_header = TTDate::getDayOfWeekArrayByStartWeekDay( $start_week_day );

					foreach( $calendar_header as $header_name ) {
						$pdf->Cell($cell_width,15,$header_name, 1, 0, 'C');
					}

					$pdf->Ln();
					unset($calendar_header, $header_name);
				}

				$month_name = NULL;
				if ( $i == 0 OR $calendar['isNewMonth'] == TRUE ) {
					$month_name = $calendar['month_name'];
				}

				if ( ($i > 0 AND $i % 7 == 0) ) {
					$this->writeWeekSchedule( $pdf, $cell_width, $week_date_stamps, $max_week_data, $left_margin, $group_schedule, $start_week_day);
					unset($week_date_stamps);
				}

				$pdf->SetFont('freesans','B',8);
				$pdf->Cell($cell_width/2, 15, $month_name, 'LT', 0, 'L');
				$pdf->Cell($cell_width/2, 15, $calendar['day_of_month'], 'RT', 0, 'R');

				$week_date_stamps[] = $calendar['date_stamp'];

				$i++;
			}

			$this->writeWeekSchedule( $pdf, $cell_width, $week_date_stamps, $max_week_data, $left_margin, $group_schedule, $start_week_day, TRUE);
		}

		if ( $valid_schedules > 0 ) {
			$output = $pdf->Output('','S');
			return $output;
		}

		return FALSE;
	}

	function Validate() {
		Debug::Text('User Date ID: '. $this->getUserDateID(), __FILE__, __LINE__, __METHOD__,10);

		$this->handleDayBoundary();

		if ( $this->getUserDateID() == FALSE OR !is_object( $this->getUserDateObject() ) ) {
			Debug::Text('UserDateID is INVALID! ', __FILE__, __LINE__, __METHOD__,10);
			$this->Validator->isTrue(		'user_date',
											FALSE,
											TTi18n::gettext('Invalid User/Date. Pay Period may be locked'));
		}

		if ( is_object( $this->getUserDateObject() ) ) {
			//Make sure we're not conflicting with any other schedule shifts.
			$slf = new ScheduleListFactory();
			$conflicting_schedule_shift_obj = $slf->getConflictingByUserIdAndStartDateAndEndDate( $this->getUserDateObject()->getUser(), $this->getStartTime(), $this->getEndTime() );

			if ( is_object($conflicting_schedule_shift_obj) ) {
				$conflicting_schedule_shift_obj = $conflicting_schedule_shift_obj->getCurrent();

				if ( $conflicting_schedule_shift_obj->isNew() === FALSE
						AND $conflicting_schedule_shift_obj->getId() != $this->getId() ) {
					Debug::text('Conflicting Schedule Shift ID:'. $conflicting_schedule_shift_obj->getId() .' Schedule Shift ID: '. $this->getId() , __FILE__, __LINE__, __METHOD__, 10);
					$this->Validator->isTrue(		'start_time',
													FALSE,
													TTi18n::gettext('Conflicting start time'));
				}
			}
		}

		return TRUE;
	}

	function preSave() {
		$this->handleDayBoundary();

		if ( $this->getTotalTime() == FALSE ) {
			$this->setTotalTime( $this->calcTotalTime() );
		}

		if ( $this->getStatus() == 10 ) {
			$this->setAbsencePolicyID( NULL );
		}
		return TRUE;
	}

	function postSave() {
		Debug::text(' postSave()', __FILE__, __LINE__, __METHOD__,10);

		if ( $this->getEnableReCalculateDay() == TRUE ) {
			//Calculate total time. Mainly for docked.
			UserDateTotalFactory::reCalculateDay( $this->getUserDateID(), TRUE, FALSE );
			//ExceptionPolicyFactory::calcExceptions( $this->getUserDateID() );
		}

		return TRUE;
	}

	function addLog( $log_action ) {
		return TTLog::addEntry( $this->getId(), $log_action, TTi18n::getText('Schedule'), NULL, $this->getTable() );
	}
}
?>