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

/*
 *	exceptions_search() 
 *
 */

function exceptions_search() {
	
	if ( user_access('view manage menu') == FALSE ) {
		drupal_set_message( t('Unauthorized.  Permission is required.'), 'status');
		drupal_goto('');
		return '';
  }
	
	drupal_add_library('system','ui.dialog');
	drupal_add_library('system','ui.datepicker');
	drupal_add_js(drupal_get_path('module', 'sabreTools') . '/js/sabreTools.lib.js');
	drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/sabreScheduler.exceptions.search.js');


	$form_cache_name = SCH_EXCEPTIONS_SEARCH_FORM;

	$search_form = '';
	$search_results = '';
	
	
	$form_cache = cache_get($form_cache_name);
	if ( isset($form_cache->data) && ($form_cache->data <> '') ) {
		$search_form = $form_cache->data;
	}
	else {
	
		// search results not in cache, create empty div for ajax to populate after search
		// search results will get populated to cache after ajax call
    $df = drupal_get_form('exceptions_search_form');
		$search_form = render($df);
	
		//maintains cache entry for the form for 20 min before refresh (time in seconds)
		cache_set($form_cache_name, $search_form, 'cache', REQUEST_TIME + QMS_CACHE_TIMEOUT);			
	}	
	

	// determine cache name
	$results_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_RESULTS);	

	$results_cache = cache_get($results_cache_name);
	if ( isset($results_cache->data) && ($results_cache->data <> '') ) {
		$search_results = $results_cache->data;
	}
	else {
		// search results not in cache, create empty div for ajax to populate after search
		// search results will get populated to cache after ajax call
		$search_results = '<div id="sch-search-results-div"></div>';
		
		cache_set($results_cache_name, $search_results, 'cache', REQUEST_TIME + QMS_CACHE_TIMEOUT);			
	}
	
	$content = $search_form . $search_results;
	
	// text strings for multilang handling, positional, pipe-delim
	$content .= '<div id="sch-strings" class="qms-hidden">' . 
									t('OK') . '|' . 
									t("Server Request Error") . '|' . 
									t('The server has experienced an error.  Please refresh the page and try again.') . '|' . 
									t('The server has experienced an error.  Please refresh the page and try again.') . '|' . 
									t("The server request has timed out.  Please refresh the page and try again.") . 
							'</div>';
	
	
	return $content;
}

/*
 * 	exceptions_search_form()
 * 	display form to get user input of search parameters
 *
 */

function exceptions_search_form($form, $form_state) {
	
		//------------ BUILD SEARCH FORM ------------------
		global $base_url;

		$simulator_list = new SimulatorList();
		

		$form['search'] = array(
			'#type' => 'fieldset',
			'#title' => t('Search'),
			'#collapsible' => True,
			'#collapsed' => False,
			'#attributes' => array('id' => array('sch-search-div')),
		);
		
		$form['search']['search_table_begin'] = array(
			'#markup' => '<table class="qms-plain-table"><tr><td rowspan="2" style="width:40%;">'
		);
		

		$form['search']['simulator'] = array(
			'#type' => 'select',
			'#title' => t('Simulator'),
			'#options' => $simulator_list->getAll(),
			'#default_value' => 0,
			'#attributes' => array('class' => array('qms-select'),
														'id' => 'sch-simulator'),
		);

		$form['search']['exception_desc'] = array(
			'#type' => 'textfield',
			'#title' => t('Exception Description'),
			'#default_value' => '',
			'#size' => 30,
			'#maxlength' => 100,
			'#attributes' => array('id' => 'sch-exception-desc'),
		);
		
		$form['search']['maintenance'] = array(
			'#type' => 'checkbox',
			'#title' => t('Maintenance'),
			'#default_value' => 0,
			'#attributes' => array('id' => 'sch-exception-maintenance'),
		);
    
    $form['search']['event'] = array(
			'#type' => 'checkbox',
			'#title' => t('Event'),
			'#default_value' => 0,
			'#attributes' => array('id' => 'sch-exception-event'),
		);
		
		$form['search']['override'] = array(
			'#type' => 'checkbox',
			'#title' => t('Override Allowed'),
			'#default_value' => 0,
			'#attributes' => array('id' => 'sch-exception-override'),
			'#suffix' => '</td>',
		);
		
		
		$form['search']['markup1'] = array(
			'#markup' => '<td colspan="2" style="vertical-align:top">',
		);
		
		
		$form['search']['search_date_label'] = array(
			'#markup' => '<label style="padding-bottom:8px">' . 
                    t('Search Exceptions by Date Range') . '</label></td></tr>',
		);
		


		$form['search']['from_date'] = array(
			'#type' => 'date_popup',
			'#title' => t('From Date'),
			'#size' => 10,
			'#date_format' => 'm-d-Y',					 	
			'#attributes' => array('style' => array('float:left', 'width:50px'),
															'id' => 'sch-from-date',
															'class' => array('qms-date-picker')),
			'#prefix' => '<tr><td style="width:22%;text-align:left;">',
			'#suffix' => '</td>',
		);

		$form['search']['to_date'] = array(
			'#type' => 'date_popup',
			'#title' => t('To Date'),
			'#size' => 10,
			'#date_format' => 'm-d-Y',
			'#attributes' => array('style' => array('float:left', 'clear:right', 'width:50px'),
															'id' => 'sch-to-date',
																'class' => array('qms-date-picker')),
			'#prefix' => '<td>',
			'#suffix' => '</td></tr></table>',
		);


		//--------------------------------------------
		// Adds a simple submit button that refreshes the form and clears its contents 
		// -- this is the default behavior for forms.
		$form['search']['actions'] = array('#type' => 'actions');
		$form['search']['actions']['submit'] = array(
			'#type' => 'button',
			'#value' => 'Search',
			'#attributes' => array('id' => 'sch-btn-search',
															'sch-url' => url('exceptions/search') ),
	/*	DO NOT do Drupal AJAX here, being done through JQuery by the javascript file
	**	as we need the .live("click") function due to form caching, which won't work here with Drupal
	*/
		);
    
    $form['search']['actions']['clear'] = array(
      '#type' => 'button',
      '#value' => t('Clear'),
      '#attributes' => array('class' => array('sch-clear', 'qms-btn-extra'),
                             'id' => 'sch-btn-clear',
                             'sch-url' => url('exceptions/clear') ),
      '#suffix' => '<span class="qms-waiting"><img class="qms-waiting-img" src="' . 
										$base_url . QMS_IMAGES_DIR . 'waiting.gif" /></span>',
    );
    
//		$form['search']['actions']['clear'] = array(
//			'#markup' => l(t('Clear'), '', array('attributes' => array('id' => 'sch-btn-clear',
//			 																														'sch-url' => url('exceptions/clear') ))) . 
//										'<span class="qms-waiting"><img class="qms-waiting-img" src="' . 
//										$base_url . QMS_IMAGES_DIR . 'waiting.gif" /></span>',
//		);
		
		// storage area for dynamic dialog element
		$form['popup_dialog'] = array(
			'#markup' => '<div id="sch-popup-dialog"></div>',
		);
		

		$form['add_exception'] = array(
			'#markup' => l(t('Add Exception'), 'exception/add',	array('attributes' => array('class' => array('sch-exception-add')) ) ),
		);

		return $form;
}

/**
 * exceptions_search_callback()
 * displays the search results, first page only
 * Has to be a separate handler from pager & sort link callback
 * This is the callback that gets called when the Search button is clicked
 *
 */
function exceptions_search_callback() {
	
	$search = new stdClass();
	
	$search->simulator_id = trim($_POST['simulator']);
	$search->exception_desc = trim($_POST['exception_desc']);
	$search->from_date = trim($_POST['from_date']);
	$search->to_date = trim($_POST['to_date']);
	$search->maintenance = (int)$_POST['maintenance'];
  $search->event = (int)$_POST['event'];
	$search->allow_override = (int)$_POST['allow_override'];
	
	// cache the latest search key
	// determine cache name, always use it specific to this user + session
	// set key cache to expire in 30 minutes
	$key_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_KEY);
	
	cache_set($key_cache_name, $search, 'cache', REQUEST_TIME + QMS_CACHE_TIMEOUT);		

	die(_get_exceptions_search_list($search));
}


/**
 * exceptions_pager_callback()
 * displays the search results, for subsequent page results
 * Has to be a separate handler from pager & sort link callback
 * This is the callback that gets called when the page number links are clicked
 *
 */
