<?php
/**
 *		sabreScheduler Module for plugin to Drupal 7 Framework
 *
 */

/*
 *	  CLASS:  SessionValidator
 *    
*/

class SessionValidator {
	const SV_NOCONFLICT = 0;
	const SV_BLOCKED = -1;
	const SV_CONFLICT = -2;
  const SV_SCHEDULE_RANGE = -5;
	const SV_ERROR = -99;
	
	const SV_UNIX_DATE_LIMIT = 2147407200;  // 1-18-2038 GMT
	
	const SV_EXCEPTION = 'sch-exception-block';
	//const SV_MAINTENANCE = 'sch-maintenance';
	const SV_LEADING = 'sch-leading'; 
	const SV_TRAILING = 'sch-trailing';
	
	
	private $validation_result = self::SV_NOCONFLICT;
	private $assignment_id = 0;
  private $exception_id = 0;
	private $schedule_id = 0;
	private $session_id = 0;
	private $session_date = 0;
  private $conflict_assigns = array();
  private $conflict_excepts = array();
	private $exception_id_list = array();
  private $exception_reason_list = array();
  private $exceptions_override = array();
	
	private $session = Null;
  private $session_set = False;
	private $exceptions = array();
	private $exceptions_set = False;
  private $schedule = Null;
  private $timezone = Null;
	
	private $month = 0;
	private $day = 0;
	private $year = 0;
  private $date_format = array();
  
  private $dt;
	
		
	function __construct($assignment_id = 0, $exception_id = 0, $schedule_id = 0, 
                        $session_id = 0, $session_date = 0) {
		if ( $assignment_id || $exception_id || $schedule_id || $session_id || $session_date ) {
			$this->init($assignment_id, $exception_id, $schedule_id, $session_id, $session_date);
		}
		else {
			$this->clear();
		}
	}
	
	
	public function init($assignment_id = 0, 
                       $exception_id = 0, 
                       $schedule_id = 0, 
                       $session_id = 0, 
                       $session_date = 0,
                       $simulator_id = 0) {
    $this->clear();
		
		$this->assignment_id = $assignment_id;
    $this->exception_id = $exception_id;
		$this->schedule_id = $schedule_id;
		$this->session_id = $session_id;
		$this->session_date = $session_date;
    $this->simulator_id = $simulator_id;
   
    $this->timezone = _get_schedule_timezone($schedule_id);
    $this->dt = new DateTime('now', new DateTimeZone($this->timezone));
		
		if ( $session_date ) {
      $this->dt->setTimestamp($session_date);
      $this->day = (int)$this->dt->format('j');
			$this->month = (int)$this->dt->format('n');
			$this->year = (int)$this->dt->format('Y');
		}
    
    $this->date_format = array(
                    'custom_format' => 'm/d/Y H:i',
                    'user_tz' => False,
                    'timezone' => $this->timezone,
                   );
	}	
  
