<?php

/**
 * For the coding challenge for the position in Las Vegas through the Lucas Group.
 * @author David Cole, davidcole@davidcole.net
 */

class Paydate_Calculator
{

  
/** This function determines the first available due date following the funding of a loan.
    * The paydate will be at least 10 days in the future from the $fund_day. The
    * due_date will fall on a day that is a paydate based on their paydate model
    * specified by '$pay_span' unless the date must be adjusted forward to miss a
    * weekend or backward to miss a holiday
    * Holiday adjustment takes precedence over Weekend.
    *
    * @param unix_timestamp $fund_day The day the loan was funded.
    * @param array $holiday_array An array of unix timestamp's containing holidays.
    * @param string $pay_span A string representing the frequency at which the customer is paid. (weekly,bi-weekly,monthly)
    * @param unix_timestamp $pay_day A timestamp containing one of the customers paydays
    * @param bool $direct_deposit A boolean determining whether or not the customer receives their paycheck via direct deposit.
    * @return unix_timestamp A unix timestamp representing the determined due date.
  */
  
public function Calculate_Due_Date($fund_day$holiday_array$pay_span$pay_day$direct_deposit)
  {
    if (
$pay_day == null// For the first time here - run unless the ending pay day was less than ten days after the fund day
    
{
      
$pay_day $this->get_first_pay_day($fund_day$pay_span);
    }
    
$pay_day $this->adjust_for_direct_deposit($pay_day$direct_deposit);
    
$pay_day $this->adjust_for_holidays_and_weekends($pay_day'forward'$holiday_array);

    if (
$pay_day strtotime("+10 days"$fund_day))
    {
      
// Would ask for clarification on this point to make sure this is what is wanted
      // What I'm doing is reseting the $pay_day and getting the pay day after the next before adjusting for direct deposit, weekends, or holidays
      
$pay_day $this->get_first_pay_day($fund_day$pay_span);
      
$pay_day $this->get_next_pay_day($pay_day$pay_span); // To get the _next_ pay_day
      
$pay_day $this->Calculate_Due_Date($fund_day$holiday_array$pay_span$pay_day$direct_deposit);
    }
    return 
$pay_day;
  }

  
/** Gets the pay date from the fund date based on the pay span.
    *
    * @param unix_timestamp $fund_day The day the loan was funded.
    * @param string $pay_span A string representing the frequency at which the customer is paid. (weekly,bi-weekly,monthly)
    * @return unix_timestamp A unix timestamp representing the modified pay day.
  */
  
private function get_first_pay_day($fund_day$pay_span)
  {
    
// Unnecessary now, but allows for additional processing later
    
return $this->get_next_pay_day($fund_day$pay_span);
  }

  
/** Gets the pay date from the given start date based on the pay span.
    *
    * @param unix_timestamp $start_day The day to calculate the pay date from.
    * @param string $pay_span A string representing the frequency at which the customer is paid. (weekly,bi-weekly,monthly)
    * @return unix_timestamp A unix timestamp representing the modified pay day.
  */
  
private function get_next_pay_day($start_day$pay_span)
  {
    switch (
$pay_span) {
      case 
'weekly':
        
$pay_day strtotime("+1 week"$start_day);
        break;
      case 
'bi-weekly':
        
$pay_day strtotime("+2 weeks"$start_day);
        break;
      case 
'monthly':
        
$pay_day strtotime("+1 month"$start_day);
        break;
    }
    return 
$pay_day;
  }

  
/** Adds a day if there is no direct deposit.
    *
    * @param unix_timestamp $pay_day The pay day calculated thus far.
    * @param bool $direct_deposit A boolean determining whether or not the customer receives their paycheck via direct deposit.
    * @return unix_timestamp A unix timestamp representing the modified pay day.
  */
  
private function adjust_for_direct_deposit($pay_day$direct_deposit)
  {
    if (!
$direct_deposit)
    {
      
$pay_day strtotime("+1 day"$pay_day);
    }
    return 
$pay_day;
  }

  
/** The date is be adjusted forward to miss a weekend or backward to miss a holiday.
    * Holiday adjustment takes precedence over weekend.
    *
    * @param unix_timestamp $pay_day The pay day calculated thus far.
    * @param string $loop_type A string representing whether to adjust the date forward or reverse. (forward, reverse)
    * @param array $holiday_array An array of unix timestamp's containing holidays.
    * @return unix_timestamp A unix timestamp representing the modified pay day.
  */
  
private function adjust_for_holidays_and_weekends($pay_day$loop_type$holiday_array)
  {
    if (
in_array($pay_day$holiday_array))
    {
      
$loop_type 'reverse';
      
$pay_day strtotime("-1 day"$pay_day);
      
$pay_day $this->adjust_for_holidays_and_weekends($pay_day$loop_type$holiday_array);
    }
    else if (
$this->is_weekend($pay_day))
    {
      
$pay_day = ($loop_type == 'forward') ? strtotime("+1 day"$pay_day) : strtotime("-1 day"$pay_day);
      
$pay_day $this->adjust_for_holidays_and_weekends($pay_day$loop_type$holiday_array);
    }
    return 
$pay_day;
  }

  
/** Determines whether the pay day is on a weekend.
    *
    * @param unix_timestamp $pay_day The pay day calculated thus far.
    * @return boolean True if the pay day falls on a weekend.
  */
  
private function is_weekend($pay_day)
  {
    return (
date('N'$pay_day) > 5);
  }

}

?>