function exceptions_pager_callback() {
	
	$search = new stdClass();
	
	// determine the key cache
	$key_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_KEY);
	
	$key_cache = cache_get($key_cache_name);
	if ( isset($key_cache->data) && ($key_cache->data <> '') ) {
		$search = $key_cache->data;
	}
	else {
		// search key not in cache, get it from POST
		// though, this should never happen... should always be in cache
		$search->simulator_id = trim($_POST['simulator']);
		$search->exception_desc = trim($_POST['exception_desc']);
		$search->from_date = trim($_POST['from_date']);
		$search->to_date = trim($_POST['to_date']);
		$search->maintenance = (int)$_POST['maintenance'];
    $search->event = (int)$_POST['event'];
		$search->allow_override = (int)$_POST['allow_override'];

		
		// cache the latest search key
		cache_set($key_cache_name, $search, 'cache', REQUEST_TIME + QMS_CACHE_TIMEOUT);		
	}
	
	die(_get_exceptions_search_list($search));
}


/*
 * exceptions_clear_callback()
 * 
 * Clears the search results cache
 *
 */

function exceptions_clear_callback() {
	
	// Clear the cache 
	$search_results = '<div id="sch-search-results-div"></div>';
	
	// determine cache name
	$results_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_RESULTS);
	$key_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_KEY);
	
	cache_set($results_cache_name, $search_results, 'cache', REQUEST_TIME + QMS_CACHE_TIMEOUT);
	cache_clear_all($key_cache_name, 'cache', TRUE); 
	// this is an ajax callback.  Need to return + die or it renders a full page
	// we only want a piece of a page
	
	die($search_results);  
}


/*
 * _get_exceptions_search_list()
 * display the search results
 * This function gets called by exceptions_search_callback() and exceptions_pager_callback()
  *
 */

function _get_exceptions_search_list($search) {
	
	if ( user_access('view manage menu') == FALSE ) {
		drupal_set_message( t('Unauthorized.  Permission is required.'), 'status');
		drupal_goto(''); // go to main page -- not authorized for any other page
		return '';
  }
  
  $timezone = _get_schedule_timezone();
  
  $bEdit = user_access('edit schedules');
  
	
	$table_header = array( 
		array( 'data' => t('From'), 'field' => 'begin_timestamp', 'sort' => 'desc', 'class' => array('sch-tbl-exception-begins') ),
		array( 'data' => t('To'), 'field' => 'end_timestamp', 'sort' => 'desc', 'class' => array('sch-tbl-exception-ends') ),
		array( 'data' => t('Exception'), 'field' => 'exception_desc', 'sort' => 'asc', 'class' => array('sch-tbl-exception-desc') ),
		array( 'data' => t('Simulator'), 'field' => 'sim_name', 'sort' => 'asc', 'class' => array('sch-tbl-exception-simulator') ),
		array( 'data' => t('Maint?'), 'field' => 'maintenance', 'sort' => 'asc', 'class' => array('sch-tbl-exception-maintenance') ),
    array( 'data' => t('Event?'), 'field' => 'event', 'sort' => 'asc', 'class' => array('sch-tbl-exception-event') ),
		array( 'data' => t('Block?'), 'field' => 'allow_override', 'sort' => 'asc', 'class' => array('sch-tbl-exception-override') ),
		
	);
  
  if ( $bEdit ) { 
    $table_header[] = array( 'data' => t('Admin'), 'class' => array('qms-results-admin') ); 
  }
  
  $search_init = array(
		'table_name' => 'sch_exceptions',
		'table_alias' => 'e',
		'table_header' => $table_header,
		'pager_path' => 'exceptions/pager',
		'order' => 'begin_timestamp',
		'sort' => 'asc',
	);
	
	$query = _st_setup_paged_search($search_init);
	
	// Need to use the DB abstration layer here for the paging features
	//$query = db_select('sch_exceptions', 'e');
	
	
	//--------- Date filtering -----------
	
	if ( isset($search->from_date) || isset($search->to_date) ) {
		if ( (strlen($search->from_date) > 0) && (strlen($search->to_date) > 0)) {
			$query->condition('begin_timestamp',
                        _st_format_schedule_timestamp($search->from_date . " 00:00:00", $timezone), '>=');
			$query->condition('end_timestamp', 
                        _st_format_schedule_timestamp($search->to_date . " 23:59:59", $timezone), '<=');
		}
		else if (strlen($search->from_date) > 0) {
			$query->condition('begin_timestamp', 
                        _st_format_schedule_timestamp($search->from_date . " 00:00:00", $timezone), '>=');
		}
		else if (strlen($search->to_date) > 0) {
			$query->condition('begin_timestamp', 
                        _st_format_schedule_timestamp($search->to_date . " 23:59:59", $timezone), '<=');
		}
	}
	
	// simulator
	$query->leftJoin('sch_simulators', 's', 'e.simulator_id = s.simulator_id');
	if ( isset($search->simulator_id) && $search->simulator_id ) {
		$query->condition('e.simulator_id', $search->simulator_id);
	}
	
	// exception_desc	
	if ( $search->exception_desc <> '' ) {

		//---------------------- Keyword Seaching:  Exception Desc --------------
		// Combo Keyword and Exact Phrase Matching ( ex:  keyword "exact phrase" )
		_st_add_keyword_search_condition($query, 'e.exception_desc', $search->exception_desc);
	}	
	
	// maintenance
	// only a search condition if maintenance == True
	if ( isset($search->maintenance) && $search->maintenance ) {
		$query->condition('e.maintenance', $search->maintenance);
	}
  
  // event
	// only a search condition if event == True
	if ( isset($search->event) && $search->event ) {
		$query->condition('e.event', $search->event);
	}
	
	// allow_override
	// only a search condition if allow_override == False (blocked)
	if ( isset($search->allow_override) &&  !$search->allow_override ) {
		$query->condition('e.allow_override', $search->allow_override);
	}
	
	
	$query->fields('e', array('exception_id', 'exception_desc', 'simulator_id', 
														'maintenance', 'event', 'allow_override', 
                            'begin_timestamp', 'end_timestamp') )
				->fields('s', array('sim_name', 'active', 'device_id_internal'));
  
  $max_count = 0;
	$result = _st_execute_paged_search($query, $max_count);
							
	
	$i = 0;
	$table_rows = array();
	
	foreach ($result as $row) {
    $simulator_name = (!empty($row->simulator_id) ? 
                        _st_format_sim_name($row->sim_name, $row->device_id_internal, $row->active):
                       'ALL'
                      );
		$row_data = array(
			_st_format_system_date($row->begin_timestamp, 'short'),
			( $row->end_timestamp ? _st_format_schedule_date($row->end_timestamp, 'short', $timezone) : ''),
			$row->exception_desc,
			$simulator_name,
			( $row->maintenance ? 'x' : ''),
      ( $row->event ? 'x' : ''),
			( $row->allow_override ? '' : 'x'),
		);
    
    if ( $bEdit ) { 
      $row_data[] = array(
          'data' => _st_generate_options('exception', $row->exception_id, 'exceptions'),
          'class' => array('qms-results-admin'));
    }
				
		$table_rows[] = array('data' => $row_data);
		$i++;
	}
	
	$content = '<div id="sch-search-results-div">';
	
	$content .= theme( 'table', array(
		'header' => $table_header,
		'rows' => $table_rows,
		'empty' => t('None'),
	));
	
	//Append pager:  http://api.drupal.org/api/drupal/includes--pager.inc/function/theme_pager
	$content .= theme('pager', array('tags' => array(), 'quantity' => QMS_RECORDS_PER_PAGE));
	$content .= '</div>';
	
	// Save to cache
	$results_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_RESULTS);
	
	$search_results = $content;
	cache_set($results_cache_name, $search_results, 'cache', REQUEST_TIME + (30*60));
	
	return $content;
}

/*
 *	exception_form()
 *
 *  overloads:  hook_form()
 *
 *	Displays the exception form for adding/updating
 */