  public function clear() {
		$this->validation_result = self::SV_NOCONFLICT;
		$this->assignment_id = 0;
    $this->exception_id = 0;
		$this->schedule_id = 0;
		$this->session_id = 0;
		$this->session_date = 0;
    $this->simulator_id = 0;
		$this->conflict_assigns = array();
    $this->conflict_excepts = array();
		$this->session = Null;
    $this->schedule = Null;
		$this->exceptions = array();
		$this->exceptions_set = False;
    $this->exceptions_override = array();
    $this->session_set = False;
    $this->month = 0;
		$this->day = 0;
		$this->year = 0;
	}
	
	
	  
	
	private function getSession() {
		try {
      
      $this->session = _get_session($this->schedule_id, $this->session_id);
      if ($this->session) {
        $this->simulator_id = $this->session->simulator_id;
        $this->session_set = True;
				return True;
      }

			$this->validation_result = self::SV_ERROR;
			return False;
		}
		catch (Exception $e) {
			watchdog(SCH_SCHEDULER, 'SessionValidator::getSession() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
		}
	}

		
	private function getExceptions() {
		try {
      
      if ( !$this->simulator_id || !$this->session_date || !$this->schedule_id ) {
        return self::SV_ERROR;
      }
      if (!isset($this->timezone)) {
        $this->timezone = _get_schedule_timezone($this->schedule_id);
      }
      // broken out due to debugging requirements

      // get all exceptions for the specified session_date's month
      // we might be dealing with more than one date in the month in a single processing event
      // so get them all
      $view_month_year = _st_format_system_date($this->session_date, 'F Y');
      
      $dt = new DateTime();
      $dt->setTimezone(new DateTimeZone($this->timezone));
      $dt->modify('first day of ' . $view_month_year);
      $dt->setTime(0,0,0);
      $begins = $dt->getTimestamp();
      
      $dt->modify('last day of ' . $view_month_year);
      $dt->setTime(23,59,59);
      $ends = $dt->getTimestamp();
      
      
      $this->exceptions = _get_exceptions($begins, $ends, $this->timezone, $this->simulator_id);
      
			if ( count($this->exceptions) ) {
				$this->exceptions_set = True;
				return True;
			}
			$this->validation_result = self::SV_NOCONFLICT;
			return False;
		}
		catch (Exception $e) {
			watchdog(SCH_SCHEDULER, 'SessionValidator::getExceptions() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
		}
	}
	
	/* 
	 *	getSimulator()
	 *  gets the simulator_id from the schedule_id
	 */
	private function getSimulatorId() {
    try {
      if ( !$this->schedule_id ) {
        return self::SV_ERROR;
      }
      $query = db_select('sch_schedules', 'sch')
               ->condition('schedule_id', $this->schedule_id)
               ->fields('sch', array('simulator_id'));
      $this->simulator_id = $query->execute()->fetchField();

      if ( !$this->simulator_id || ($this->simulator_id == Null)) {
        return False;
      }
      return True; // no errors
    }
    catch(Exception $e) {
      watchdog(SCH_SCHEDULER, 'SessionValidator::getSimulatorId() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
    }
  }
  
	
	
	/* 
	 *	setSession()
	 *  Accepts a $session object and sets it
	 */
	public function setSession($sess) {
		$this->session = $sess;
    if ( isset($sess->simulator_id) ) {
      $this->simulator_id = $sess->simulator_id;
    }
    $this->session_set = True;
	}
	
	/* 
	 *	setExceptions()
	 *  Accepts an Exception array object and sets it
	 */
	
	public function setExceptions($exceptions) {
		$this->exceptions = $exceptions;
		$this->exceptions_set = True;
	}
  
	
	/* 
	 *	clearRelevantException()
	 *  Clears Rel Exception array
	 */
	
	public function clearRelevantException() {
		$this->conflict_excepts = array();
	}
	
	
	/* 
	 *	setDate()
	 *  Accepts numeric date parts and sets them
	 */
	
	public function setDate($mon, $day, $year) {
		$this->month = $mon;
		$this->day = $day;
		$this->year = $year;
	}
	
	/*	isOverrideableExceptionDate()
	 *
	 *	for the given exceptions, year, month, day
	 *	is the entire day an exception?  True | False
	 */
	
	public function isOverrideableExceptionDate() {
		
		if ( !$this->month || !$this->day || !$this->year ) { 
			//echo 'date not set' . '<br />';
			$this->validation_result = self::SV_ERROR;
			return $this->validation_result; 
		}
		
		// we need to get the exceptions first
		if ( !$this->exceptions_set ) {
			if ( $this->getExceptions() == False ) { 
				//echo 'failed to get exceptions' . '<br />';
				return $this->validation_result; 
			}
		}
    
    if (!isset($this->timezone)) {
        $this->timezone = _get_schedule_timezone($this->schedule_id);
      }
      
    $this->exceptions_override = array();
		
		//$class = '';
		$allow_override = 1;
		$bException = false;

		// EXCEPTIONS
		if (  isset($this->exceptions[$this->day]) ) {
//			$day_begins = _format_timestamp_parts($this->year, $this->month, $this->day, '0000');
//			$day_ends = _format_timestamp_parts($this->year, $this->month, $this->day, '2359');
      
			$dt = new DateTime();
      $dt->setTimezone(new DateTimeZone($this->timezone));
      $dt->setDate($this->year, $this->month, $this->day);
      $dt->setTime(0,0,0);
      $day_begins = $dt->getTimestamp();
      
      $dt->setTime(23,59,0);
      $day_ends = $dt->getTimestamp();
			
			
			foreach( $this->exceptions[$this->day] as $except ) {
				
				if( $except->end_timestamp == 0 ) {
					$except->end_timestamp = self::SV_UNIX_DATE_LIMIT;
				}
				
				//if ( ($except->begin_timestamp <= $day_begins) && ( $day_ends <= $except->end_timestamp)) {
					$bException = True;
					$this->exception_id_list[] = $except->exception_id;
//					$this->exception_reason_list[] = $except->exception_desc;
					$allow_override = (int)$except->allow_override;
          $this->exceptions_override[] = $except;
				//}
				
			}
			
//			if ( count($this->exception_id_list) ) {
//				$this->exception_id_list = array_unique($this->exception_id_list, SORT_NUMERIC);
//				$this->exception_reason_list = array_unique($this->exception_reason_list, SORT_STRING);
//			}
			
		}
		
		return ($bException && $allow_override);
	}
  
  private function checkForConflictWithAssignment($begin_ts = 0, $end_ts = 0) {
		try {
			// see if an assignment exists for this date
			// for an exising assignment, look for assignments, but ignore this one
			// for new assignments, look for assignments (assignment_id==0) which still works for this validation
			
			// also, not using the session_id.  We don't care about it for this as more than one assignment can
			// overlap a single session.  Just seeing if there are overlapping timestamps
			
			$this->conflict_assigns = array();
			
			if ( !$this->session_date && !$this->session_set && !$begin_ts && !$end_ts ) {
				return False;
			}
      
			// substract a second to prevent single second overlap
			//$begin = ($begin_ts ? $begin_ts + 1 : _format_timestamp($this->session_date, '00:00:01'));
			//$end = ($end_ts ? $end_ts - 1 : _format_timestamp($this->session_date, '23:59:59'));
      
      if ( !$begin_ts || !$end_ts ) {
        $begin = _format_timestamp($this->session_date, $this->session->begin_time, $this->timezone);
        $end = _format_timestamp($this->session_date, $this->session->end_time, $this->timezone);
        if ( (int)$this->session->begin_time > (int)$this->session->end_time ) {
          //$end += SCH_ONE_TS_DAY;
          $end = _add_day_ts($end);
        }
      }
      else {
        $begin = $begin_ts + 1;
        $end = $end_ts - 1;
      }
      
      
		
			$query = db_select('sch_assignments', 'a');
			$query->innerJoin('sch_customers', 'cu', 'a.customer_id = cu.customer_id');		
			
			// time range is contained within assignment time
			$and_1 = db_and()->condition('a.begin_timestamp', $begin, '<=')->condition('a.end_timestamp', $end, '>=');
			//time range completely overlaps assignment time
			$and_2 = db_and()->condition('a.begin_timestamp', $begin, '>=')->condition('a.end_timestamp', $end, '<=');
			//assignment mid-to-end overlaps time range
			$and_3 = db_and()->condition('a.end_timestamp', $begin, '>')->condition('a.end_timestamp', $end, '<=');
			// assignment beginning-to-mid overlaps time range
			$and_4 = db_and()->condition('a.begin_timestamp', $begin, '>=')->condition('a.begin_timestamp', $end, '<');
			
			$or = db_or()->condition($and_1)->condition($and_2)->condition($and_3)->condition($and_4);
			$query->condition($or);
			
			//$query->condition('a.begin_timestamp', $begin_date, '>=');
			//$query->condition('a.end_timestamp', $end_date, '<=');
			
			$query->condition('a.cut_bill', 0); // do not include cut_bill assignments
      
      // this might be zero in the case of exceptions... that's ok
      if ( isset($this->schedule_id) && $this->schedule_id ) {
        $query->condition('a.schedule_id', $this->schedule_id);
      }
			// don't anchor to a single session - search based on timestamps
			//$query->condition('a.schedule_session_id', $this->session_id);
      //
      // assignment_id might be zero... that's ok
			$query->condition('a.assignment_id', $this->assignment_id, '!=');
      
			$query->leftJoin('sch_schedule_sessions', 'ss', 'a.schedule_session_id = ss.schedule_session_id');
			$query->leftJoin('sch_session_names', 'sn', 'ss.session_name_id = sn.session_name_id');
			$query->fields('a', array('assignment_id', 'schedule_id', 'schedule_session_id', 'customer_id', 
																 'begin_time_actual', 'end_time_actual', 
																 'begin_timestamp', 'end_timestamp'))
						->fields('sn', array('session_name'))
						->fields('cu', array('customer', 'customer_code') );
			$results = $query->execute();
		
			if ( $results->rowCount() ) {
	
				// conflict exists
				$this->validation_result = self::SV_CONFLICT;
				$this->conflict_assigns = $results->fetchAll();
        foreach($this->conflict_assigns as $idx => $a) {
          $this->conflict_assigns[$idx]->begin_timestamp_f = _st_format_date($a->begin_timestamp, 'custom', 
                                                                             $this->date_format);
          $this->conflict_assigns[$idx]->end_timestamp_f = _st_format_date($a->end_timestamp, 'custom', 
                                                                             $this->date_format);
        }
				return True;
			}
			$this->validation_result = self::SV_NOCONFLICT;
			return False;
		}
		catch (Exception $e) {
			watchdog(SCH_SCHEDULER, 'SessionValidator::checkForConflictWithAssignment() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
		}
	}
  
  private function checkForExceptionConflictWithException($begin_ts = 0, $end_ts = 0) {
		try {
			// see if an assignment exists for this date
			// for an exising assignment, look for assignments, but ignore this one
			// for new assignments, look for assignments (assignment_id==0) which still works for this validation
			
			// also, not using the session_id.  We don't care about it for this as more than one assignment can
			// overlap a single session.  Just seeing if there are overlapping timestamps
			
			$this->conflict_excepts = array();
			
			if ( !$this->session_date && !$this->session_set &&  !$begin_ts && !$end_ts ) {
				return False;
			}
      
			// substract a second to prevent single second overlap
			//$begin = ($begin_ts ? $begin_ts + 1 : _format_timestamp($this->session_date, '00:00:01'));
			//$end = ($end_ts ? $end_ts - 1 : _format_timestamp($this->session_date, '23:59:59'));
      
      if ( !$begin_ts || !$end_ts ) {
        $begin = _format_timestamp($this->session_date, $this->session->begin_time, $this->timezone);
        $end = _format_timestamp($this->session_date, $this->session->end_time, $this->timezone);
        if ( (int)$this->session->begin_time > (int)$this->session->end_time ) {
          $end += SCH_ONE_TS_DAY;
        }
      }
      else {
        $begin = $begin_ts + 1;
        $end = $end_ts - 1;
      }
			
			
		
			$query = db_select('sch_exceptions', 'e');
	
			
			// time range is contained within exception time
			$and_1 = db_and()->condition('e.begin_timestamp', $begin, '<=')->condition('e.end_timestamp', $end, '>=');
			//time range completely overlaps excepton time
			$and_2 = db_and()->condition('e.begin_timestamp', $begin, '>=')->condition('e.end_timestamp', $end, '<=');
			//excepton mid-to-end overlaps time range
			$and_3 = db_and()->condition('e.end_timestamp', $begin, '>')->condition('e.end_timestamp', $end, '<=');
			// excepton beginning-to-mid overlaps time range
			$and_4 = db_and()->condition('e.begin_timestamp', $begin, '>=')->condition('e.begin_timestamp', $end, '<');
			
			$or_dates = db_or()->condition($and_1)->condition($and_2)->condition($and_3)->condition($and_4);
			$query->condition($or_dates);
      
      if ( $this->simulator_id ) {
        $or_sim = db_or()->condition('e.simulator_id', $this->simulator_id)
                      ->condition('e.simulator_id', 0);
        $query->condition($or_sim);
      }
      
      $query->condition('e.allow_override', 0);
			$query->condition('e.exception_id', $this->exception_id, '!=');
      
			$query->fields('e', array('exception_id', 'simulator_id', 'exception_desc',
                                'begin_timestamp', 'end_timestamp', 'allow_override', 'maintenance',
                                'event', 'customer_id', 'note_text', 
																 'created_date', 'created_by_user', 
																 'updated_date', 'updated_by_user'));

			$results = $query->execute();
		
			if ( $results->rowCount() ) {
	
				// conflict exists
				$this->validation_result = self::SV_CONFLICT;
				$this->conflict_excepts = $results->fetchAll();
        foreach($this->conflict_excepts as $idx => $e) {
          $this->conflict_excepts[$idx]->begin_timestamp_f = _st_format_date($e->begin_timestamp, 'custom', 
                                                                               $this->date_format);
          $this->conflict_excepts[$idx]->end_timestamp_f = _st_format_date($e->end_timestamp, 'custom', 
                                                                               $this->date_format);
        }
				return True;
			}
			$this->validation_result = self::SV_NOCONFLICT;
			return False;
		}
		catch (Exception $e) {
			watchdog(SCH_SCHEDULER, 'SessionValidator::checkForExceptionConflictWithException() ' . 
                  $e->getMessage(), array(), WATCHDOG_ERROR);
		}
	}


	
	/*
	 *	checkForConflictWithException()
	 *
	 *	for the given exceptions, year, month, day, session
	 *	determine if the exceptions overlap the session, if they do, return the appropriate class to mark the 
	 *  sch-session-open or sch-session-assignment divs in the display
	 */

	public function checkForConflictWithException($begin_ts = 0, $end_ts = 0) {

		if ( !$this->month || !$this->day || !$this->year ) { 
			//echo 'date not set' . '<br />';
			$this->validation_result = self::SV_ERROR;
			return $this->validation_result; 
		}
		
		// we need to get the exceptions first
		if ( !$this->exceptions_set ) {
			if ( $this->getExceptions() == False ) { 
				//echo 'failed to get exceptions' . '<br />';
				return $this->validation_result; 
			}
		}
		
    // clear out the old values
    $this->conflict_excepts = array();
		$has_exception = False;
    $ex = 0;
		

		// EXCEPTIONS
		if (  isset($this->exceptions[$this->day]) || isset($this->exceptions[$this->day+1])  ) {
      
      // bump the seconds by 1 in either direction to avoid problems with perceived overlap (by seconds)
			if ( !$begin_ts || !$end_ts ) {
        $i_sess_begin_time = _format_timestamp_parts($this->year, 
                                                     $this->month, 
                                                     $this->day, 
                                                     $this->session->begin_time, 
                                                     $this->session->timezone) + 1;
        $i_sess_end_time = _format_timestamp_parts(  $this->year, 
                                                     $this->month, 
                                                     $this->day, 
                                                     $this->session->end_time, 
                                                     $this->session->timezone) - 1;  
        if ( (int)$this->session->begin_time > (int)$this->session->end_time )   {
          // this happens when the last session wraps into the next day.
          $i_sess_end_time = _st_modify_system_timestamp($i_sess_end_time, '+1 day');
        }
      }
      else {
        $i_sess_begin_time = $begin_ts + 1;
        $i_sess_end_time = $end_ts - 1;
      }
//      if (!isset($this->session->timezone)) {
//        $debug = 1;
//      }
      
      $session_begin_time_f = _st_format_schedule_date($i_sess_begin_time, 'short', $this->session->timezone);
      $session_end_time_f = _st_format_schedule_date($i_sess_end_time, 'short', $this->session->timezone);

			

			// cycle through all of the exceptions for this date and the next (session bleed over) and mark the class
			$ex_array = array_merge( (isset($this->exceptions[$this->day]) ? 
                                    $this->exceptions[$this->day] : array()),
															 (isset($this->exceptions[$this->day+1]) ? 
                                    $this->exceptions[$this->day+1] : array()) );

			foreach ($ex_array as $except) {
				
				// if exception does not have an end date, assign it a pseudo end date (1-18-2038, unix limit) 
				// makes for easier comparison
				if( $except->end_timestamp == 0 ) { $except->end_timestamp = self::SV_UNIX_DATE_LIMIT; }

				//check the assignment begin & end times against the exception begin & end times
				if ( ($except->begin_timestamp <= $i_sess_begin_time) && 
             ($i_sess_end_time <= $except->end_timestamp) ) {
					
					// assignment falls in the middle of the exception - add exception class
					// fully excepted date, no need to continue
          // ignore if exception isn't a block
					if ( 0 == (int)$except->allow_override ) {
            //$allow_override = False;
            $has_exception = True;
            
            $this->exception_id_list[] = $except->exception_id;
            $this->exception_reason_list[] = $except->exception_desc;
            
            $ex = $i_sess_begin_time;
            while ( isset($this->conflict_excepts[$ex]) && 
                    ($this->conflict_excepts[$ex]->exception_id != $except->exception_id) ) {
              $ex++; // indexed by begin_time, increment by a second until 
            }
            $this->conflict_excepts[$ex] = clone $except;
            $this->conflict_excepts[$ex]->percent = 1;
            $this->conflict_excepts[$ex]->begin_timestamp = $i_sess_begin_time-1;
            $this->conflict_excepts[$ex]->end_timestamp = $i_sess_end_time+1;   
          }
				}
				else if ( ($i_sess_begin_time <= $except->begin_timestamp ) && ($except->end_timestamp <= $i_sess_end_time ) ) {
					// exception completely contained by the session
          // ignore if exception isn't a block
					if ( 0 == (int)$except->allow_override ) {
            $has_exception = True;
            $this->exception_id_list[] = $except->exception_id;
            $this->exception_reason_list[] = $except->exception_desc;
            
            $ex = $except->begin_timestamp;
            while ( isset($this->conflict_excepts[$ex]) && 
                    ($this->conflict_excepts[$ex]->exception_id != $except->exception_id) ) {
              $ex++; // indexed by begin_time, increment by a second until 
            }
            
            $this->conflict_excepts[$ex] = clone $except;
            $this->conflict_excepts[$ex]->percent = 
                         round(($except->end_timestamp - $except->begin_timestamp) / 
                        ($i_sess_end_time - $i_sess_begin_time),2);
            $this->conflict_excepts[$ex]->begin_timestamp = $except->begin_timestamp;
            $this->conflict_excepts[$ex]->end_timestamp = $except->end_timestamp;
          }
				}
				else if ( ($i_sess_begin_time <= $except->begin_timestamp) && 
									($except->begin_timestamp < $i_sess_end_time ) && 
				          ( $i_sess_end_time <= $except->end_timestamp ) ) {
					// exception overlaps middle to end of session
					// ignore if exception isn't a block
					if ( 0 == (int)$except->allow_override ) {
            $allow_override = False;
            $has_exception = True;
            
						$this->exception_id_list[] = $except->exception_id;
						$this->exception_reason_list[] = $except->exception_desc;
            
            $ex = $except->begin_timestamp;
            while ( isset($this->conflict_excepts[$ex]) && 
                    ($this->conflict_excepts[$ex]->exception_id != $except->exception_id) ) {
              $ex++; // indexed by begin_time, increment by a second until 
            }
            
            $this->conflict_excepts[$ex] = clone $except;
            $this->conflict_excepts[$ex]->percent = 
                         round(($i_sess_end_time - $except->begin_timestamp) / 
                    ($i_sess_end_time - $i_sess_begin_time), 2);
            $this->conflict_excepts[$ex]->begin_timestamp = $except->begin_timestamp;
						$this->conflict_excepts[$ex]->end_timestamp = $i_sess_end_time+1;    
					}
				}
				else if ( ($except->begin_timestamp <= $i_sess_begin_time) && 
									($i_sess_begin_time < $except->end_timestamp )  &&
									( $except->end_timestamp <= $i_sess_end_time) ) {
					// exception overlaps beginning to middle of session	
							
					if ((int)$except->allow_override == 0) { 
            $has_exception = True;
						$allow_override = False; 
					
						$this->exception_id_list[] = $except->exception_id;
						$this->exception_reason_list[] = $except->exception_desc;
						
            $ex = $i_sess_begin_time;
            while ( isset($this->conflict_excepts[$ex]) && 
                    ($this->conflict_excepts[$ex]->exception_id != $except->exception_id) ) {
              $ex++; // indexed by begin_time, increment by a second until 
            }
            
            $this->conflict_excepts[$ex] = clone $except;
            $this->conflict_excepts[$ex]->percent = 
                    round(($except->end_timestamp - $i_sess_begin_time) / 
                      ($i_sess_end_time - $i_sess_begin_time),2);
            $this->conflict_excepts[$ex]->begin_timestamp = $i_sess_begin_time-1;
						$this->conflict_excepts[$ex]->end_timestamp = $except->end_timestamp;       
					}
				}
        
        
			} // End - Foreach Loop
     
		}	
			
		// figure out the appropriate class setting
		// if this isn't a blocked session, do not return an exception class, 
		// causes unnecessary grid clutter
		
		if ( $has_exception ) {
			
			$this->validation_result = self::SV_BLOCKED;
      $class = self::SV_EXCEPTION;
 
      if ( count($this->exception_id_list) ) {
        $this->exception_id_list = array_unique($this->exception_id_list, SORT_NUMERIC);
        $this->exception_reason_list = array_unique($this->exception_reason_list, SORT_STRING);
        ksort($this->conflict_excepts);  // order by the begin_timestamp
        
        foreach($this->conflict_excepts as $idx => $e) {
          $this->conflict_excepts[$idx]->begin_timestamp_f = _st_format_date($e->begin_timestamp, 'custom', 
                                                                               $this->date_format);
          $this->conflict_excepts[$idx]->end_timestamp_f = _st_format_date($e->end_timestamp, 'custom', 
                                                                               $this->date_format);
        }
      }
		}

		return $this->validation_result;
	}
  
  private function getSchedule() {
    if (empty($this->schedule_id)) { return False; }
    
    $this->schedule = _get_schedule($this->schedule_id);
    if (!$this->schedule) { return False; }
    
    $this->date_format['timezone'] = $this->timezone;
    return True;
  }
  
  public function checkScheduleRange($begin_ts = 0, $end_ts = 0) {
    try{
      $this->getSchedule();
      if (!$this->schedule) {
        // something went wrong
        $this->validation_result = self::SV_ERROR;
        return $this->validation_result; 
      }
      
      if ( ( !empty($this->schedule->end_date) && ($this->schedule->end_date < $end_ts) ) ||
           ( $begin_ts < $this->schedule->begin_date) ) {
        $this->validation_result = self::SV_SCHEDULE_RANGE;
        return $this->validation_result; 
      }
      $this->validation_result = self::SV_NOCONFLICT;
      return $this->validation_result; 
      
    } catch (Exception $ex) {
      watchdog(SCH_SCHEDULER, 'SessionValidator::checkScheduleRange() ' . $e->getMessage(), 
               array(), WATCHDOG_ERROR);
    }
  }
  
	
	
	
	public function checkForConflict($mode = 'A', $begin_ts = 0, $end_ts = 0){
    
    // check schedule range first
    if ( !empty($this->schedule_id) && 
         $this->checkScheduleRange($begin_ts, $end_ts)) { 
      return $this->validation_result; 
      
    }
		
    if ( 'A' == $mode ) {
      
      //-------------- Assignment Validation -----------------
      // if submitted, use it, but not required when passing in date range
      if ( $this->schedule_id && $this->session_id && $this->session_date ) { 
        if ( !$this->getSession() ) { return $this->validation_result; }
      }
      
      if ( $this->checkForConflictWithAssignment($begin_ts, $end_ts) ) { 
        return $this->validation_result; 
      }
      
      
      if ( $this->checkForConflictWithException($begin_ts, $end_ts) ) { 
        return $this->validation_result; 
      }
    }
    else if ( 'E' == $mode ) {
      
      //-------------- Exception Validation -----------------
      
      // if submitted, use it, but not required when passing in date range
      if ( $this->schedule_id && $this->session_id && $this->session_date  ) { 
        if ( !$this->getSession() ) { return $this->validation_result; }
      }
      
      // try to get the sim id from the schedule id, though not required
      if ( $this->schedule_id && !$this->simulator_id) {
        if ( !$this->getSimulatorId() ) { return self::SV_ERROR; }
      }
      
      if ( $this->checkForConflictWithAssignment($begin_ts, $end_ts) ) { 
        return $this->validation_result; 
        
      }
      
      if ( $this->checkForExceptionConflictWithException($begin_ts, $end_ts) ) { 
        return $this->validation_result; 
      }
    }
    else {
      return self::SV_ERROR; 
    }
    

		return $this->validation_result;
	}
  
  
	
	
	public function getValidationResult() {
		return $this->validation_result;
	}
	public function getAssignmentConflicts() {
		return $this->conflict_assigns;
	}
  public function getExceptionConflicts() {
		return $this->conflict_excepts;
	}
	public function getExceptionIds() {
		return implode('|', $this->exception_id_list);
	}
	public function getExceptionReasons() {
		return implode(', ', $this->exception_reason_list);
	}
  
  public function getExceptionInfo() {
    $content = '';
		foreach ($this->exceptions_override as $ex) {
      $sim_name = !empty($ex->simulator_id) ? _get_simulator_name($ex->simulator_id) : t('ALL SIMULATORS');
      
      $content .= $ex->exception_desc . '<br />';
      $content .= _st_format_date($ex->begin_timestamp). ' - '._st_format_date($ex->end_timestamp).'<br />';
      $content .= $sim_name.'<br /><br />';
    }
    return $content;
	}
  
  public function getScheduleRange() {
    $begin_date_f = _st_format_date($this->schedule->begin_date, 'custom', $this->date_format);
    $end_date_f = _st_format_date($this->schedule->end_date, 'custom', $this->date_format);
    return array($begin_date_f, $end_date_f);
  }
	
	
	
} // end - class SessionValidator



