function getShiftData($user_date_id = NULL, $user_id = NULL, $epoch = NULL, $filter = NULL, $tmp_punch_control_obj = NULL)
 {
     global $profiler;
     $profiler->startTimer('PayPeriodScheduleFactory::getShiftData()');
     if (is_numeric($user_date_id) and $user_date_id > 0) {
         $user_id = $epoch = NULL;
     }
     if ($user_date_id == '' and $user_id == '' and $epoch == '') {
         return FALSE;
     }
     //Debug::text('User Date ID: '. $user_date_id .' User ID: '. $user_id .' TimeStamp: '. TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
     $new_shift_trigger_time = $this->getNewDayTriggerTime();
     $plf = new PunchListFactory();
     if ($user_date_id != '') {
         $plf->getByUserDateId($user_date_id);
     } else {
         //Get punches by time stamp.
         $punch_control_id = 0;
         if (is_object($tmp_punch_control_obj)) {
             $punch_control_id = $tmp_punch_control_obj->getId();
         }
         $plf->getShiftPunchesByUserIDAndEpoch($user_id, $epoch, $punch_control_id, $this->getMaximumShiftTime());
         unset($punch_control_id);
     }
     Debug::text('Punch Rows: ' . $plf->getRecordCount() . ' UserID: ' . $user_id . ' Date: ' . TTDate::getDate('DATE+TIME', $epoch) . '(' . $epoch . ') MaximumShiftTime: ' . $this->getMaximumShiftTime(), __FILE__, __LINE__, __METHOD__, 10);
     //Debug::Arr($punches, ' Punches: ', __FILE__, __LINE__, __METHOD__, 10);
     if ($plf->getRecordCount() > 0) {
         $shift = 0;
         $i = 0;
         $nearest_shift_id = 0;
         $nearest_punch_difference = FALSE;
         $prev_punch_obj = FALSE;
         foreach ($plf as $p_obj) {
             //Debug::text('Shift: '. $shift .' Punch ID: '. $p_obj->getID() .' Punch Control ID: '. $p_obj->getPunchControlID() .' TimeStamp: '. TTDate::getDate('DATE+TIME', $p_obj->getTimeStamp() ), __FILE__, __LINE__, __METHOD__, 10);
             //If we're editing a punch, we need to use the object passed to this function instead of the one
             //from the database.
             if ($epoch == NULL) {
                 //If user_date_id is passed without epoch, set epoch to the first punch we find.
                 $epoch = $p_obj->getTimeStamp();
             }
             if (isset($prev_punch_arr) and $p_obj->getTimeStamp() > $prev_punch_arr['time_stamp']) {
                 $shift_data[$shift]['previous_punch_key'] = $i - 1;
                 if ($shift_data[$shift]['previous_punch_key'] < 0) {
                     $shift_data[$shift]['previous_punch_key'] = NULL;
                 }
             }
             //Determine if a non-saved PunchControl object was passed, and if so, match the IDs to use that instead.
             if (is_object($tmp_punch_control_obj) and $p_obj->getPunchControlID() == $tmp_punch_control_obj->getId()) {
                 Debug::text('Passed non-saved punch control object that matches, using that instead... Using ID: ' . (int) $tmp_punch_control_obj->getId(), __FILE__, __LINE__, __METHOD__, 10);
                 $punch_control_obj = $tmp_punch_control_obj;
             } else {
                 $punch_control_obj = $p_obj->getPunchControlObject();
             }
             //Can't use PunchControl object total_time because the record may not be saved yet when editing
             //an already existing punch.
             //When editing, simply pass the existing PunchControl object to this function so we can
             //use it instead of the one in the database perhaps?
             $total_time = $punch_control_obj->getTotalTime();
             /*
             				//We can't skip records with total_time == 0, because then when deleting one of the two
             				//punches in a pair, the remaining punch is ignored and causing punches to jump around between days in some cases.
             				if ( $total_time == 0 ) {
             					Debug::text('Total time is 0, skipping this punch control object...', __FILE__, __LINE__, __METHOD__, 10);
             					//continue;
             				}
             */
             if ($i > 0 and isset($shift_data[$shift]['last_out']) and ($p_obj->getStatus() == 10 or $p_obj->getStatus() == $prev_punch_arr['status_id'])) {
                 Debug::text('Checking for new shift...', __FILE__, __LINE__, __METHOD__, 10);
                 if ($p_obj->getTimeStamp() - $shift_data[$shift]['last_out']['time_stamp'] > $new_shift_trigger_time) {
                     $shift++;
                 }
             }
             if (!isset($shift_data[$shift]['total_time'])) {
                 $shift_data[$shift]['total_time'] = 0;
             }
             $punch_day_epoch = TTDate::getBeginDayEpoch($p_obj->getTimeStamp());
             if (!isset($shift_data[$shift]['total_time_per_day'][$punch_day_epoch])) {
                 $shift_data[$shift]['total_time_per_day'][$punch_day_epoch] = 0;
             }
             //Determine which shift is closest to the given epoch.
             $punch_difference_from_epoch = abs($epoch - $p_obj->getTimeStamp());
             if ($nearest_punch_difference === FALSE or $punch_difference_from_epoch < $nearest_punch_difference) {
                 Debug::text('Nearest Shift Determined to be: ' . $shift . ' Nearest Punch Diff: ' . (int) $nearest_punch_difference . ' Punch Diff: ' . $punch_difference_from_epoch, __FILE__, __LINE__, __METHOD__, 10);
                 $nearest_shift_id = $shift;
                 $nearest_punch_difference = $punch_difference_from_epoch;
             }
             $punch_arr = array('id' => $p_obj->getId(), 'punch_control_id' => $p_obj->getPunchControlId(), 'user_date_id' => $punch_control_obj->getUserDateID(), 'time_stamp' => $p_obj->getTimeStamp(), 'status_id' => $p_obj->getStatus(), 'type_id' => $p_obj->getType());
             $shift_data[$shift]['punches'][] = $punch_arr;
             $shift_data[$shift]['punch_control_ids'][] = $p_obj->getPunchControlId();
             if ($punch_control_obj->getUserDateID() != FALSE) {
                 $shift_data[$shift]['user_date_ids'][] = $punch_control_obj->getUserDateID();
             }
             $shift_data[$shift]['span_midnight'] = FALSE;
             if (!isset($shift_data[$shift]['first_in']) and $p_obj->getStatus() == 10) {
                 //Debug::text('First In -- Punch ID: '. $p_obj->getID() .' Punch Control ID: '. $p_obj->getPunchControlID() .' TimeStamp: '. TTDate::getDate('DATE+TIME', $p_obj->getTimeStamp() ), __FILE__, __LINE__, __METHOD__, 10);
                 $shift_data[$shift]['first_in'] = $punch_arr;
             } elseif ($p_obj->getStatus() == 20) {
                 //Debug::text('Last Out -- Punch ID: '. $p_obj->getID() .' Punch Control ID: '. $p_obj->getPunchControlID() .' TimeStamp: '. TTDate::getDate('DATE+TIME', $p_obj->getTimeStamp() ), __FILE__, __LINE__, __METHOD__, 10);
                 $shift_data[$shift]['last_out'] = $punch_arr;
                 //Debug::text('Total Time: '. $total_time, __FILE__, __LINE__, __METHOD__, 10);
                 $shift_data[$shift]['total_time'] += $total_time;
                 //Check to see if the previous punch was on a different day then the current punch.
                 if (isset($prev_punch_arr) and is_array($prev_punch_arr) and ($p_obj->getStatus() == 20 and $prev_punch_arr['status_id'] != 20) and TTDate::doesRangeSpanMidnight($prev_punch_arr['time_stamp'], $p_obj->getTimeStamp()) == TRUE) {
                     Debug::text('Punch pair DOES span midnight', __FILE__, __LINE__, __METHOD__, 10);
                     $shift_data[$shift]['span_midnight'] = TRUE;
                     $total_time_for_each_day_arr = TTDate::calculateTimeOnEachDayBetweenRange($prev_punch_arr['time_stamp'], $p_obj->getTimeStamp());
                     if (is_array($total_time_for_each_day_arr)) {
                         foreach ($total_time_for_each_day_arr as $begin_day_epoch => $day_total_time) {
                             if (!isset($shift_data[$shift]['total_time_per_day'][$begin_day_epoch])) {
                                 $shift_data[$shift]['total_time_per_day'][$begin_day_epoch] = 0;
                             }
                             $shift_data[$shift]['total_time_per_day'][$begin_day_epoch] += $day_total_time;
                         }
                     }
                     unset($total_time_for_each_day_arr, $begin_day_epoch, $day_total_time, $prev_day_total_time);
                 } else {
                     $shift_data[$shift]['total_time_per_day'][$punch_day_epoch] += $total_time;
                 }
             }
             $prev_punch_arr = $punch_arr;
             $i++;
         }
         //Debug::Arr($shift_data, 'aShift Data:', __FILE__, __LINE__, __METHOD__, 10);
         if (isset($shift_data)) {
             //Loop through each shift to determine the day with the most time.
             foreach ($shift_data as $tmp_shift_key => $tmp_shift_data) {
                 krsort($shift_data[$tmp_shift_key]['total_time_per_day']);
                 //Sort by day first
                 arsort($shift_data[$tmp_shift_key]['total_time_per_day']);
                 //Sort by total time per day.
                 reset($shift_data[$tmp_shift_key]['total_time_per_day']);
                 $shift_data[$tmp_shift_key]['day_with_most_time'] = key($shift_data[$tmp_shift_key]['total_time_per_day']);
                 $shift_data[$tmp_shift_key]['punch_control_ids'] = array_unique($shift_data[$tmp_shift_key]['punch_control_ids']);
                 if (isset($shift_data[$tmp_shift_key]['user_date_ids'])) {
                     $shift_data[$tmp_shift_key]['user_date_ids'] = array_unique($shift_data[$tmp_shift_key]['user_date_ids']);
                 }
             }
             unset($tmp_shift_key, $tmp_shift_data);
             if ($filter == 'first_shift') {
                 //Only return first shift.
                 $shift_data = $shift_data[0];
             } elseif ($filter == 'last_shift') {
                 //Only return last shift.
                 $shift_data = $shift_data[$shift];
             } elseif ($filter == 'nearest_shift') {
                 $shift_data = $shift_data[$nearest_shift_id];
                 //Check to make sure the nearest shift is within the new shift trigger time of EPOCH.
                 if (isset($shift_data['first_in']['time_stamp'])) {
                     $first_in = $shift_data['first_in']['time_stamp'];
                 } elseif (isset($shift_data['last_out']['time_stamp'])) {
                     $first_in = $shift_data['last_out']['time_stamp'];
                 }
                 if (isset($shift_data['last_out']['time_stamp'])) {
                     $last_out = $shift_data['last_out']['time_stamp'];
                 } elseif (isset($shift_data['first_in']['time_stamp'])) {
                     $last_out = $shift_data['first_in']['time_stamp'];
                 }
                 if (TTDate::isTimeOverLap($epoch, $epoch, $first_in - $new_shift_trigger_time, $last_out + $new_shift_trigger_time) == FALSE) {
                     Debug::Text('Nearest shift is outside the new shift trigger time... Epoch: ' . $epoch . ' First In: ' . $first_in . ' Last Out: ' . $last_out . ' New Shift Trigger: ' . $new_shift_trigger_time, __FILE__, __LINE__, __METHOD__, 10);
                     return FALSE;
                 }
                 unset($first_in, $last_out);
             }
             $profiler->stopTimer('PayPeriodScheduleFactory::getShiftData()');
             //Debug::Arr($shift_data, 'bShift Data:', __FILE__, __LINE__, __METHOD__, 10);
             return $shift_data;
         }
     }
     $profiler->stopTimer('PayPeriodScheduleFactory::getShiftData()');
     return FALSE;
 }
 function getInCompletePunchControlIdByUserIdAndEpoch($user_id, $epoch, $status_id)
 {
     Debug::text(' Epoch: ' . TTDate::getDate('DATE+TIME', $epoch), __FILE__, __LINE__, __METHOD__, 10);
     if ($user_id == '') {
         return FALSE;
     }
     if ($epoch == '') {
         return FALSE;
     }
     $plf = new PunchListFactory();
     $plf->getShiftPunchesByUserIDAndEpoch($user_id, $epoch);
     if ($plf->getRecordCount() > 0) {
         //Check for gaps.
         $prev_time_stamp = 0;
         foreach ($plf as $p_obj) {
             if ($p_obj->getStatus() == 10) {
                 $punch_arr[$p_obj->getPunchControlId()]['in'] = $p_obj->getTimeStamp();
             } else {
                 $punch_arr[$p_obj->getPunchControlId()]['out'] = $p_obj->getTimeStamp();
             }
             if ($prev_time_stamp != 0) {
                 $prev_punch_arr[$p_obj->getTimeStamp()] = $prev_time_stamp;
             }
             $prev_time_stamp = $p_obj->getTimeStamp();
         }
         unset($prev_time_stamp);
         if (isset($prev_punch_arr)) {
             $next_punch_arr = array_flip($prev_punch_arr);
         }
         //Debug::Arr( $punch_arr, ' Punch Array: ', __FILE__, __LINE__, __METHOD__,10);
         //Debug::Arr( $next_punch_arr, ' Next Punch Array: ', __FILE__, __LINE__, __METHOD__,10);
         if (isset($punch_arr)) {
             $i = 0;
             foreach ($punch_arr as $punch_control_id => $data) {
                 $found_gap = FALSE;
                 Debug::text(' Iteration: ' . $i, __FILE__, __LINE__, __METHOD__, 10);
                 //Skip complete punch control rows.
                 if (isset($data['in']) and isset($data['out'])) {
                     Debug::text(' Punch Control ID is Complete: ' . $punch_control_id, __FILE__, __LINE__, __METHOD__, 10);
                 } else {
                     //Make sure we don't assign a In punch that comes AFTER an Out punch to the same pair.
                     //As well the opposite, an Out punch that comes BEFORE an In punch to the same pair.
                     if ($status_id == 10 and !isset($data['in']) and (isset($data['out']) and $epoch <= $data['out'])) {
                         Debug::text(' aFound Valid Gap...', __FILE__, __LINE__, __METHOD__, 10);
                         $found_gap = TRUE;
                     } elseif ($status_id == 20 and !isset($data['out']) and (isset($data['in']) and $epoch >= $data['in'])) {
                         Debug::text(' bFound Valid Gap...', __FILE__, __LINE__, __METHOD__, 10);
                         $found_gap = TRUE;
                     } else {
                         Debug::text(' No Valid Gap Found...', __FILE__, __LINE__, __METHOD__, 10);
                     }
                 }
                 if ($found_gap == TRUE) {
                     if ($status_id == 10) {
                         //In Gap
                         Debug::text(' In Gap...', __FILE__, __LINE__, __METHOD__, 10);
                         if (isset($prev_punch_arr[$data['out']])) {
                             Debug::text(' Punch Before In Gap... Range Start: ' . TTDate::getDate('DATE+TIME', $prev_punch_arr[$data['out']]) . ' End: ' . TTDate::getDate('DATE+TIME', $data['out']), __FILE__, __LINE__, __METHOD__, 10);
                             if ($prev_punch_arr[$data['out']] == $data['out'] or TTDate::isTimeOverLap($epoch, $epoch, $prev_punch_arr[$data['out']], $data['out'])) {
                                 Debug::text(' Epoch OverLaps, THIS IS GOOD!', __FILE__, __LINE__, __METHOD__, 10);
                                 Debug::text(' aReturning Punch Control ID: ' . $punch_control_id, __FILE__, __LINE__, __METHOD__, 10);
                                 $retval = $punch_control_id;
                                 break;
                                 //Without this adding mass punches fails in some basic circumstances because it loops and attaches to a later punch control
                             } else {
                                 Debug::text(' Epoch does not OverLaps, Cant attached to this punch_control!', __FILE__, __LINE__, __METHOD__, 10);
                             }
                         } else {
                             //No Punch After
                             Debug::text(' NO Punch Before In Gap...', __FILE__, __LINE__, __METHOD__, 10);
                             $retval = $punch_control_id;
                             break;
                         }
                     } else {
                         //Out Gap
                         Debug::text(' Out Gap...', __FILE__, __LINE__, __METHOD__, 10);
                         //Start: $data['in']
                         //End: $data['in']
                         if (isset($next_punch_arr[$data['in']])) {
                             Debug::text(' Punch After Out Gap... Range Start: ' . TTDate::getDate('DATE+TIME', $data['in']) . ' End: ' . TTDate::getDate('DATE+TIME', $next_punch_arr[$data['in']]), __FILE__, __LINE__, __METHOD__, 10);
                             if ($data['in'] == $next_punch_arr[$data['in']] or TTDate::isTimeOverLap($epoch, $epoch, $data['in'], $next_punch_arr[$data['in']])) {
                                 Debug::text(' Epoch OverLaps, THIS IS GOOD!', __FILE__, __LINE__, __METHOD__, 10);
                                 Debug::text(' bReturning Punch Control ID: ' . $punch_control_id, __FILE__, __LINE__, __METHOD__, 10);
                                 $retval = $punch_control_id;
                                 break;
                                 //Without this adding mass punches fails in some basic circumstances because it loops and attaches to a later punch control
                             } else {
                                 Debug::text(' Epoch does not OverLaps, Cant attached to this punch_control!', __FILE__, __LINE__, __METHOD__, 10);
                             }
                         } else {
                             //No Punch After
                             Debug::text(' NO Punch After Out Gap...', __FILE__, __LINE__, __METHOD__, 10);
                             $retval = $punch_control_id;
                             break;
                         }
                     }
                 }
                 $i++;
             }
         }
     }
     if (isset($retval)) {
         Debug::text(' Returning Punch Control ID: ' . $retval, __FILE__, __LINE__, __METHOD__, 10);
         return $retval;
     }
     Debug::text(' Returning FALSE No Valid Gaps Found...', __FILE__, __LINE__, __METHOD__, 10);
     //FALSE means no gaps in punch control rows found.
     return FALSE;
 }