function exception_form($form, $form_state, $exception_id = 0, $simulator_id = 0, $schedule_id = 0) {

	if ( False == user_access('view manage menu') ) {
		drupal_set_message( t('Unauthorized.  Permission is required.'), 'status');
		drupal_goto(''); 
		return $form;	
  }
  
  $destination = drupal_get_destination();
	$goto_url = $destination['destination'];

	if( !strlen($goto_url) || ($goto_url == current_path()) ) {
		$goto_url = 'exceptions';
	}
  
  if ( False == user_access('edit schedules') ) {
		drupal_set_message( t('Unauthorized.  Permission is required.'), 'status');
		drupal_goto($goto_url); 
		return;	
  }
  
  $timezone = _get_schedule_timezone($schedule_id);
 

	$exception = (object) Null;
  $schedule = (object) Null;
	$simulator_list = new SimulatorList();
  $customer_list = new CustomerList();
  
  $sel_sessions = '';
  $sel_session_list = array();
  $session_id = 0;
  $session_date = '';
  $session = (object) Null;
  
  
  if ( isset($_SESSION['sel_grid_sessions']) ) {
    $sel_sessions = $_SESSION['sel_grid_sessions'];
    // do not unset this yet, wait until end of submit or done
    //unset($_SESSION['sel_grid_sessions']);
  }
  
	
	if ( $exception_id > 0 ) {   // Edit existing record
		
		$exception = _get_exception($exception_id);
    
    //kpr($exception);
		
		if ( !$exception ) {
	    drupal_set_message( t('Oops!  Something seems to have gone wrong.  Exception record not found.'), 'error');
			drupal_goto('exceptions');
			return;
	  }
    
    
    $simulator_id = $exception->simulator_id;
    
    if ( $simulator_id ) {  // this could be zero, which means its for ALL sims
      // get the schedule
      $schedule = _get_schedule(0, $simulator_id);
      if ( (object) Null == $schedule) {
        drupal_set_message( t('Oops!  Something seems to have gone wrong.  Schedule not found.'));
        drupal_goto($goto_url);
        return;
      }
      $schedule_id = $schedule->schedule_id;
    }
    
	}
  else if ( $simulator_id && $schedule_id && strlen($sel_sessions) ) {
    
    // get the schedule
    $schedule = _get_schedule($schedule_id, $simulator_id);
    if ( !$schedule ) {
      drupal_set_message( t('Oops!  Something seems to have gone wrong.  Schedule not found.'));
			drupal_goto($goto_url);
			return;
    }
    //$schedule_id = $schedule->schedule_id;
    
    $session_arr = explode('&', $sel_sessions);
		
		$n = 0;
		for($i=0; $i<count($session_arr); $i++) {
			if ( strlen($session_arr[$i])) {
				$s = explode('~', $session_arr[$i]);
				$sel_session_list[$n++] = array(
					'session_id' => $s[0],
					'session_date' => $s[1],
          'portion' => $s[2],
          'begin_time' => $s[3],
          'end_time' => $s[4],
				);
			}
		}
    
    if ( 0 == count($sel_session_list)) { // shouldn't happen, front end checks this
			drupal_set_message( t('Oops!  Something seems to have gone wrong.  No sessions selected.'));
			drupal_goto($goto_url);
			return;
		}
		else if ( 1 == count($sel_session_list)) {
			
			$session_id = $sel_session_list[0]['session_id'];
			$session_date = $sel_session_list[0]['session_date'];
      $begin_time = _st_format_schedule_date($sel_session_list[0]['begin_time'], 'Hi', $timezone);
      $end_time = _st_format_schedule_date($sel_session_list[0]['end_time'], 'Hi', $timezone);
			
			$session = _get_session( $schedule_id, $session_id );//$session_id);
			if ( !$session ) {
				drupal_set_message( t('Oops!  Something seems to have gone wrong.  Session not found.'));
				drupal_goto($goto_url);
				return;
			}
			
			$session_date_display = str_replace('-', '/', $session_date);
			$form['session'] = array(
				'#markup' => 
					'<h3>' . $session_date_display . ', ' .
							$session->session_name . ': ' . $begin_time . ' - ' . $end_time . 
				 '</h3><hr />',
			);	
			
		}
		else {  // multiple selected sessions -  get the sessions for this schedule
			
			$sched_sessions = _get_sessions($schedule_id, 0, ($key_by_id = True)) ;
			if ( !count($sched_sessions) ) {
				drupal_set_message( t('Oops!  Something seems to have gone wrong.  Schedule sessions not found.'));
				drupal_goto($goto_url);
				return;
			}
			
			$form['session'] = array(
				'#markup' =>  '<h3>' . t('Multiple') . '</h3><hr />',
			);	
			
		}
  }
  /*  Allow this to be the case
  else {
		// error -- missing required data
		drupal_set_message( t('Error.  Unable to continue with Exceptions, missing required data.'));
		drupal_goto($goto_url); // go to main page -- not authorized for any other page
		return $form;
  }
  */
  
  // all page-specific javascript loaded in the after_build function
	$form['#after_build'][] = 'exception_form_after_build';
	
	
  $form['exception_desc'] = array(
		'#type' => 'textfield',
		'#title' => t('Exception Description'),
		'#default_value' => ( $exception_id ? $exception->exception_desc : '' ),
		'#maxlength' => 100,
		'#size' => 50,
		'#required' => True,
		'#attributes' => array('id' => 'sch-exception-desc'),
	);
  
	$form['simulator'] = array(
		'#type' => 'select',
		'#title' => t('Simulator'),
		'#options' => $simulator_list->getScheduledSimSelectList(SimulatorList::INCL_ALL),
		'#default_value' => ( ($exception_id > 0) ? $exception->simulator_id : $simulator_id ),
		'#required' => False,
		'#attributes' => array('class' => array('qms-select'),
													'id' => 'sch-simulator-list'),
		'#prefix' => '<div class="sch-exception-div-1">'
	);
  
  
  $form['customer'] = array(
		'#type' => 'select',
		'#title' => t('Link to Customer'),
		'#options' => $customer_list->getAll(),
		'#default_value' => ( ($exception_id > 0) ? $exception->customer_id : 0 ),
		'#required' => False,
		'#attributes' => array('class' => array('qms-select'),
													'id' => 'sch-customer-list'),
		'#suffix' => '</div>',
	);

	
	$form['maintenance'] = array(
		'#type' => 'checkbox',
		'#title' => t('Maintenance'),
		'#default_value' => ( !empty($exception->maintenance) ? $exception->maintenance : 0 ),
		'#attributes' => array('id' => 'sch-exception-maintenance'),
    '#prefix' => '<div class="sch-exception-div-2">',
	);	
  
  $form['event'] = array(
		'#type' => 'checkbox',
		'#title' => t('Event'),
		'#default_value' => ( !empty($exception->event) ? $exception->event : 0 ),
		'#attributes' => array('id' => 'sch-exception-event'),
	);	
	
	
	$form['allow_override'] = array(
		'#type' => 'checkbox',
		'#title' => t('Allow Override'),
		'#default_value' => ( !empty($exception->allow_override) ? $exception->allow_override : 0 ),
		'#attributes' => array('id' => 'sch-exception-override'),
		'#suffix' => '</div>',
	);	
	
//	if ( $exception_id && $exception->maintenance ) {
//		$form['allow_override']['#attributes']['disabled'] = 'disabled';
//	}
  
  
  
  $form['session_box'] = array(
		'#type' => 'fieldset',
		'#title' => (( $exception_id || ( 1 == count($sel_session_list))) ? 
									t('Session Date & Time (actual)') : t('Sessions')),
		'#attributes' => array('id' => 'sch-session-fieldset'),
		'#suffix' => '<br class="clearBoth">',
	);
  
	
  // allow for cases of an Edit
  // single add or
  // free-style (no session selected, direct date input
  
	if ( $exception_id || ( 1 == count($sel_session_list))  || 
       (!$exception_id && !count($sel_session_list) )) {
    
    $begin_cal_date = '';
    $end_cal_date = '';
    
    if ( $exception_id ) {
      $begin_cal_date = _st_format_schedule_date($exception->begin_timestamp, 'short', $timezone);
      $end_cal_date = _st_format_schedule_date($exception->end_timestamp, 'short', $timezone);
    }
    else if ( 1 == count($sel_session_list) ) {
      if ( 'P' == $sel_session_list[0]['portion']) {
        $begin_cal_date = _st_format_schedule_date($sel_session_list[0]['begin_time'], 'short', $timezone);
        $end_cal_date = _st_format_schedule_date($sel_session_list[0]['end_time'], 'short', $timezone);
      }
      else { // Full
        $sdate = str_replace('-', '/', $session_date);
        $idate = _st_format_schedule_timestamp($sdate, $timezone);
        $ibegin_date = _format_timestamp($idate, $session->begin_time, $timezone);
        $iend_date = _format_timestamp($idate, $session->end_time, $timezone);
        if ( $ibegin_date > $iend_date) { $iend_date = _add_day_ts($iend_date); }


        $begin_cal_date = _st_format_schedule_date($ibegin_date, 'short', $timezone);
        $end_cal_date = _st_format_schedule_date($iend_date, 'short', $timezone);
      }
    }
    else {
      $begin_cal_date = _st_format_schedule_date(REQUEST_TIME, 'short', $timezone);
      $end_cal_date = $begin_cal_date;
    }
    
    	
    $form['session_box']['begin_date'] = array(
      '#type' => 'date_popup',
      '#title' => t('Begins'),
      '#size' => 14,
      '#date_format' => 'm-d-Y H:i',						// displayed format
        //default value has to be in this format
      //'#default_value' => date('Y-m-d H:i'), 
      '#default_value' => $begin_cal_date,
      '#required' => True,   // if no date entered, returns NULL
      '#attributes' => array('class' => array('qms-date-picker')),
      '#prefix' => '<div id="sch-exception-dates">',
    );

    
    
    $form['session_box']['end_date'] = array(
      '#type' => 'date_popup',
      '#title' => t('Ends'),
      '#size' => 14,
      '#date_format' => 'm-d-Y H:i',						// displayed format
        //default value has to be in this format
      '#default_value' => $end_cal_date,
      '#attributes' => array('class' => array('qms-date-picker')),
      '#suffix' => '<div class="timezone">'. t('Timezone') . ':&nbsp;&nbsp;'. $timezone .'</div>'
      
    );
    
    $form['session_box']['all_day'] = array(
      '#type' => 'checkbox',
      '#title' => t('All Day'),
      '#default_value' => ( !empty($exception->all_day) ? $exception->all_day : 0 ),
      '#attributes' => array('id' => 'sch-exception-all-day'),
      '#suffix' => '</div>',
    );
    
    
    
    /*----------------- Repeat Options -------------------*/
    /* Adding one assignment, but not editing */
    if ( !$exception_id || ( 1 == count($sel_session_list)) ) {
      // allow for repeat option when one session selected for adding
      // do not allow for edit
      // do not allow for multi-select add
      
      $form['session_box']['repeat_box_begin'] = array(
        '#type' => 'markup',
        '#markup' => '<div id="sch-repeat-box">',
      );
      

      /*------------- Repeat-n Days ---------------*/
      $form['session_box']['repeat_days'] = array(
        '#type' => 'checkbox',
        '#title' => t('Repeat for'),
        '#default_value' => False, 
        '#attributes' => array('id' => 'sch-repeat-days-chk'),
        '#prefix' => '<div id="sch-repeat-days">'
      );	
      $form['session_box']['repeat_days_num'] = array(
        '#type' => 'textfield',
        //'#title' => t('Repeat'),
        '#maxlength' => 2,
        '#size' => 2,
        '#default_value' => False, 
        '#attributes' => array('id' => 'sch-repeat-days-num',
                               'class' => array('qms-numeric')),
      );	
      $form['session_box']['repeat_days_label'] = array(
          '#markup' => '<span class="sch-suffix-label">' . 
                        t('days total') . '</span></div>',
      );

      /*--------------- Repeat Until Date ----------*/
      $form['session_box']['repeat_until'] = array(
        '#type' => 'checkbox',
        '#title' => t('Repeat until (end on date)'),
        '#default_value' => False, 
        '#attributes' => array('id' => 'sch-repeat-until-chk'),
        '#prefix' => '<div id="sch-repeat-until">'
      );	
      $form['session_box']['repeat_until_date'] = array(
        '#type' => 'date_popup',
        //'#title' => t('Repeat'),
        '#size' => 11,
        '#date_format' => 'm-d-Y',						// displayed format
        '#default_value' => '', 
        '#attributes' => array('class' => array('qms-date-picker')),
        '#prefix' => '<div id="sch-repeat-until-date">',
        '#suffix' => '</div>',
      );
      
      $form['session_box']['repeat_box_end'] = array(
        '#type' => 'markup',
        '#markup' => '</div>',
      );
    }
  }
  else {
    // display a list of selected sessions 
		$table_header = array(
			array('data' => t('Date'), 'class' => array('sch-sess-tbl-date')),
			array('data' => t('Name'), 'class' => array('sch-sess-tbl-name')),
			array('data' => t('Begins'), 'class' => array('sch-sess-tbl-begins')),
			array('data' => t('Ends'), 'class' => array('sch-sess-tbl-ends')),
		);
		$table_rows = array();
		$i = 0;

		foreach ($sel_session_list as $s) {
			$sdate = str_replace('-', '/', $s['session_date']);
			
      $row_data = array();
      
      if ( 'P' == $s['portion']) {
        $row_data = array(
          $sdate,
          $sched_sessions[$s['session_id']]['session_name'],
          _st_format_system_date($s['begin_time'], 'Hi', $timezone),
          _st_format_system_date($s['end_time'], 'Hi', $timezone),
        );
      }
      else { // Full session
        $row_data = array(
          $sdate,
          $sched_sessions[$s['session_id']]['session_name'],
          $sched_sessions[$s['session_id']]['begin_time'],
          $sched_sessions[$s['session_id']]['end_time'],
        );
      }


			$table_rows[] = array( 'data' => $row_data );		
			$i++;
		}

		$content = theme( 'table', array(
			'header' => $table_header,
			'rows' => $table_rows,
			'empty' => t('None'),
			'attributes' => array('id' => 'sch-sess-display'),
		));		
		
		$form['session_box']['session_table'] = array(
			'#markup' => $content,
		);
		
		
		// hidden storage area 	
		$form['selected_sessions'] = array(
			'#type' => 'textarea',
			'#default_value' => $sel_sessions,
      '#resizable' => false, // this is required, but unnecessary
			'#attributes' => array('class' => array('qms-hidden'),
															'id' => 'sch-selected-sessions'),
			'#prefix' => '<div class="qms-hidden">',
			'#suffix' => '</div',
		);	
    
  }
  
  //------------- Note Text Editor -----------------
	$text_settings = array(
		'name' => 'note_text',
		'title' => t('Notes'),
		'text' =>	(isset($exception->note_text) ? $exception->note_text : ''),
		'required' => False,
	);
	 
	_st_add_text_editor($form, $text_settings);
	
		
  //------------- Hidden -------------------------
	$form['exception_id'] = array(
		'#type' => 'textfield',
		'#default_value' => ( $exception_id ? $exception_id : 0 ),
    '#attributes' => array('class' => array('qms-hidden')),
	);		
  
  
  $form['schedule_id'] = array(
    '#type' => 'textfield',
    '#default_value' => $schedule_id,
    '#attributes' => array('class' => array('qms-hidden')),
  );	
  $form['session_id'] = array(
    '#type' => 'textfield',
    '#default_value' => $session_id,
    '#attributes' => array('class' => array('qms-hidden')),
  );	
  
  $form['simulator_id'] = array(
    '#type' => 'textfield',
    '#default_value' => $simulator_id,
    '#attributes' => array('class' => array('qms-hidden')),
  );	
  
  // reformat date for datepicker widget
  $dt = new DateTime($begin_cal_date);
  $form['begin_date_original'] = array(
    '#type' => 'textfield',
    '#default_value' => $dt->format('m-d-Y H:i'),
    '#attributes' => array('class' => array('qms-hidden'),
                            'id' => 'sch-begin-date-original'),
  );	

  $dt = new DateTime($end_cal_date);
  $form['end_date_original'] = array(
    '#type' => 'textfield',
    '#default_value' => $dt->format('m-d-Y H:i'),
    '#attributes' => array('class' => array('qms-hidden'),
                            'id' => 'sch-end-date-original'),
  );	
		
		
	
	$form['actions'] = array('#type' => 'actions');
	$form['actions']['submit'] = array(
		'#type' => 'submit',
		'#value' => t('Submit'),
    '#name' => 'submit',
	);
	if ( $exception_id ) {
		
    $form['actions']['delete'] = array(
      '#type' => 'button',
      '#value' => t('Delete'),
      '#attributes' => array('class' => array('qms-btn-delete', 'qms-btn-extra'),
                             'onclick' => 'window.location="' . 
                                  url('exception/delete/' . $exception_id) . 
                                  '"; return false;'),
    );
	}
  
  $form['actions']['done'] = array(
		'#type' => 'submit',
		'#value' => t('Done'),
    '#name' => 'done',
		'#attributes' => array('class' => array('qms-btn-done', 'qms-btn-extra')),
                           //'onclick' => 'window.location="' . 
                           //     url($goto_url) . '"; return false;'),
	);
  
	
//	$form['actions']['cancel'] = array(
//		'#markup' =>  l(t('Cancel'), $goto_url),
//	);
	

	return $form;
}

