<?php

/**
 * 		
 * 		sabreScheduler Module for plugin to Drupal 7 Framework
 *
 * 		Schedule Grid function handling
 *    
 */
require_once( drupal_get_path('module', 'sabreTools') . '/sabreTools.class.yearlist.inc');
require_once('sabreScheduler.sessions.inc');
require_once('sabreScheduler.schedules.inc');
require_once('sabreScheduler.simulators.inc');
require_once('sabreScheduler.assignments.inc');
require_once('sabreScheduler.exceptions.inc');
require_once('sabreScheduler.sessions.inc');


// defines
define('SCH_CLASS_ASSIGNMENT', 'sch-session-assignment');
define('SCH_CLASS_OPEN', 'sch-session-open');
define('SCH_CLASS_EXCEPTION', 'sch-exception');
define('SCH_CLASS_EXCEPTION_BLOCK', 'sch-exception-block');
define('SCH_CLASS_DROPPABLE', 'droppable');
define('SCH_CLASS_SELECTABLE', 'selectable');
define('SCH_CLASS_SELECT_ME', 'select-me');
define('SCH_CLASS_SELECT_DATE', 'sch-select-date');
define('SCH_CLASS_SESSION_DATE', 'sch-session-date');
define('SCH_CLASS_UNAVAILABLE', 'sch-unavailable');
define('SCH_CLASS_MOTION', 'sch-full-motion');
define('SCH_CLASS_FIXED', 'sch-fixed-base');



/*
 * 	schedule_grid_display()
 *
 */

function schedule_grid_display($simulator_id, $schedule_id, $customer_id = 0, $view_month = '', $view_year = '') {

  
  if (!$simulator_id) {
    drupal_set_message(t('Error: Invalid simulator id.'));
    drupal_goto('schedules');
    return;
  }

  // if not specified, use the current month and year
  if ($view_month == '') {
    $view_month = date('F');
  }
  if ($view_year == '') {
    $view_year = date('Y');
  }


  drupal_add_library('system', 'ui.dialog');
  // we need the latest jquery lib to use with drag-n-drop
  drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/vendor/jquery-1.8.3.js');
  drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/vendor/jquery-ui-1.9.2.custom.min.js');
  drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/vendor/ui.multidraggable.js');
  drupal_add_js(drupal_get_path('module', 'sabreTools') . '/js/sabreTools.lib.js');
  drupal_add_js(drupal_get_path('module', 'sabreScheduler') . '/js/sabreScheduler.grid.js');


  $form_elems = drupal_get_form('schedule_grid_form', $simulator_id, $schedule_id, $customer_id, $view_month, $view_year);
  $content = render($form_elems);
  $content .= _build_schedule_grid($simulator_id, $schedule_id, $customer_id, $view_month, $view_year);
  
  $form_elems = drupal_get_form('grid_selected_session_form');
  $content .= render($form_elems);

  return $content;
}




/*
 * 	_build_schedule_grid($view_month = '', $view_year = '')
 *
 * 	builds the schedule grid for the specified $view_month, $view_year.
 *  if no month or year specified, returns the grid for the current month/year
 */