/*------ AFTER BUILD FUNCTION --------
*	 functions called, as it implies, after the form is built
*  this is necessary when custom javascript and/or css files are added as needed
*  at the page level for forms that go through validation with hook_form_validate()
*  Otherwise, if a form fails validation, the page/form is reloaded for the user to correct
*  but the accompanying javascript & css is not reloaded with it. 
*  when $form['#after_build][] is used, the external scripts will be reloaded properly.
*/

/* 
 * exception_form_after_build()
 *
 */
function exception_form_after_build($form, &$form_state)
{
	drupal_add_library('system','ui.datepicker');
  $sabreTools = drupal_get_path('module', 'sabreTools');
	drupal_add_js($sabreTools . '/js/sabreTools.lib.js');
  drupal_add_js($sabreTools . '/js/sabreTools.ckeditor.js');
	drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/sabreScheduler.exception.addedit.js');
  
  global $base_url;
	drupal_add_js( $base_url . QMS_CKEDITOR_PATH_CONFIG);
	// need to specify preprocess == false otherwise 'Aggregate Javascript' setting in drupal 
	// causes ckeditor not to load properly
	// adding scope to footer speeds up the page load
	drupal_add_js( $base_url . QMS_CKEDITOR_LIB, array('type' => 'file', 'scope' => 'footer', 'preprocess' => FALSE));
	
	return $form;
}



/*
 *	exception_form_validate()  
 *
 *  overloads:  hook_form_validate()
 *
 *	Validates exception_form after it is submitted
 */

function exception_form_validate($form, $form_state) {
  
  $button_clicked = $form_state['triggering_element']['#name'];
  
  if ( $form['actions']['submit']['#name'] != $button_clicked ) {
    form_clear_error();
    $destination = drupal_get_destination();
    drupal_goto($destination['destination']);
    return $form;
  }
	
	if ( $form_state['values']['exception_desc'] == '' ) {
		form_set_error('exception_desc', t('Exception Description is required.'));
	}

	  
  //--- if only dealing with one session adding/editing, do validation ---- 
	// otherwise validate during the multi add process
	// selected_sessions only set if this is an add with a list of selected sessions
  $schedule_id = (int)$form_state['values']['schedule_id'];
	if (!empty($schedule_id) && !isset( $form_state['values']['selected_sessions'] ) )  {
		
		$begin_date = $form_state['values']['begin_date'];
		$end_date = $form_state['values']['end_date'];
		
		if (  !isset($begin_date) || ($begin_date == '') || 
					 !isset($end_date) || ($end_date == '')   )  {
						
			if ( !isset($begin_date) || ($begin_date == '') ) {
				form_set_error('begin_date', t('Unable to save Exception.  Exception Begins is a required field'));
			}
			if ( !isset($end_date) || ($end_date == '') ) {
				form_set_error('end_date', t('Unable to save Exception.  Exception Ends is a required field'));
			}
			return;
		}
    $schedule_id = (int)$form_state['values']['schedule_id'];
    $schedule = _get_schedule($schedule_id);
    if (!$schedule) {
      form_set_error('begin_date', 
				t('Error.  Schedule not found'));
				return;
    }
    
		$begin_time_ts = _st_format_schedule_timestamp($begin_date, $schedule->timezone);
		$end_time_ts = _st_format_schedule_timestamp($end_date, $schedule->timezone);
		
		if ( $begin_time_ts > $end_time_ts ) {
			form_set_error('begin_date', 
				t('Invalid Dates.  Exception Begins cannot be set to a later date than Exception Ends'));
				return;
		}
    
    
    // validate repeat settings
    // actual repeated sessions do not get validated for conflicts until they are created
    if ( isset($form_state['values']['repeat_days'])) {
      if ( 1 == (int)$form_state['values']['repeat_days'] ) {
        $day_count = (int)$form_state['values']['repeat_days_num'];
        if ( $day_count < 2 ) {
          form_set_error('repeat_days_num', 
                t('Value entered for "Repeat for ___ days total" must be 2 or greater.'));
        }
      }
      if ( 1 == (int)$form_state['values']['repeat_until']) {
        $end_on_date = $form_state['values']['repeat_until_date'];
        if ( '' == $end_on_date ) {
          form_set_error('repeat_until', 
                  t('"Repeat until (end on date)" requires a valid date value.'));
        }
        else {
          $eod_ts = _st_format_schedule_timestamp($end_on_date, $timezone);
          if ( $eod_ts <= $end_time_ts ) {
            form_set_error('repeat_until', 
                t('"Repeat until (end on date)" must be a later date than the specified end date and time'));
          }
        }
      }
    }

		
		//-- check if date entered conflicts with another assignment
		$exception_id = (int)$form_state['values']['exception_id'];
    $simulator_id = (int)$form_state['values']['simulator_id'];
		$schedule_id = (int)$form_state['values']['schedule_id'];
		$session_id = (int)$form_state['values']['session_id'];
		
		$sessValidator = new SessionValidator(0, $exception_id, $schedule_id, 
																					$session_id, $begin_time_ts, $simulator_id);
		
		
		// assignment can be extended in each direction, exceeding current session
		// when this happens, we are essentially creating new assignments for new overlapping sessions
		// provided that they are not already booked
		
		$result = $sessValidator->checkForConflict('E', $begin_time_ts, $end_time_ts);

		if ( ($result != SessionValidator::SV_NOCONFLICT) ) {
			$conflict_text = '';
      
      if ($result == SessionValidator::SV_SCHEDULE_RANGE) {
        list($schedule_begin_date_f, $schedule_end_date_f) = $sessValidator->getScheduleRange();
        $conflict_text .= '<li>' . t('Exceeds schedule date range') . ': '. 
                                  $schedule_begin_date_f . ' - ' . $schedule_end_date_f . '</li>';
                                   
      }
      
			$assigns = $sessValidator->getAssignmentConflicts();
			foreach($assigns as $a) {
				$conflict_text .= '<li>' . $a->session_name . ':  ' . 
                                   $a->begin_timestamp_f . ' - ' . $a->end_timestamp_f . '</li>';
			}
      
      $excepts = $sessValidator->getExceptionConflicts();
      foreach($excepts as $ex) {
        $conflict_text .= '<li>' . $ex->exception_desc . ':  ' . 
                                   $ex->begin_timestamp_f . ' - ' . $ex->end_timestamp_f . '</li>';
      }
			
			form_set_error('begin_date', 
				t('This exception date & time conflicts with existing assignments or exceptions.') . 
					(strlen($conflict_text) ? '<ul>' . $conflict_text . '</ul>' : '' )
				);
		}
	
	}
		
}

/*
 *	exception_form_submit()  
 *
 *  overloads:  hook_form_submit()
 *
 *	Saves form data upon successful submit
 */