function _build_schedule_grid($simulator_id, $schedule_id = 0, $customer_id = 0, $view_month = '', $view_year = '') {
  try {
    // schedule display

    global $base_url;
    global $theme;

    // get the schedule_sessions for this simulator schedule
    $sessions = array();
    $assignments = array();
    $exceptions = array();
    
    $date_count = 0;
    $record_count = 0;
    $years = '';
    $month_and_year = '';
    $date_row = array('', '');
    $date_group = 0;
    $prev_year = '';
    $prev_month = '';
    $prev_date = 0;
    $sess_ptr = 0;
    $content = '';
    
    
    $schedule = _get_schedule($schedule_id);
    $sessions = _get_sessions($schedule_id, $simulator_id, False);
    
    $timezone = $schedule->timezone;
    
    // safety initialization
    foreach ($sessions as $s) {
      $assignments[$s->schedule_session_id] = array();
      if (!$schedule_id) {
        $schedule_id = $s->schedule_id;
      }
    }
    
    /*----------------- Set Grid Date Range -------------------*/
    $now = _st_format_timestamp('now');
    $bd_ts = _st_modify_timestamp($now, 'first day of ' . $view_month . ' ' . $view_year);
    $ed_ts = _st_modify_timestamp($now, 'last day of ' . $view_month . ' ' . $view_year);
    
    
    // get the  schedule's first session begin time and the last session end time of the day
    reset($sessions);
    $first_key = key($sessions);
    $first_session_begins = $sessions[$first_key]->begin_time;
    end($sessions);
    $last_key = key($sessions);
    $last_session_ends = $sessions[$last_key]->end_time;
    $end_date_modify = '';
    
    // if last session bleeds over into next calendar day include it in this grid
    if ((int)$sessions[$last_key]->end_time < (int) $sessions[$last_key]->begin_time)  {
      $end_date_modify = '+1 day';
    }
    

    $grid_begins = _format_timestamp($bd_ts, $first_session_begins, $timezone);
    $grid_ends = _format_timestamp($ed_ts, $last_session_ends, $timezone, $end_date_modify);
    $cal_grid_begins = _format_timestamp($bd_ts, '00:00:00', $timezone);
    $cal_grid_ends = _format_timestamp($ed_ts, '23:59:59', $timezone);
    

    if (!empty($schedule->end_date)) {
      if ($schedule->end_date < $grid_begins) {
        $content .= '<br class="clearBoth" /><div class="grid-alert">' . t('This schedule ended ') . 
                      _st_format_schedule_date($schedule->end_date, 'F Y', $schedule->timezone) .
                   '</div>';
        return $content;
      } else if (($grid_begins < $schedule->end_date) && ($schedule->end_date < $grid_ends)) {
        $content .= '<br class="clearBoth" /><div class="grid-alert">' . t('This schedule ends ') . 
                      _st_format_schedule_date($schedule->end_date, 'F Y', $schedule->timezone) .
                   '</div>';
      }
    }
    if ($grid_begins < $schedule->begin_date ) {
      $content .= '<br class="clearBoth" /><div class="grid-alert">' . t('This schedule begins ') . 
                      _st_format_schedule_date($schedule->begin_date, 'F Y', $schedule->timezone) .
                   '</div>';
      return $content;
    }
  
    // -----------TIMEZONE-----------
    // display Grid times in the default timezone
    // all time calculations are in GMT unix timestamp

    
    
    if ( !count($sessions) ) {
      $content .= '<div id="sch-grid-div">';
      $content .= '<br class="clearBoth" />[' . t('Schedule Unavailable') . ']';
      $content .= '</div>';
      return $content;
    }

    
    /*----------------- Query: Calendar Date ------------------*/
    
    $results_cal = _get_calendar_range($cal_grid_begins, $cal_grid_ends);


    $date_count = 0;
    $dt = new DateTime();
    
    foreach ($results_cal as $cal) {
      // determine what year should be displayed for this date
      $dt->setTimestamp($cal->calendar_date);
      if ( !$date_count ) {
        $year_text = $dt->format('Y');
        $month_text = $dt->format('M');
        $month_and_year = '<div class="sch-grid-month-span">' . $month_text . ' ' . $year_text . '</div>';
        $n_view_month = $dt->format('n');
        $n_view_year = $year_text; 
      }
      $date_count++;

      // New Date Column
      if (!$date_group && ($date_count == 16)) {
        $date_group++;  // only do this once
      }
      
      $date_row[$date_group][$date_count] = 
              $dt->format('D') . '<br />' .
              $dt->format('n/j');
    }

    /*-------------------  Query: Assignments -------------------*/

    $assignments = _get_assignments($grid_begins, $grid_ends, 0, $simulator_id);
   

    /*-------------------  Query: Exceptions -------------------*/
    
    $exceptions = _get_exceptions($grid_begins, $grid_ends, $schedule->timezone, $simulator_id);
    //kpr($exceptions);

    //$cal_total = $results->rowCount();
    //$cal_total = count($results);
    
    
    //--------------------- SCHEDULE -- ADD HIDDEN FIELDS -------------
    //$content = '';
    
    $return_url = 'schedule/grid/' . $simulator_id . '/' . $schedule_id . '/' . $customer_id . '/' . $view_month . '/' . $view_year;
    
    $content .= '<div id="sch-return-url" class="qms-hidden">' . $return_url . '</div>';
    // add hidden fields for this schedule
    $content .= "<div id=\"sch-schedule-id\" class=\"sch-schedule-id qms-hidden\">$schedule_id</div>";
    
    // ---------------------------- Build Grid ----------------------------
    $content .= '<div id="sch-grid-div">';

    
    // ---------------------------- Action Links ----------------------------
    $action_links = _get_action_links($schedule_id);

    
    // ----------------- printing -- heading (logo & title) to top each grid page
    $print_page_heading = _get_print_page_heading($view_month, $view_year);


    //--------------------------- BUILD the GRID ---------------------------------
    // after a 15/16-day grid has been built [1-15, 16-31]
    // output the month, date blocks, and session rows (with or without assignments)

    //if ((($date_count == 16) && isset($date_row[0])) || (($record_count == $cal_total) && isset($date_row[1]))) {
      // if we are at $date_count == 16, all the assignments have been processed for dates 1-15
      // also, if strlen($date_row[0]) > 0, it hasn't been output yet.

    for ($date_row_ptr = 0; $date_row_ptr < 2; $date_row_ptr++) {

      $istart = (0 == $date_row_ptr) ? 1 : 16;
      $iend = (0 == $date_row_ptr) ? 15 : $date_count;
      $date_section = 'dateSection' . ($date_row_ptr + 1);

      $sessValidator = new SessionValidator(0,0,$schedule_id);
      $sessValidator->setExceptions($exceptions);

        
      //------------------- output:  DATE ROW BLOCKS -----------------

      $content .= '<div class="' . $date_section . ' ' . SCH_CLASS_SELECTABLE . '"></div>';
      $content .= $print_page_heading;

      $content .= $month_and_year . '<div class="clearBoth"></div>';

      for ($day = $istart; $day <= $iend; $day++) {
        
        $add_ex_class = '';
        $except_ids = '';
        $exception_info = '';
        $sessValidator->setDate($n_view_month, $day, $n_view_year);
        $sessValidator->clearRelevantException();
//        if ($day == 19) { 
//          $debug = 1; 
//        }
        if ($sessValidator->isOverrideableExceptionDate()) {
          $add_ex_class = SCH_CLASS_EXCEPTION . ' ' . SCH_CLASS_SELECTABLE;
          $except_ids = $sessValidator->getExceptionIds();
          $exception_info = $sessValidator->getExceptionInfo();
        }
        $content .= 
          '<div class="' . SCH_CLASS_SESSION_DATE . ' ' . $add_ex_class . '">' .
            '<div class="' . SCH_CLASS_SELECT_ME . ' ' . SCH_CLASS_SELECT_DATE . '">' .
                $date_row[$date_row_ptr][$day] .
                '<div class="sch-exception-id qms-hidden">' . $except_ids . '</div>' . 
                '<div class="sch-exception-desc qms-hidden">' . $exception_info . '</div>' . 
                /*'<div class="sch-simulator qms-hidden">' . $simulators . '</div>' . */
            '</div>' .
          '</div>';
        
        if ($day == $iend) {
          $content .= '<div class="clearBoth"></div>';
          $content .= $action_links;
        }
      }
      

      //-------------------- output:  SESSION BLOCKS ------------------------
      foreach ($sessions as $s) {
        
        $session_display = $s->session_name . ': &nbsp;&nbsp;' . $s->begin_time . ' - ' . $s->end_time;
        $content .= '<p class="sch-session-time">' . $session_display . '</p>';
        
        for ($day = $istart; $day <= $iend; $day++) {
          
//          if (19 == $day)  {
//            $debug = 1;
//          }

          $sessValidator->clear();
          $sessValidator->setSession($s);
          $sessValidator->setExceptions($exceptions);
          $sessValidator->setDate($n_view_month, $day, $n_view_year);

          // does the session contain a block?
          $blocked = (SessionValidator::SV_BLOCKED == $sessValidator->checkForConflictWithException() );

          /// is there an applicable exception for this session?
          $except_list = $sessValidator->getExceptionConflicts();

          $except_id_list = $sessValidator->getExceptionIds();
          $exception_reason_list = $sessValidator->getExceptionReasons();

//          if ( (2 == $day) && (54 == $s->schedule_session_id)) {
//            $debug = 1;
//          }

          // how much time is available in this session?
          $session_begins_ts = 0;
          $session_ends_ts = 0;
          $session_total_time = _calc_session_time($s, $n_view_month, $day, $n_view_year, $session_begins_ts, $session_ends_ts, $timezone);
          
          
          $rem_time = $session_total_time;
          $ts_ptr = $session_begins_ts;

          $session_content = '';

          $assign = null;
          $assign_count = 0;

          if ( isset($assignments[$s->schedule_session_id][$day]) && 
               is_array($assignments[$s->schedule_session_id][$day]))  {
            
            $assign = reset($assignments[$s->schedule_session_id][$day]);
            next($assignments[$s->schedule_session_id][$day]);
            $assign_count = count($assignments[$s->schedule_session_id][$day]);
          }

          $except = reset($except_list);
          next($except_list);
          $except_count = count($except_list);

          $bFirst = True;
            

          while ($rem_time > SCH_THRESHOLD) {  //( $rem_time > SCH_THRESHOLD )
            $percent = '';

            // these will apply to most situations, we alter for specifics as needed below
            $vars = new stdClass();
            $vars->session_class = SCH_CLASS_OPEN;
            $vars->blocked = $blocked;
            $vars->status = array();
            $vars->month = $view_month;
            $vars->n_month = $n_view_month;
            $vars->day = $day;
            $vars->year = $view_year;
            $vars->schedule_id = $s->schedule_id;
            $vars->schedule_session_id = $s->schedule_session_id;
            $vars->session_name = $s->session_name;
            $vars->simulator_id = $simulator_id;
            $vars->schedule_id = $schedule_id;
            $vars->timezone = $schedule->timezone;
            $vars->session_total_time = $session_total_time;
            $vars->begin_time = $session_begins_ts;
            $vars->end_time = $session_ends_ts;
            $vars->percent = 100;

            

            // Is there an assignment for this session & day ?
            if ( $assign_count && isset($assign->processed)) {
              //$assign = $assignments[$s->schedule_session_id][$day][$a++];
              $assign = current($assignments[$s->schedule_session_id][$day]);
              next($assignments[$s->schedule_session_id][$day]);
            }
            if ($assign == False) { $assign = Null; }


            if ($except_count && isset($except->processed)) {
              $except = current($except_list);
              next($except_list);
            }
            if ($except == False) { $except = Null; }


            if (!isset($assign)) {

              $gap_time = 0;

              //--------------------- NO ASSIGNMENTS -----------------------------

              if ($blocked) {
              //if ($except) {
                //( ($ts_ptr + SCH_THRESHOLD) >= $except->begin_timestamp ) ) {
                if (!isset($except->processed) && isset($except->percent) &&
                        ($except->percent <> 1) &&
                        (($ts_ptr + SCH_THRESHOLD) < $except->begin_timestamp )) {
                  // there is an unblocked open gap before the blocking exception
                  $gap_time = _date_diff_seconds($ts_ptr, $except->begin_timestamp);
                  $percent = round(($gap_time / $session_total_time), 2);
                  $vars->begin_time = $ts_ptr;
                  $vars->end_time = $except->begin_timestamp;
                  $vars->blocked = False;
                  $ts_ptr = $except->begin_timestamp;
                } 
                else if (!isset($except->processed) && isset($except->exception_id)) {
                  // if exception is less than threshold, ignore it in the grid
                  if (($except->end_timestamp - $ts_ptr) > SCH_THRESHOLD) {
                    $vars->begin_time = $except->begin_timestamp;
                    $vars->end_time = $except->end_timestamp;
                    $percent = $except->percent;                      
                    $vars->blocked = True;
                    // clear the SCH_CLASS_OPEN setting, its a block ( not open )
                    $vars->session_class = '';
                    $ts_ptr = $except->end_timestamp;
                  } 
                  else {
                    $gap_time = 0;
                    $percent = 0;
                    $vars->blocked = False;
                  }
                  $except->processed = True;
                } 
                else { // trailing gap after an exception block
                  $gap_time = _date_diff_seconds($ts_ptr, $session_ends_ts);
                  $percent = round(($gap_time / $session_total_time), 2);  
                  $vars->begin_time = $ts_ptr;
                  $vars->end_time = $session_ends_ts; 
                  $vars->blocked = False;
                  $ts_ptr = $session_ends_ts;
                }
              } else {
                //// ------------ TRAILING OPEN BLOCK ----------------------
                // NO MORE assignments but there's a trailing gap (after assignment or exception) or 
                // NO Assignment for this date
                $gap_time = _date_diff_seconds($ts_ptr, $session_ends_ts);
                $percent = round(($gap_time / $session_total_time), 2);
                $vars->begin_time = $ts_ptr;
                $vars->end_time = $session_ends_ts;
                $ts_ptr = $session_ends_ts;
              }


              // ---------------- SESSION-OPEN or EXCEPTION-BLOCK Gap --------------------
              //if ( $gap_time ) {
              if ( $percent ) {
                $vars->percent = ($percent * 100);

                if (!strlen($session_content)) {
                  $session_content = _begin_session_block($session_total_time);
                }

                if ((!$customer_id) || (($customer_id) && !$blocked)) {

                  $vars->droppable = True;
                  $session_content .= _build_session_block($vars, array(), $except, $bFirst);
                } 
                else {  // GRID_CUSTOMER -- grey out blocked sessions
                  $vars->unavailable = True;
                  $session_content .= _build_session_block_unavailable($vars, array(), $except, $bFirst);
                }
              }
              //}
            } else { 
            //if ($assign) {
              //---------- ASSIGNMENT EXISTS --------------------------

//              if ( (25 == $day) && (2 == $s->schedule_session_id)) {
//                $debug = 1;
//              }
              //------------------ SESSION-OPEN before an assignment or between assignments in session
              if (($ts_ptr + SCH_THRESHOLD) < $assign->begin_timestamp) {

                // leading or middle gap before an assignment

                $gap_time = _date_diff_seconds($ts_ptr, $assign->begin_timestamp);
                $percent = round(($gap_time / $session_total_time), 2);
                $vars->begin_time = $ts_ptr;
                $vars->end_time = $assign->begin_timestamp;
                $vars->percent = ($percent * 100);
                $ts_ptr = $assign->begin_timestamp;

                if (!strlen($session_content)) {
                  $session_content = _begin_session_block($session_total_time);
                }

                if ((!$customer_id) || (($customer_id) && !$blocked)) {

                  $vars->droppable = True;
                  $session_content .= _build_session_block($vars, array(), $except, $bFirst);
                } 
                else {  // customer specified (availability report) grey out blocked sessions
                  $vars->unavailable = True;
                  $session_content .= _build_session_block_unavailable($vars, array(), $except, $bFirst);
                }
              } else {

                //----------------- ASSIGNMENT BLOCK --------------------------
                
//                if ( (11 == $day) || (16 == $day)) {
//                  $debug = 1;
//                }

                $vars->session_class = SCH_CLASS_ASSIGNMENT;

                if ($blocked && isset($except->begin_timestamp)) {
                  //check if the assignment falls within the block or if the block is in a diff part 
                  // of the session -- only part of it needs to overlap in order to show up as blocked
//                if ( (25 == $day) ) {
//                  $debug = 1;
//                }

                  $block_check1 = 
                       ( (($except->begin_timestamp <= $assign->begin_timestamp ) &&
                          ($assign->begin_timestamp < $except->end_timestamp) )   );
                  $block_check2 = 
                          ((( $assign->begin_timestamp <= $except->begin_timestamp ) &&
                          ( $except->begin_timestamp < $assign->end_timestamp) ) );
                  
                  $vars->blocked = ($block_check1 || $block_check2);
                }

                $percent = round(((int) $assign->total_time / 
                                (_date_diff_seconds($session_begins_ts, 
                                                   $session_ends_ts))), 
                                 2, PHP_ROUND_HALF_DOWN);
                if ($percent >= 0.94) {
                  $percent = 1;
                }  // ignore the threshold amount
                $vars->percent = ($percent * 100);
                $vars->begin_time = $assign->begin_timestamp;
                $vars->end_time = $assign->end_timestamp;
                $ts_ptr = $assign->end_timestamp;

                if (!strlen($session_content)) {
                  $session_content = _begin_session_block($session_total_time);
                }

                if (!$customer_id ||
                        ( $customer_id && ( $customer_id == $assign->customer_id))) {
                  // this is an assignment for the specified customer									
                  $session_content .= _build_session_block($vars, $assign, $except, $bFirst);
                } 
                else {
                  // this is an assignment for a different customer... 
                  // show as blocked off/greyed out w/no info
                  $vars->unavailable = True;
                  $session_content .= _build_session_block_unavailable($vars, $assign, $except, $bFirst);
                }
                $assign->processed = True;
              }
            }
            
            
            // under some circumstances, this will cause a perm loop
            //$rem_time -= ($session_total_time * $percent);
            
            $time_used = $ts_ptr - $session_begins_ts;
            $rem_time = ($session_ends_ts - $session_begins_ts) - $time_used;
            
          } // while

          $content .= $session_content . _end_session_block();
          $session_content = '';
        } // for ($day) loop
      } // foreach ($session) loop
            

    } // END - FOR $date_row_ptr
    
    $content .= '<br class="clearBoth">';
    $content .= '</div>';   // end <div id="sch-grid-div">
    // container for message box
    $content .= '<div id="qms-message-box" class="qms-hidden"></div>';

    $content .= _get_grid_strings();
    

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

/*
 *  _get_action_links()
 */
function _get_action_links($schedule_id) {
  
  $action_links = '';
  global $base_url;
  
  if ( user_access('edit schedules') ) {
    // sch-link-assign && sch-link-except do not have normal href url
    // these are for passing through to the hidden grid_selected_sessions_form
    // for later redirection

    $action_links =
      '<div class="qms-actions">' .
      '<span class="qms-waiting"><img class="qms-waiting-img" src="' .
      $base_url . QMS_IMAGES_DIR . 'waiting.gif" /></span>' .
      '<a class="sch-link-delete" href="' . url('schedule/grid/delete') . '">' . t('DELETE') . '</a>' .
      '<a class="sch-link-except" href="' . 'exception/add' . '">' . t('EXCEPTION') . '</a>' .
      '<a class="sch-link-elog" href="' . url('elog/assign') . '">' . t('E-LOG') . '</a>' .
      '<a class="sch-link-cut" href="' . url('schedule/grid/cut') . '">' . t('CUT/BILL') . '</a>' .
      '<a class="sch-link-copy" href="' . url('schedule/grid/copy') . '">' . t('COPY') . '</a>' .
      '<a class="sch-link-paste" href="' . url('schedule/grid/paste/' . $schedule_id) . '">' .
      t('PASTE') . '</a>' .
      '<a class="sch-link-move" href="' . url('schedule/grid/move/' . $schedule_id) . '">' .
      t('MOVE') . '</a>' .
      '<a class="sch-link-assign" href="' . 'schedule/assignment/add/' . $schedule_id . '">' .
      t('ASSIGN/BOOK') . '</a>' . 
      '<a class="sch-link-view" href="#">' . t('VIEW') . '</a>' .
      '<a class="sch-link-cancel" href="#">' . t('CANCEL') . '</a>' .
      '<span class="qms-waiting"><img class="qms-waiting-img" src="' . $base_url . 
         QMS_IMAGES_DIR . 'waiting.gif" /></span>' .
      '</div>';
  }
  
  return $action_links;
}

/*
 *  
 */
function _get_print_page_heading($view_month, $view_year) {
  global $base_url;
  global $theme; 
  
  $content = 
  '<br class="clearBoth" />' .
  '<div class="sch-print-header">' . 
    '<img class="sch-print-logo" src="' . $base_url . '/sites/all/themes/' . $theme . '/images/logo.png" />' .
    '<div class="sch-page-title">' . drupal_get_title() . '</div>' .
    '<div class="sch-page-subtitle">' . t($view_month) . ' ' . $view_year . '</div>' .
  '</div>';
  
  return $content;
}


/*
 * _get_grid_strings()
 */
function _get_grid_strings() {
  // text strings for multilang handling, positional, pipe-delim
  $content =
            '<div id="sch-strings" class="qms-hidden">' .
            t('Yes') . '|' .
            t('No') . '|' .
            t('OK') . '|' .
            t('Too Many Selections!') . '|' .
            t('Only one selected assignment may be edited at a time.') . '|' .
            t('Adding a new session assignment requires selecting a single open session.') . '|' .
            t('Only session assignments may be moved.') . '|' .
            t('Complete Move?') . '|' .
            t('Save schedule items to their new locations?') . '|' .
            t('Invalid Move!') . '|' .
            t('Unable to move the schedule item(s) to the selected location.') . '|' .
            t('MOVE') . '|' .
            t('SAVE') . '|' .
            t('Delete?') . '|' .
            t('Are you sure you want to delete the selected session items?') . '|' .
            t('Selected Assignments') . '|' .
            t("Server Request Error") . '|' .
            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.") . '|' .
            t('Please select only one exception to edit.') . '|' .
            t('Multiple Selected') . '|' .
            t('More than one exception exists for this session time.  Select the exception to edit:') . '|' .
            t('Done') . '|' .
            t('Unable to perform task for all selected items.') . '|' .
            t('Processed') . '|' .
            t('Both Assignment(s) and Exception(s) selected.  Please check your selections and try again.') . '|' .
            t('Selected Exceptions') . '|' .
            t('Unable to') . '|' .
            t('copy') . '|' .
            t('Move Processing Complete') . '|' . 
            t('Assignment') . '|' . 
            t('Exception') . 
            '</div>';
  
  return $content;
}



/*
 * 	schedule_grid_form()
 *
 */

function schedule_grid_form($form, $form_state, $simulator_id, $schedule_id, $customer_id = 0, $view_month = '', $view_year = '') {

  global $base_url;
  $month_list = new MonthList();
  $year_list = new YearList();

  $href_base = 'schedule/grid';

  $goto_url = "$href_base/$simulator_id/$schedule_id/$customer_id";
  $dest_url = $goto_url;
  if ( strlen($view_month) && strlen($view_year)) {
    $dest_url .= '/' . $view_month . '/' . $view_year;
  }
  
  $sched_rpt_url = 'reports/grid/' . $simulator_id . '/' . $schedule_id . '/' . $customer_id . '/' . 
                                     $view_month . '/' . $view_year;
  $excel_rpt_url = 'reports/grid/excel/' . $simulator_id . '/' . $schedule_id . '/' . $customer_id . '/' . 
                                     $view_month . '/' . $view_year;

  $month_array = $month_list->get(); // includes '- select -' option, need to remove this to calc next/prev month
  array_shift($month_array);

  $href_prev_month = _st_array_key_relative($month_array, $view_month, -1);
  $href_prev_year = ((int) $view_year);

  if (($href_prev_month === False)) {
    $href_prev_month = 'December';
    $href_prev_year--;
  }
  $href_next_month = _st_array_key_relative($month_array, $view_month, 1);
  $href_next_year = ((int) $view_year);
  if (($href_next_month === False) || ($href_next_month == 'January')) {
    $href_next_month = 'January';
    $href_next_year++;
  }

  $form['report_box'] = array(
      '#type' => 'fieldset',
      '#title' => t('Settings'),
      '#attributes' => array('id' => 'sch-report-box')
  );

  $form['report_box']['form_table_begin'] = array(
      '#markup' => '<div id="sch-grid-view-form">' .
      '<table class="qms-plain-table" style="vertical-align:bottom;width:100%;height:5em;"><tr>',
  );


  $simulator_list = new SimulatorList();
  $scheduled_simulators = $simulator_list->getScheduledSimList();
  $select_opts = array();
  foreach($scheduled_simulators as $s) {
    $key = $s->simulator_id . '|' . $s->schedule_id;
    $value = $s->sim_name . ' : ' . $s->schedule_name;
    $select_opts[$key] = $value;
  }
  

  $form['report_box']['simulator'] = array(
      '#type' => 'select',
      '#title' => t('Select a Simulator') . '<span>&nbsp;&nbsp;(* = ' . t('inactive') . ')</span>',
      '#options' => $select_opts,
      '#default_value' => ($simulator_id ? $simulator_id : 0),
      '#attributes' => array('id' => 'sch-simulator-select',
          'class' => array('qms-select')),
      '#default_value' => $simulator_id .'|'.$schedule_id,
      '#validated' => True,
      '#prefix' => '<td>&nbsp;</td><td colspan="2" style="vertical-align:bottom;text-align:left;">',
      '#suffix' => '</td>',
  );

  $customer_list = new CustomerList();

  $form['report_box']['customer'] = array(
      '#type' => 'select',
      '#title' => t('Customer'),
      '#required' => True,
      '#options' => $customer_list->getAll(CustomerList::INCL_ALL),
      '#default_value' => $customer_id,
      '#validated' => True,
      '#attributes' => array('id' => 'sch-customer-select',
          'class' => array('qms-select')),
      '#prefix' => '<td colspan="2" style="vertical-align:bottom;text-align:left;">',
      '#suffix' => '</td><td>&nbsp;</td>',
  );

  $form['report_box']['form_table_markup'] = array(
      '#markup' => '</tr><tr>',
  );

  $form['report_box']['go_left'] = array(
      '#markup' =>
      '<td style="vertical-align:bottom;width:5em;">' .
      '<a  href="' .
      url($goto_url . '/' . $href_prev_month . '/' . $href_prev_year) . '">' .
      '<img class="sch-grid-arrow" src="' . $base_url . QMS_IMAGES_DIR . 'go_left.png' . '" />' .
      '</a></td>',
  );
  $form['report_box']['view_month'] = array(
      '#type' => 'select',
      '#title' => t('View Month'),
      '#options' => $month_list->get(),
      '#default_value' => $view_month,
      '#required' => False,
      '#attributes' => array('id' => 'sch-view-month-select'),
      '#prefix' => '<td style="vertical-align:bottom;width:10em;">',
      '#suffix' => '</td>',
  );
  
  
  $form['report_box']['view_year'] = array(
      '#type' => 'select',
      '#title' => t('View Year'),
      '#options' => $year_list->get(),
      '#default_value' => (int) $view_year,
      '#required' => False,
      '#attributes' => array('id' => 'sch-view-year-select'),
      '#prefix' => '<td style="vertical-align:bottom;width:10em;">',
      '#suffix' => '</td>',
  );

  $form['report_box']['go_to'] = array(
      '#type' => 'button',
      '#default_value' => t('GO'),
      '#attributes' => array('class' => array('sch-btn'),
          'id' => 'sch-btn-submit'),
      '#prefix' => '<td style="vertical-align:bottom;width:5em;">',
      '#suffix' => '</td>',
  );

  $form['report_box']['view_today'] = array(
      '#type' => 'button',
      '#default_value' => t('Today'),
      '#attributes' => array('class' => array('sch-btn'),
          'id' => 'sch-btn-view-today'),
      '#prefix' => '<td style="vertical-align:bottom;width:7em;">',
      '#suffix' => '</td>',
  );



  $form['report_box']['go_right'] = array(
      '#markup' =>
      '<td style="vertical-align:bottom;text-align:center;width:5em;">' .
      '<a  href="' .
      url($goto_url . '/' . $href_next_month . '/' . $href_next_year) . '">' .
      '<img class="sch-grid-arrow" src="' . $base_url . QMS_IMAGES_DIR . 'go_right.png' . '" />' .
      '</a></td>',
  );
  
  $form['report_box']['form_table_markup2'] = array(
      '#markup' => '</tr><tr>',
  );
  
  $form['report_box']['sched_report_link'] = array(
    '#markup' => '<td>&nbsp;</td><td colspan="5">' . 
                  l(t('View Schedule Report'), $sched_rpt_url, 
                    array('attributes' => array('class' => array('sch-schedule-report-link')),
                            'query' => array('destination' => $dest_url)) )  . 
                  '&nbsp;&nbsp;&bull;&nbsp;&nbsp;' . 
                  l(t('Export to Excel'), $excel_rpt_url, 
                    array('attributes' => array('class' => array('sch-excel-report-link')),
                            'query' => array('destination' => $dest_url)) ) .
                 '</td>',
  );
  

  $form['report_box']['form_table_end'] = array(
      '#markup' => '</tr></table></div>',
  );

  $form['report_box']['href_base'] = array(
      '#type' => 'textfield',
      '#default_value' => url($href_base),
      '#attributes' => array('class' => array('qms-hidden'),
          'id' => 'sch-schedule-grid-href-base'),
  );


  $timezone = _get_schedule_timezone($schedule_id);
  
  $form['color_key'] = array(
      '#markup' =>
      '<div class="sch-grid-legend">' .
      '<div class="sch-key sch-full-motion"></div><span>' . t('Full Motion') . '</span><br />' .
      '<div class="sch-key sch-fixed-base"></div><span>' . t('Fixed Base') . '</span><br />' .
      '<div class="sch-key sch-session-open"></div><span>' . t('Session Available') . '</span><br />' .
      '<div class="sch-key sch-session-date sch-exception"></div><span>' . t('Exception Date') . '</span><br />' .
      '<div class="sch-key sch-exception-block"></div><span>' . t('Maintenance') . '</span><br /><br />' .
      '<div class="timezone">'. t('Schedule Timezone') . ':&nbsp;&nbsp;'. $timezone .'</div>' .
      '</div>',
  );
 
  

  return $form;
}


/*
 *  grid_selected_session_form()
 * 
 *  ASSIGNMENTS && EXCEPTIONS
 *  uses hidden form on grid to store list of selected grid sessions along with...
 * 
 *  command url (assignment or exception url path)
 *  dest_url (url for destination to include with redirect when calling command_url)
 *  selected_sessions (delimited list of sessions from grid which get parsed later in their 
 *                     respective handling functions, stored in the 
 *                     $_SESSION['sel_grid_sessions'] variable to pass to the command_url)
 */
function grid_selected_session_form($form, $form_state) {
  
  $form['command_url'] = array(
    '#type' => 'textfield',
    '#default_value' => '',
    '#attributes' => array( 'id' => 'sch-command-url',
                            'class' => array('qms-hidden')),
  );
  
  $form['dest_url'] = array(
    '#type' => 'textfield',
    '#default_value' => '',
    '#attributes' => array( 'id' => 'sch-dest-url',
                            'class' => array('qms-hidden')),
  );
  
  $form['selected_sessions'] = array(
    '#type' => 'textarea',
    '#resizable' => False,
    '#default_value' => '',
    '#attributes' => array( 'id' => 'sch-selected-sessions',
                            'class' => array('qms-hidden')),
       // text areas require a wrapper div in order to hide them
    '#prefix' => '<div id="sch-selected-sessions-div" class="qms-hidden">',
    '#suffix' => '</div>',
  );
  
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
    '#submit' => array('grid_selected_session_form_submit'),
    '#attributes' => array( 'id' => 'sch-selected-sessions-submit',
                            'class' => array('qms-hidden')),
  );

  
  
  return $form;
}


/*
 * 
 *    grid_selected_session_form_submit()
 * 
 *    retrieve the data from the hidden grid form fields, 
 *    store the sel sessions in the SESSION variable and call the
 *    respective form as indicated by $cmd_url
 */
function grid_selected_session_form_submit($form, $form_state) {
  
  $cmd_url = $form_state['values']['command_url'];
  $dest_url = $form_state['values']['dest_url'];
  $sel_sessions = $form_state['values']['selected_sessions'];
  
  // store the list of selected session from the hidden grid form
  // in the $_SESSION variable to be retrieve by the assignment or exception form
  // immediately when redirected from here to $cmd_url path
  // this variable will be unset after the sel_session list is retrieved in the
  // destination form
  $_SESSION['sel_grid_sessions'] = $sel_sessions;
  
  unset($_GET['destination']);
  drupal_goto($cmd_url, array('query' => array('destination' => $dest_url)));
}



/*
 * 		_build_session_block()
 * 		
 * 		format a session block assignment for display in the grid
 * 		IN:  assignment object
 * 				 additional assignment variables
 * 		RETURN:  $content string
 */

function _build_session_block($vars, $assign = Null, $except = Null, &$bFirst = True) {

  // format a block for display in the grid ( with or without an assignment)
  
  $schedule_date = '';
  $blocked_div = '';

  $classes = array();
  $class_list = '';
  
  $dt = new DateTime();
  $dt->setTimezone(new DateTimeZone($vars->timezone));

  $session_date = sprintf('%02d-%02d-%s', $vars->n_month, $vars->day, $vars->year);

  if (isset($assign->begin_timestamp)) {
    $dt->setTimestamp($assign->begin_timestamp);
    $schedule_date = $dt->format('n/j');
  } else if (isset($vars->n_month) && isset($vars->day)) {
    $schedule_date = $vars->n_month . '/' . $vars->day;
  }

  if ($vars->blocked) {
    if (isset($assign->assignment_id)) {
      // for a blocked assignment, flip the exception class to plain exception
      // differentiates from exception-blocks with no assignments
      $classes[] = SCH_CLASS_EXCEPTION;
    } 
    else {
      $classes[] = SCH_CLASS_EXCEPTION_BLOCK;
    }
  }


  // build the classes based on the status settings	foreach($vars->status as $status => $setting) {

  if (isset($vars->unavailable)) {
    $classes[] = SCH_CLASS_UNAVAILABLE;
  } 
  else if (True == $vars->blocked) {
    if (!isset($assign->assignment_id)) {
      $except_desc = ($except->maintenance ? t('MNT') : _st_left($except->exception_desc, 10));
      $blocked_div = '<div class="sch-blocked">' . $except_desc .
              (isset($except->customer_code) ? '<br />' . $except->customer_code : '' ) .
              '</div>';
    }
  } 
  else {
    // For Assignments
    // these are for all other statuses that are assigned classes
    // when not unavailable & not blocked
    if (isset($assign->session_type)) {
      if (1 == $assign->session_type) {
        $classes[] = SCH_CLASS_MOTION;
      } 
      else if (2 == $assign->session_type) {
        $classes[] = SCH_CLASS_FIXED;
      }
    } 
    else {  // no assignment or block
      if (isset($vars->droppable)) {
        $classes[] = SCH_CLASS_DROPPABLE;  // this is an open session that can have assignments dropped on it
      }
    }
  }


  $classes[] = SCH_CLASS_SELECT_ME;

  $class_list = join(' ', $classes);


  $content =
          '<div class="' . $vars->session_class . ' ' .
          $class_list . ' ' . '" style="height:' . $vars->percent . '%">' .
          /* assignment or open session fields - displayed */
          '<div class="sch-schedule-date-md' . ($bFirst ? '' : ' qms-hidden ') . 
          '">' . 
          $schedule_date . '</div>' . $blocked_div;

  if ($bFirst) {
    $bFirst = False;
  }

  if (isset($assign->assignment_id)) {  // Assignment-specific fields
    $content .=
            /* assignment fields that are displayed */
            '<div class="sch-customer">' . 
              $assign->customer_code . 
              ((!empty($assign->elog_id)) ? 
                ('<br><span class="sch-elog-flag">&nbsp;&nbsp;&nbsp;</span>') : '') . 
            '</div>' .
            '<div class="sch-group">' . ($assign->group_id ? $assign->group_code : '') . '</div>' .
            '<div>' . 
              '<span class="sch-begin-time-actual">' . $assign->begin_time_actual . '</span>' . '-' .
              '<span class="sch-end-time-actual">' . $assign->end_time_actual . '</span>' . 
            '</div>' .
            /* assignment fields that are hidden */
            '<div class="sch-assignment-id qms-hidden">' . $assign->assignment_id . '</div>' .
            '<div class="sch-customer-display qms-hidden">' .
            $assign->customer . '<br />' .
            ($assign->group_id ? ($assign->group_name . '<br />') : '') .
            '</div>' .
            '<div class="sch-url qms-hidden">' .
              l(t('Edit'), 'schedule/assignment/edit/' . $assign->assignment_id, 
                  array('query' => array('destination' => ('schedule/grid/' . $vars->simulator_id . '/' . $vars->schedule_id . '/0/' .
                  $vars->month . '/' . $vars->year )))) .
            '</div>' . 
            '<div class="sch-note-text qms-hidden">' . 
              nl2br(_st_convert_symbols($assign->note_text)) . 
            '</div>';
  } 
  

  // these are needed for ALL schedule items (assignments, exceptions, & open time)
  $session_date_display = sprintf('%02d/%02d/%s', $vars->n_month, $vars->day, $vars->year);
  
  $content .=
          '<div class="sch-session-id qms-hidden">' . $vars->schedule_session_id . '</div>' .
          '<div class="sch-schedule-date qms-hidden">' . $session_date . '</div>' . 
          '<div class="qms-hidden">' . 
              '<span class="sch-portion">' . (($vars->percent == 100) ? 'F' : 'P') . '</span>' . 
              '<span class="sch-begin-time">' . $vars->begin_time . '</span>' . '-' .
              '<span class="sch-end-time">' . $vars->end_time . '</span>' . 
          '</div>' . 
          '<div class="sch-session-display qms-hidden">' .
            $session_date_display . ', ' . $vars->session_name . ': ' .
            _st_format_schedule_date($vars->begin_time, 'H:i', $vars->timezone) . '-' . 
            _st_format_schedule_date($vars->end_time, 'H:i', $vars->timezone) .
          '</div>';


  if (isset($except->exception_id)) {
    $content .= /* assignment or open session  fields - hidden */
            '<div class="sch-exception-id qms-hidden">' . $except->exception_id . '</div>' .
            '<div class="sch-exception-desc qms-hidden">' . $except->exception_desc . '</div>';
    
    $content .= '<div class="sch-url qms-hidden">' .
                  l($except->exception_desc, 'exception/edit/' . $except->exception_id, 
                  array('query' => array('destination' => ('schedule/grid/' . $vars->simulator_id . '/' . $vars->schedule_id . '/0/' .
                  $vars->month . '/' . $vars->year )))) .
                '</div>';
            
    
    if ( isset($except->customer) ) {
      $content .= '<div class="sch-customer-display qms-hidden">' . $except->customer . '</div>';
    }
    if ( isset($except->simulator_id) ) {
      if( !$except->simulator_id ) {
        $content .= '<div class="sch-simulator qms-hidden">' . t('ALL SIMULATORS') . '</div>';
      }
    }
    
    if ( isset($except->note_text) ) {
      $content .= '<div class="sch-note-text qms-hidden">' . 
                    nl2br(_st_convert_symbols($except->note_text)) . 
                  '</div>';
    }
  }
  $content .= '</div>';


  return $content;

  /*
    if ( True == $omitSessionBlock ) {
    return $content;
    }

    if ( 0 == $vars['session_total_time']) {
    $vars['session_total_time'] = 14400; // base it all on 4 hour blocks
    }
    $block_height = ((string)round(($vars['session_total_time']/14400), 2) * 6.2) . 'em';

    return '<div class="sch-session-block selectable" style="height:' . $block_height . ';">' . $content . '</div>';
   */
}

/*
 * 		_build_session_block_unavailable()
 * 		
 * 		format a session block as unavailable (with or without an assignment) for display in the grid
 * 		IN:  additional assignment variables
 * 				 assignment object (optional)
 * 		RETURN:  $content string
 */

function _build_session_block_unavailable($vars, $assign = Null, $except = Null, &$bFirst = True) {

  $schedule_date = '';
  $blocked_div = '';
  $classes = array();
  $class_list = '';
  $dt = new DateTime();
  $dt->setTimezone(new DateTimeZone($vars->timezone));

  if (isset($assign->begin_timestamp)) {
    $dt->setTimestamp($assign->begin_timestamp);
    $schedule_date = $dt->format('n/j');
  } else if (isset($vars->n_month) && isset($vars->day)) {
    $schedule_date = $vars->n_month . '/' . $vars->day;
  }


  // build the classes based on the status settings
  if (isset($vars->unavailable)) {
    $classes[] = SCH_CLASS_UNAVAILABLE;
  } 
  else if (True == $vars->blocked) {
    if (!isset($assign->assignment_id)) {
      $blocked_div = '<div class="sch-blocked">&nbsp;</div>';
      $classes[] = SCH_CLASS_EXCEPTION_BLOCK;
    } 
    else {
      // for a blocked assignment, flip the exception class to plain exception
      // differentiates from exception-blocks with no assignments
      $classes[] = SCH_CLASS_EXCEPTION;
    }
  }

  $class_list = join(' ', $classes);
  
  
  

  $content =
          '<div class="' . $vars->session_class . ' ' . $class_list . 
            ' ' . '" style="height:' . $vars->percent . '%">' .
          /* these are displayed */
          '<div class="sch-schedule-date-md' . ($bFirst ? '' : ' qms-hidden ') . '">' . 
            $schedule_date . '</div>';
 
  if (isset($except->exception_id)) {
    $content .= '<div class="sch-exception-id qms-hidden">' . $except->exception_id . '</div>' .
          (isset($except->exception_id) ?
          '<div class="sch-exception-desc qms-hidden">' . $except->exception_desc . '</div>' : '') .
          $blocked_div;
          
  }
  $content .= '</div>';

          

  if ($bFirst) {
    $bFirst = False;
  }


  return $content;
}

function _begin_session_block($total_time = 14400) {

  $block_height = ((string) round(($total_time / 14400), 2) * 6.2) . 'em';

  return '<div class="sch-session-block ' . SCH_CLASS_SELECTABLE . '" style="height:' . $block_height . ';">';
}

function _end_session_block() {
  return '</div>';
}



/*
 * 		cut_copy_delete( $mode )
 *
 * 		callback function, the selected assignments or exceptions 
 *                        (without key id, and date info, are copied to the clipboard)
 * 	  if $mode == 'CUT', selected assignments (only) are removed from the schedule, but flagged with cut_bill = 1
 * 	  if $mode == 'COPY', no change to the grid is made, selected assignments or exceptions save to clipboard
 *    if $mode == 'DELETE', everything selected is deleted
 */

function cut_copy_delete($mode = 'COPY') {

  $assignment_ids = $_POST['assignment_list'];
  $exception_ids = $_POST['exception_list'];

  $exception = (object) Null;
  $copied = array();
  $except_count = 0;

  if (!strlen($assignment_ids) && !strlen($exception_ids)) {
    return drupal_json_output(array(
        'response' => False,
        'assign_count' => 0,
        'except_count' => 0,
    ));
  }

  // DELETE - remove the selected assignments and return the result.. DONE!
  if ($mode == 'DELETE') {

    $assigns_deleted = _delete_selected_assignments($assignment_ids);
    $excepts_deleted = _delete_selected_exceptions($exception_ids);
    $rc = True;

    return drupal_json_output(array(
        'response' => $rc,
        'assign_count' => $assigns_deleted,
        'except_count' => $excepts_deleted,
    ));
  }

  // CUT, COPY....
  // get all of the selected assignments
  $assignment_list = _get_selected_assignments($assignment_ids);
  $assign_count = count($assignment_list);


  if ('CUT' == $mode) {
    // update the selected assignments as CUT/BILLABLE for reporting/billing
    // these are not saved to the clipboard
    $num_cut = _cut_bill_selected_assignments($assignment_ids);
    $rc = ($num_cut > 0);
    return drupal_json_output(array(
        'response' => $rc,
        'assign_count' => $num_cut,
        'except_count' => 0,
    ));
  }

  // get the selected exception
  // only able to copy one exception at a time
  if (strlen($exception_ids)) {
    $exception_id_list = explode(',', $exception_ids);
    if (isset($exception_id_list[0])) {
      $exception_id = $exception_id_list [0];
      $exception = _get_exception($exception_id);
      if (isset($exception)) {
        $except_count = 1;
      }
    }
  }


  // COPY == $mode
  $clipboard_name = _get_scheduler_cache_name(SCH_USER_CLIPBOARD);


  // if for some reason, both assignments & exceptions copy make it to this point,
  // should never happen as js in the browser will block this
  // preferentially handle assignments, ignore exceptions
  // store the whole array in variable (so it doesn't get dumped with cache)

  if ($assign_count) {
    $copied['assignments'] = $assignment_list;
  } else if ($except_count) {
    $copied['exception'] = $exception;
  }

  variable_set($clipboard_name, $copied);

  return drupal_json_output(array(
      'response' => True,
      'assign_count' => $assign_count,
      'except_count' => $except_count,
  ));
}

/*
 * 		paste_from_clipboard( )
 *
 * 		callback function, is sent an open schedule_id & session_id
 * 	  pastes clipboard data into open sessions from the date selected
 */

function paste_from_clipboard($schedule_id) {

  $clipboard = array();

  /* ONE OR MORE SESSIONS BEING SENT AS A SINGLE POST VALUE
    $schedule_id = (int)$_POST['schedule_id'];
    $session_id = (int)$_POST['session_id'];
    $session_date = $_POST['schedule_date'];
   */
  $sessions_str = $_POST['sessions'];

  $sessions = explode('|', $sessions_str);
  $sel_session_list = array();

  $n = 0;
  for ($i = 0; $i < count($sessions); $i++) {
    if (strlen($sessions[$i])) {
      $s = explode('~', $sessions[$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 (!count($sel_session_list)) {
    return drupal_json_output(array('response' => False));
  }

  // determine the storage name based on the user id
  $clipboard_name = _get_scheduler_cache_name(SCH_USER_CLIPBOARD);

  // retrieve the user's clipboard contents
  $clipboard = variable_get($clipboard_name, '');

  $clipboard_count = (is_array($clipboard) ? count($clipboard) : 0);

  if ($clipboard_count) {
    if (isset($clipboard['assignments'])) {
      // previous assignment data was stored in the clipboard.

      if (1 == $clipboard_count) {
        // single assignment copied/cut
        // paste to multiple selected session locations
        _paste_one_assignment_to_many_sessions($schedule_id, $sel_session_list, $clipboard['assignments']);
      } else if (1 < $clipboard_count) {
        // pasting more than one assignment from the clipboard, do a cluster paste based on the 
        // position of the first selected open session
        // pasted in the configuration of the originally copied/cut assignments
        _paste_many_clipboard_assignments($schedule_id, $sel_session_list, $clipboard['assignments']);
      }
    } else if (isset($clipboard['exception'])) {
      // only 1 exception may be copied at a time
      _paste_one_exception_to_many_sessions($schedule_id, $sel_session_list, $clipboard['exception']);
    }
    // return true from here even if it fails, browser needs to refresh after this step
    return drupal_json_output(array('response' => True));
  }

  // nothing changed, do not refresh the browser
  //return drupal_json_output(array('response' => True));		
  return drupal_json_output(array('response' => False));
}

/*
 * 		move_schedule_items()
 *
 * 		callback function after schedule grid drag & drop move, assignments & exceptions are updated
 * 		then browser page is reloaded
 *    IN:       $schedule_id
 *    POST_IN:  $_POST['selected']    // pipe-delim string of elements to update, each item subdiv by commas
 *              formatted as:  type[A/E],id,sess_id,new_date
 */

function move_schedule_items($schedule_id) {

  $sessions = $_POST['selected'];
  define('POS_ELEM_TYPE', 0);
  define('POS_ELEM_ID', 1);
  define('POS_SESSION_ID', 2);
  define('POS_DATE', 3);
  define('POS_PORTION', 4);
  define('POS_BEGIN_TIME', 5);
  define('POS_END_TIME', 6);
  $bBlocked = False;
  $bOverlap = False;
  $valid_idx = array();
  $msg = '';

  $msg_conflict =
          '<p>' .
          t('Conflicts exist with existing assignments or exceptions. ' .
                  'Unable to update all to new schedule locations') .
          '</p>';

  $session_list = explode('|', $sessions);
  
  $timezone = _get_schedule_timezone($schedule_id);

  $moved_sessions = array();
  $msg = '';
  $sessValidator = new SessionValidator();


  foreach ($session_list as $s) {
    
    $sess = explode(',', $s);
    $sess[POS_DATE] = str_replace('-', '/', $sess[POS_DATE]);
    $sdate = _st_format_schedule_timestamp($sess[POS_DATE], $timezone);

    if (strlen($sess[POS_ELEM_TYPE]) &&
            strlen($sess[POS_ELEM_ID]) &&
            strlen($sess[POS_SESSION_ID]) &&
            $sdate) {

      $moved_sessions[] = array(
        'type' => $sess[POS_ELEM_TYPE],
        'id' => (int) $sess[POS_ELEM_ID],
        'new_sess_id' => (int) $sess[POS_SESSION_ID],
        'new_date' => $sdate,
        'portion' => $sess[POS_PORTION],
        'begin_time' => $sess[POS_BEGIN_TIME],
        'end_time' => $sess[POS_END_TIME],
      );
    }
  }

  // Validate the entire move first
  // Once all are cleared to move, then do the move
  // otherwise, if one fails it could impact the locations fo the other items
  for ($m = 0; $m < count($moved_sessions); $m++) {
    $bFound = False;


    $except_id = 0;
    $assign_id = 0;

    if ('A' == $moved_sessions[$m]['type']) {
      $assign_id = $moved_sessions[$m]['id'];
    } 
    else if ('E' == $moved_sessions[$m]['type']) {
      $except_id = $moved_sessions[$m]['id'];
    } 
    else {
      break;
    }

    $sessValidator->init($assign_id, $except_id, $schedule_id, $moved_sessions[$m]['new_sess_id'], $moved_sessions[$m]['new_date']);

    $result = $sessValidator->checkForConflict($moved_sessions[$m]['type'], 
                                               $moved_sessions[$m]['begin_time'],
                                               $moved_sessions[$m]['end_time']);


    if ($result == SessionValidator::SV_CONFLICT) {

      $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>';
                                   
      }

      $conflict_assigns = $sessValidator->getAssignmentConflicts();
      foreach ($conflict_assigns as $a) {
        $conflict_text .= '<li>' . '[' . $a->assignment_id . '] ' . $a->session_name . ':  <br />' .
                                    $a->begin_timestamp_f . ' - ' . $a->end_timestamp_f . '</li>';
      }

      $conflict_excepts = $sessValidator->getExceptionConflicts();
      foreach ($conflict_excepts as $ex) {
        $conflict_text .= '<li>' . '[' . $ex->exception_id . '] ' . $ex->exception_desc . ':  <br />' . 
                                    $ex->begin_timestamp_f . ' - ' . $ex->end_timestamp_f . '</li>';
      }

      if ((( 1 == count($conflict_assigns) ) && isset($conflict_assigns[0]->assignment_id) ) ||
          (( 1 == count($conflict_excepts) ) && isset($conflict_excepts[0]->exception_id))) {

        //check if the conflict is with an item that is being moved right now
        for ($i = 0; $i < count($moved_sessions); $i++) {

          if ((isset($conflict_assigns[0]->assignment_id) &&
                  $moved_sessions[$i]['type'] == 'A' &&
                  ($moved_sessions[$i]['id'] == $conflict_assigns[0]->assignment_id) ) ||
                  (isset($conflict_excepts[0]->exception_id) &&
                  $moved_sessions[$i]['type'] == 'E' &&
                  ($moved_sessions[$i]['id'] == $conflict_excepts[0]->exception_id) )) {
            $bFound = True;
            $bOverlap = True;
            break;
          }
        }
      }
    }

    if (($result == SessionValidator::SV_NOCONFLICT) || $bFound) {
      $valid_idx[] = $m;
    } 
    else {
      if (!strlen($msg)) {
        $msg = $msg_conflict;
      }
      $msg .= (strlen($conflict_text) ? '<ul>' . $conflict_text . '</ul>' : '' );
      $bBlocked = True;
    }
  } // end For Loop

  /*
   * ARE THESE VALID TO MOVE??
   * check the flags to see if it's ok to move the items
   * if a group of items overlap themselves in the move and one is blocked,
   * the whole move needs to be blocked
   *
   * if one item in the group is blocked, but not an overlap situation,
   * allow the move for the remaining items that are not blocked
   */
  if (count($valid_idx) &&
          (!$bBlocked || ($bBlocked && !$bOverlap))) {

    foreach ($valid_idx as $v) {
      if ('A' == $moved_sessions[$v]['type']) {
        _update_assignment_location($moved_sessions[$v]['id'], 
                                    $schedule_id, 
                                    $moved_sessions[$v]['new_sess_id'], 
                                    $moved_sessions[$v]['new_date'],
                                    $moved_sessions[$v]['portion'],
                                    $moved_sessions[$v]['begin_time'],
                                    $moved_sessions[$v]['end_time'] );
      } 
      else if ('E' == $moved_sessions[$v]['type']) {
        _update_exception_location($moved_sessions[$v]['id'], 
                                   $schedule_id, 
                                   $moved_sessions[$v]['new_sess_id'], 
                                   $moved_sessions[$v]['new_date'],
                                   $moved_sessions[$v]['portion'],
                                   $moved_sessions[$v]['begin_time'],
                                   $moved_sessions[$v]['end_time']  );
      }
    }
  }

  return drupal_json_output(array(
      'response' => True,
      'message' => $msg,
  ));
}