function exception_form_submit($form, $form_state) {
  
  
  // determine where we go if we have an error here.
	$destination = drupal_get_destination();
	$goto_url = $destination['destination'];

	if( !strlen($goto_url) || ($goto_url == current_path()) ) {
		$goto_url = 'exceptions';  // last resort option
	}
  
  //---------------- Button Handlers --------------------
  
  $button_clicked = $form_state['triggering_element']['#name'];
  
  if ( isset($form['actions']['done']['#name']) && 
            ($form['actions']['done']['#name'] == $button_clicked) ){
    
    unset($_SESSION['sel_grid_sessions']);
    drupal_goto($goto_url);
    
  }
  else if ( $form['actions']['submit']['#name'] != $button_clicked ) {
    // if not done, then make sure its a submit click, otherwise, return
    return;
  }
  
  //---------------- SUBMIT:  Process Form --------------------
	
	
	global $user;
  $sess_id_map = array();
  $sessValidator = new SessionValidator();
  $selected_sessions = array();
  $sel_session_list = array();
  $sched_sessions = array();
  $repeat_count = 1;
  $interval_days = 0;
  $conflicts_s = array();
  $conflicts_a = array();
  $conflicts_e = array();
  $bSkipped = False;
 
  
  
  $schedule_id = (int)$form_state['values']['schedule_id'];
  
 
  if ( $schedule_id ) {
    // It's possible this == 0
    // in an edit situation that's ok.
    // in a single_add situation that ok too.
    
    // we expect this to be > 0 in a multi-session add
    $schedule = _get_schedule($schedule_id);
    
    
    // get the sessions for the current schedule
    $sched_sessions = _get_sessions($schedule_id, 0, ($key_by_id = True)) ;
    if ( !count($sched_sessions) ) {
      drupal_set_message( t('Oops!  Something seems to have gone wrong.  Schedule sessions not found.'));
      drupal_goto($goto_url);
      return;
    }

    // since the sched_sessions array is keyed by session_id for access later in this function
    // we also need an index->session_id map for use earlier 
    $m = 0;  
    foreach($sched_sessions as $sess) {
      $sess_id_map[$m++] = $sess['schedule_session_id'];
    }
  } else {
    // no schedule_id, adding Exception from Manage -> Exceptions menu option
    
    
  }
  
  //kpr($form_state['values']);
	$exception_id = (int)$form_state['values']['exception_id'];
	$exception = new stdClass();
	$exception->simulator_id = (int)$form_state['values']['simulator'];
	$exception->exception_desc = $form_state['values']['exception_desc'];
  $exception->all_day = (int)$form_state['values']['all_day'];
	$exception->maintenance = (int)$form_state['values']['maintenance'];
  $exception->event = (int)$form_state['values']['event'];
	$exception->allow_override = (int)$form_state['values']['allow_override'];
  $exception->customer_id = (int)$form_state['values']['customer'];
  $exception->note_text = _st_clean_ckeditor_text($form_state['values']['note_text_editor_value']);
  

  $selected_sessions = (isset($form_state['values']['selected_sessions']) ? 
                                $form_state['values']['selected_sessions'] : array() );
  
  if ( isset($form_state['values']['repeat_days']) ) {
    if ( 1 == (int)$form_state['values']['repeat_days'] ) {
      $repeat_days_num = (int)$form_state['values']['repeat_days_num'];
      if ( $repeat_days_num > 1) {
        $repeat_count = $repeat_days_num;
      }
    }
    else if ( 1 == (int)$form_state['values']['repeat_until'] ) {
      // add seconds to make it ISO format
      $begin_date = $form_state['values']['begin_date'] . ':00';
      $end_date = $form_state['values']['end_date'] . ':00';
      $end_on_date = $form_state['values']['repeat_until_date'];

      $repeat_days_num = _calc_repeat_days($begin_date, $end_date, $end_on_date, $schedule->timezone);
      if ( $repeat_days_num > 1 )  {
        $repeat_count = $repeat_days_num;
      }
      
      
    }
  }
  
 
 
  // MULTI-SESSION ADD
  
  //if ( isset($form_state['values']['selected_sessions']) && !$exception_id ) {
  // this is a multi-add situation, individual session fields not set, 
  // we have a list of session ids and dates instead, parse the array


  if ( isset($form_state['values']['selected_sessions']) ) {
		$session_arr = explode('&', $selected_sessions);
		
		$n = 0;
		for($i=0; $i<count($session_arr); $i++) {
			if ( strlen($session_arr[$i])) {
				$s = explode('~', $session_arr[$i]);
				$sel_session_list[$n++] = array(
					'session_id' => $s[0],
					'session_date' => $s[1],
          'portion' => $s[2],
          'begin_time' => $s[3],
          'end_time' => $s[4],
				);
			}
		}
  }
  else {
    $begin_date = trim($form_state['values']['begin_date']);
    $end_date = trim($form_state['values']['end_date']);
    $exception->begin_timestamp = _st_format_schedule_timestamp($begin_date, $schedule->timezone);
    $exception->end_timestamp = _st_format_schedule_timestamp($end_date, $schedule->timezone);
    
    //_calc_exception_calendar_dates($exception);
    
    $base_interval = $exception->end_timestamp - $exception->begin_timestamp;
    $interval_days = ceil($base_interval / SCH_ONE_TS_DAY); //always round up
    if ( $interval_days < 1) { $interval_days = 1; }
  }
    
  $sel_count = count($sel_session_list);

  $max_count = ( $sel_count ? $sel_count : 1);

  $key = null;


  //--------------------- REPEAT OPTION ------------------------
  for ($r=0; $r < $repeat_count; $r++) {

    // LOOP through selected sessions
    // create a new exception for each selected session

    //foreach ( $sel_session_list as $s ) {
    //
    //----------- HANDLE MULTI-ADD to list of sessions -----------
    // the first assignment's session has already been set up above
    for ( $n = 0; $n < $max_count; $n++ ) {

      $session_id = 0;

      if ( $sel_count ) {
        
        if ( 'P' == $sel_session_list[$n]['portion'] ) {
          // PARTIAL session use submitted times
          $begin_ts = $sel_session_list[$n]['begin_time'];  // this is a full timestamp
          $end_ts = $sel_session_list[$n]['end_time'];      // this is a full timestamp

          $exception->begin_timestamp = $begin_ts;
          $exception->end_timestamp = $end_ts;
          
          //_calc_exception_calendar_dates($exception);
          // use the begin timestamp for the session date in case the session wraps
          // in the next day and the portion used is for that next day
          $session_date = $exception->begin_timestamp;
          
        }
        else {  // FULL session - use standard times
          $session_id = $sel_session_list[$n]['session_id'];
          $sdate = str_replace('-', '/', $sel_session_list[$n]['session_date']);
          $session_date = _st_format_schedule_timestamp($sdate, $schedule->timezone);
          _calc_exception_timestamps($exception, $session_date, 0,
                                          $sched_sessions[$session_id]['begin_time'], 
                                          $sched_sessions[$session_id]['end_time']);
        }
      }
      else {
        if ( !$r ) {
          // get the session id, if available, might be zero
          $session_id = (int) $form_state['values']['session_id'];
          $session_date = $exception->begin_timestamp;
        }
        else {
          //---- increment days for repeat interval ---------
          // SCH_ONE_TS_DAY == seconds per day
          // don't increment until after the base values are processed
          // 
          // --- can't due this because of a DST glitch
//          $exception->begin_timestamp += (SCH_ONE_TS_DAY * $interval_days);
//          $exception->end_timestamp += (SCH_ONE_TS_DAY * $interval_days);
//          $exception->begin_calendar_date += (SCH_ONE_TS_DAY * $interval_days);
//          $exception->end_calendar_date += (SCH_ONE_TS_DAY * $interval_days);
          
          $bd = _st_format_schedule_date($exception->begin_timestamp, 'Y-m-d H:i:s', $schedule->timezone);
          $ed = _st_format_schedule_date($exception->end_timestamp, 'Y-m-d H:i:s', $schedule->timezone);
//          $bcd = _st_format_schedule_date($exception->begin_calendar_date, 'Y-m-d H:i:s', $schedule->timezone);
//          $ecd = _st_format_schedule_date($exception->end_calendar_date, 'Y-m-d H:i:s', $schedule->timezone);
          
          $add_days = '+ ' . $interval_days . ' days';
          
          
          $exception->begin_timestamp = _st_format_schedule_timestamp($bd, 
                                                                   $schedule->timezone, 
                                                                   $add_days);
          $exception->end_timestamp = _st_format_schedule_timestamp($ed, 
                                                                   $schedule->timezone, 
                                                                   $add_days);
//          $exception->begin_calendar_date = _st_format_schedule_timestamp($bcd, 
//                                                                    $schedule->timezone, 
//                                                                    $add_days);
//          $exception->end_calendar_date = _st_format_schedule_timestamp($ecd, 
//                                                                    $schedule->timezone, 
//                                                                    $add_days);
          
          $session_date = $exception->begin_timestamp;
        }
      }


      if ( !$exception_id ) {
        $exception->created_date = REQUEST_TIME;
        $exception->created_by_user = $user->uid;
        $key = array();
      }
      else {
        $exception->exception_id = $exception_id;
        $exception->updated_date = REQUEST_TIME;
        $exception->updated_by_user = $user->uid;
        $key = 'exception_id';
      }

      
      $sessValidator->init(0, $exception_id, $schedule_id,  $session_id, 
                              $session_date, $exception->simulator_id);
      $result = $sessValidator->checkForConflict('E', $exception->begin_timestamp, $exception->end_timestamp);

      if ( ($result != SessionValidator::SV_NOCONFLICT) || 
            ( False == drupal_write_record('sch_exceptions', $exception, $key) ) ) {
        if ($result == SessionValidator::SV_SCHEDULE_RANGE) {
          $conflicts_s += $sessValidator->getScheduleRange();
        }
        $conflicts_a += $sessValidator->getAssignmentConflicts();
        $conflicts_e += $sessValidator->getExceptionConflicts();
        $bSkipped = True;          
      }

      // precautionary
      if ( isset($exception->exception_id) ) { unset($exception->exception_id); }

    } // end foreach
  } // end for loop -- $repeat_count

  if ( $bSkipped ) {
    drupal_set_message(t('Unable to complete request saving exception(s) due to conflicts with other schedule items'));
    
    $conflict_text = '';
    
    if (!empty($conflicts_s)) {
        $conflict_text .= '<li>' . t('Exceeds schedule date range') . ': '. 
                                  $conflicts_s[0] . ' - ' . $conflicts_s[1] . '</li>';
    }

    foreach($conflicts_a as $a) {
      $conflict_text .= '<li>' . $a->session_name . ':  ' . 
                                 $a->begin_timestamp_f . ' - ' . $a->end_timestamp_f . '</li>';
    }

    foreach($conflicts_e as $ex) {
      $conflict_text .= '<li>' . $ex->exception_desc . ':  ' . 
                                 $ex->begin_timestamp_f . ' - ' . $ex->end_timestamp_f . '</li>';
    }

    drupal_set_message('<p>' . 
      t('Unable to complete request saving exception(s) due to conflicts with other schedule items') . 
        '</p>' .
        (strlen($conflict_text) ? '<ul>' . $conflict_text . '</ul>' : '' )
      );
  }
  else {
    if ( $sel_count > 1 )
      drupal_set_message(t('Exceptions have been saved.'));
    else {
      drupal_set_message(t('Exception has been saved.'));
    }
  }


	// Reload the cache 

	// determine cache name
	$search = Null;
	$key_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_KEY); 
	
	$key_cache = cache_get($key_cache_name);
	if ( isset($key_cache->data) && ($key_cache->data <> '') ) {
		$search = $key_cache->data;
		
		//refresh the cached search results in the event of any changes may be displayed
		_get_exceptions_search_list($search);
	}
	
  unset($_SESSION['sel_grid_sessions']);
	
	drupal_goto($goto_url);
  
} // END -- exception_form_submit()

/*
 *	exception_delete_confirm()  
 *
 *	Validate and Ask for confirmation before deleting record
 */

function exception_delete_confirm($form, $form_state, $exception_id) {
  
  $timezone = $_ENV['schedule_timezone'];
	
	if ( user_access('view manage menu') == FALSE ) {
		drupal_set_message( t('Unauthorized.  Permission is required.'), 'status');
		drupal_goto(''); // go to main page -- not authorized for any other page
		return $form;	
  }

	if ( $exception_id == 0 ) {
		drupal_set_message( t('Invalid parameter provided. exception_id = 0.'), 'error');
		drupal_goto('exceptions'); 
		return;
	}
	
	$exception = _get_exception($exception_id);
	
	if ( $exception == (object) Null ) {
    drupal_set_message( t('Oops!  Something seems to have gone wrong.  Exception record not found.'), 'error');
		drupal_goto('exceptions');
		return;
  }

	$descrip = $exception->exception_desc . '<br />(' . 
							_st_format_schedule_date($exception->begin_timestamp, 'short', $timezone) . ' to ' . 
							( ($exception->end_timestamp) ? 
                _st_format_schedule_date($exception->end_timestamp, 'short', $timezone) : '...').')';
	
	$form['exception_id'] = array(
		'#type' => 'value',
		'#value' => $exception_id,
	);
	
	$form['exception_descrip'] = array(
		'#type' => 'value',
		'#value' => $descrip,
	);
  
  $title = t('Delete Exception') . '?';
  $question = t('Are you sure you want to delete?') . 
              '<h2>' . $descrip . '</h2><br />' . 
              t('This action cannot be undone.');
  $goto_if_canceled = 'exceptions';
  $yes_btn = 	t('Delete');
  $no_btn = t('Cancel');
  
  // force this here to avoid a problem with routing if cancelled
  $_GET['destination'] = 'exceptions';
  
  return confirm_form($form, $title,	$goto_if_canceled, $question, $yes_btn, $no_btn);	
}

/*
 *	exception_delete_confirm_submit()  
 *
 *	Submit after confirmation, delete the specified record
 */

function exception_delete_confirm_submit($form, $form_state) {
	
	try {
		if ( $form_state['values']['confirm']) {
			
			$exception_id = $form_state['values']['exception_id'];
			$descrip = $form_state['values']['exception_descrip'];
	
			// delete from database
			$num_rows = db_delete('sch_exceptions')
									->condition('exception_id', $exception_id, '=')
									->execute();
	
			$msg = '';					
			if ( $num_rows == 1 ) {
				$msg = 'Exception record for ' . $descrip . ' has been deleted.';
			}
			else {
				$msg = 'Oops!  Unable to delete record for ' . $descrip . '.';
			}
			drupal_set_message(t($msg));
	
			// Reload the cache 

			// determine cache name
			$search = (object) Null;
			$key_cache_name = _get_scheduler_cache_name(SCH_EXCEPTIONS_SEARCH_KEY); 

			$key_cache = cache_get($key_cache_name);
			if ( isset($key_cache->data) && ($key_cache->data <> '') ) {
				$search = $key_cache->data;

				//refresh the cached search results in the event of any changes may be displayed
				_get_exceptions_search_list($search);
			}
		}
		drupal_goto('exceptions');
	}
	catch (Exception $e) {
		watchdog(SCH_SCHEDULER, 'exception_delete_confirm_submit() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
	}
}


/*
 *		_get_exception()
 *
 *		retrieves the exception record
 */

function _get_exception($exception_id) {
	try {
		
		$exception = (object) Null;
		
		if ( $exception_id > 0 ) {   // Edit existing record

			$query = db_select('sch_exceptions', 'e');
			$query->leftJoin('sch_simulators', 's', 'e.simulator_id = s.simulator_id');
			$query->condition('e.exception_id', $exception_id);
			$query->fields('e', array('exception_id', 'simulator_id', 'exception_desc', 
                                'begin_timestamp', 'end_timestamp', 'all_day', 
                                'allow_override', 'maintenance', 'event', 
                                'customer_id', 'note_text', 
																'created_date', 'created_by_user', 
                                'updated_date', 'updated_by_user'));
			$query->fields('s', array('sim_name', 'active', 'device_id_internal'));
			$results = $query->execute();

			if ( $results->rowCount() ) {
		    $exception = $results->fetchObject();
		  }
		}
		return $exception;		
	}
	catch (Exception $e) {
		watchdog(SCH_SCHEDULER, '_get_exception() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
	} 
	
}


/*
 *  _get_exceptions()
 */
function _get_exceptions($begin_date, $end_date, $timezone = '', $simulator_id = 0, $raw_data=False) {
  
  $exceptions = array();
  $timezone = $timezone;
  
  $query = db_select('sch_calendar', 'c');
  if ($simulator_id) {
    $query->leftJoin('sch_exceptions', 'e', 
            '( DATE(FROM_UNIXTIME(e.begin_timestamp)) <= DATE(FROM_UNIXTIME(c.calendar_date)) ) AND 
             ( DATE(FROM_UNIXTIME(c.calendar_date)) <= DATE(FROM_UNIXTIME(e.end_timestamp))  )
            AND (e.simulator_id = :sid OR e.simulator_id = 0)', array(':sid' => $simulator_id));
  } else {
    $query->leftJoin('sch_exceptions', 'e', 
            'DATE(FROM_UNIXTIME(e.begin_timestamp)) <= DATE(FROM_UNIXTIME(c.calendar_date)) AND 
             DATE(FROM_UNIXTIME(c.calendar_date)) <= DATE(FROM_UNIXTIME(e.end_timestamp))');
  }
  $query->leftJoin('sch_customers', 'cu', 'e.customer_id = cu.customer_id');

//  $query->condition('calendar_date', $grid_begins, '>=');
//  $query->condition('calendar_date', $grid_ends, '<=');
  
  $query->condition('e.exception_id', 0, '>');
  
  $query->where("(
                   ( DATE(FROM_UNIXTIME($begin_date)) <= DATE(FROM_UNIXTIME(c.calendar_date)) ) AND 
                   ( DATE(FROM_UNIXTIME(c.calendar_date)) <= DATE(FROM_UNIXTIME($end_date)) )
                )");

  $query->fields('c', array('calendar_date'))
          ->fields('e', array('exception_id', 'exception_desc', 'maintenance', 'allow_override', 
                              'event', 'simulator_id', 'begin_timestamp', 'end_timestamp',
                              'customer_id', 'note_text'))
          ->fields('cu', array('customer_code', 'customer'));
  $query->orderBy('e.simulator_id', 'ASC');
  $query->orderBy('c.calendar_date', 'ASC');
  $query->orderBy('e.begin_timestamp', 'ASC');

  $results_except = $query->execute();
  
  if ($raw_data) {
    return $results_except;
  }
  
  $dt = new DateTime();

  if ( $results_except->rowCount() ) {
    //$results_exceptions = $results_except->fetchAll();
    foreach( $results_except as $ex ) {
      $dt->setTimestamp($ex->calendar_date);
      $date = $dt->format('j');
      $ex->begin_timestamp_f = _st_format_schedule_date($ex->begin_timestamp, 'Y-m-d H:i', $timezone);
      $ex->end_timestamp_f = _st_format_schedule_date($ex->end_timestamp, 'Y-m-d H:i', $timezone);
      $exceptions[$date][$ex->exception_id] = $ex;
    }
  }
  return $exceptions;
}



/*
 *	_delete_selected_exceptions($exception_ids) 
 *
 *	Accepts a comma-separated list of exception ids
 *  deletes the exceptions in the list
 */
function _delete_selected_exceptions($exception_ids) {
	try {
		
		$num_rows = 0;
		
		if ( strlen($exception_ids) ) {
      
      //$id_list = explode(',', $exception_ids);
      $id_list = json_decode('[' . $exception_ids . ']', true);
			
      if ( count($id_list) ) {
        $num_rows = db_delete('sch_exceptions')
                    ->condition('exception_id', $id_list, 'IN')
                    ->execute();
      }
    }
		return $num_rows;			
	}
	catch(Exception $e) {
		watchdog(SCH_SCHEDULER, '_delete_selected_exceptions() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
	}
}


/*
 *	_paste_one_exception_to_many_sessions() 
 *
 *   single exception copied, previously saved to clipboard
 *   now paste it to multiple selected session locations
 *	 the session_list array contains the multi sessions being pasted into
 */
function _paste_one_exception_to_many_sessions($schedule_id, $sel_session_list, $clip_exception) {
  
  try {
	
    if ( !$schedule_id || !count($sel_session_list) || ((object) Null == $clip_exception) ) { return False; }
    
    $timezone = _get_schedule_timezone($schedule_id);

    // get list of all sessions for this schedule
    $schedule_sessions = _get_sessions($schedule_id, 0, ($key_by_id = True));
    if ( !count($schedule_sessions) ) { return False; }

    global $user;
    $sessValidator = new SessionValidator();


    // create the exceptions for each selected session
    foreach ( $sel_session_list as $s ) {

      $session_id = $s['session_id'];
      $sdate = str_replace('-', '/', $s['session_date']);
      $session_date = _st_format_schedule_timestamp($sdate, $timezone);

      $exception = new stdClass();
      $exception->simulator_id = $clip_exception->simulator_id;
      $exception->exception_desc = $clip_exception->exception_desc;
      $exception->allow_override = $clip_exception->allow_override;
      $exception->maintenance = $clip_exception->maintenance;
      $exception->event = $clip_exception->event;
      $exception->customer_id = $clip_exception->customer_id;
      $exception->note_text = $clip_exception->note_text;
      $exception->created_date = REQUEST_TIME;
      $exception->created_by_user = $user->uid;
      
      if ( 'P' == $s['portion']) {
        $exception->begin_timestamp = $s['begin_time'];
        $exception->end_timestamp = $s['end_time'];
        
        //_calc_exception_calendar_dates($exception);
      }
      else {  // FULL session - use std session time
        _calc_exception_timestamps($exception, $session_date, 0,
                                        $schedule_sessions[$session_id]['begin_time'], 
                                        $schedule_sessions[$session_id]['end_time']);
      }

     
      $sessValidator->init(0, $clip_exception->exception_id, $schedule_id,  $session_id, $session_date);
      $result = $sessValidator->checkForConflict('E', $exception->begin_timestamp, $exception->end_timestamp);

      if ( ($result != SessionValidator::SV_NOCONFLICT ) ||
           ( False == drupal_write_record('sch_exceptions', $exception)) ) {
          $bSkipped = True;
      }
      
      // precautionary
      if ( isset($exception->exception_id) ) { 
        unset($exception->exception_id); 
      }
    }

	}
	catch (Exception $e) {
		watchdog(SCH_SCHEDULER, '_paste_one_exception_to_many_sessions() ' . $e->getMessage(), 
             array(), WATCHDOG_ERROR);
	} 
}


/*
 *	_update_exception_location($assignment_id, $schedule_id, $session_id, $session_date) 
 *
 */
function _update_exception_location($exception_id = 0, $new_schedule_id = 0, 
                                    $new_sess_id = 0, $new_date = 0, 
                                    $portion = 'F', $begin_ts = 0, $end_ts = 0) {
	try {
		
		if ( !$exception_id || !$new_schedule_id || !$new_sess_id || !$new_date ) { return False; }
		
		// update the session assignment with the new info
		global $user;
		$exception = new stdClass();
		
		
    
    $exception->exception_id = $exception_id;
    $exception->updated_date = REQUEST_TIME;
    $exception->updated_by_user = $user->uid;
    
    
    
    if ( 'P' == $portion ) {
      $exception->begin_timestamp = $begin_ts;
      $exception->end_timestamp = $end_ts;
      //_calc_exception_calendar_dates($exception);
    }
    else {  // FULL session - use std session time
      
      $new_sess = (object) Null;
		
    
      /*----- Get the new session information -------*/
      $query_s = db_select('sch_schedule_sessions', 's');
      $query_s->condition('schedule_id', $new_schedule_id);
      $query_s->condition('schedule_session_id', $new_sess_id);
      $query_s->fields('s', array('begin_time', 'end_time'));
      $result_s = $query_s->execute();

      if ( !$result_s->rowCount() ) { return False; } //Error 
      $new_sess = $result_s->fetchObject();
      
      
      _calc_exception_timestamps($exception, $new_date, 0,
                                    $new_sess->begin_time, 
                                    $new_sess->end_time);
    }

		
    drupal_write_record('sch_exceptions', $exception, 'exception_id');
		
		return True;
	}
	catch (Exception $e) {
		watchdog(SCH_SCHEDULER, '_update_exception_location() ' . $e->getMessage(), array(), WATCHDOG_ERROR);
	} 
}


/*
 * 
 *    _calc_exception_timestamps()
 */
function _calc_exception_timestamps(&$exception, $begin_ts, $end_ts = 0, $begin_time = '0000', $end_time = '0000') {
    if ( !$end_ts ) {  $end_ts = $begin_ts;  }
    
    $begin_timestamp = _format_timestamp($begin_ts, $begin_time);
    $end_timestamp = _format_timestamp($end_ts, $end_time);
    
//    // no need to pass in time here, defaults to '0000'
//    $begin_cal_date = _format_timestamp($begin_ts);
//    $end_cal_date = _format_timestamp($end_ts);
//    
//    // if begin time > end time, bump the end time calendar date by 1 day
//    if ( ($begin_ts == $end_ts) && ((int)$begin_time > (int)$end_time) ) {
//      //$end_cal_date += SCH_ONE_TS_DAY;  // SCH_ONE_TS_DAY seconds in a day
//      //$end_timestamp += SCH_ONE_TS_DAY;  // SCH_ONE_TS_DAY seconds in a day
//      $end_cal_date = _add_day_ts($end_cal_date);
//      $end_timestamp = _add_day_ts($end_timestamp);
//    }
    
    $exception->begin_timestamp = $begin_timestamp;
    $exception->end_timestamp = $end_timestamp;
//    $exception->begin_calendar_date = $begin_cal_date;
//    $exception->end_calendar_date = $end_cal_date;
}


/*
 * 
 *  _calc_exception_calendar_dates()
 * 
 *  IN:   pass in an exception record with the begin_timestamp and end_dateime fields populated
 *  OUT:  generates the begin_calendar_date and end_calendar_date fields and stores them in the record  
 */
/*
function _calc_exception_calendar_dates(&$exception) {
  if ( !$exception->begin_timestamp || !$exception->end_timestamp) { return False; }
  
  $tz = _get_schedule_timezone();
  
  $bdt = new DateTime('now', new DateTimeZone($tz));
  $bdt->setTimestamp($exception->begin_timestamp);
  $bdt->setTime(0,0,0);
  //$begin_cal_date = $dt->format('Y-m-d') . ' 00:00:00';
  
  $edt = new DateTime('now', new DateTimeZone($tz));
  $edt->setTimestamp($exception->end_timestamp);
  $edt->setTime(0,0,0);
  //$end_cal_date = $dt->format('Y-m-d') . ' 00:00:00';
  
  $exception->begin_calendar_date = $bdt->getTimestamp();
  $exception->end_calendar_date =  $edt->getTimestamp();
  return True;
}
 * 
 */




