/**
  * Calculate costs from posted values
  * @param  array $posted
  * @return string cost
  */
 public function calculate_booking_cost($posted)
 {
     if (!empty($this->booking_cost)) {
         return $this->booking_cost;
     }
     // Get costs
     $costs = $this->product->get_costs();
     // Get posted data
     $data = $this->get_posted_data($posted);
     $validate = $this->is_bookable($data);
     if (is_wp_error($validate)) {
         return $validate;
     }
     $base_cost = max(0, $this->product->wc_booking_cost);
     $base_block_cost = max(0, $this->product->wc_booking_base_cost);
     $total_block_cost = 0;
     $person_block_costs = 0;
     // See if we have an auto_assigned_resource_id
     if (isset($this->auto_assigned_resource_id)) {
         $data['_resource_id'] = $this->auto_assigned_resource_id;
     }
     // Get resource cost
     if (isset($data['_resource_id'])) {
         $resource = $this->product->get_resource($data['_resource_id']);
         $base_block_cost += $resource->get_block_cost();
         $base_cost += $resource->get_base_cost();
     }
     // Potentially increase costs if dealing with persons
     if (!empty($data['_persons'])) {
         if ($this->product->has_person_types()) {
             foreach ($data['_persons'] as $person_id => $person_count) {
                 $person_cost = get_post_meta($person_id, 'cost', true);
                 $person_block_cost = get_post_meta($person_id, 'block_cost', true);
                 // Only a single cost - multiplication comes later if wc_booking_person_cost_multiplier is enabled
                 if ($person_count > 0) {
                     if ($person_cost > 0) {
                         $base_cost += $person_cost * $person_count;
                     }
                     if ($person_block_cost > 0) {
                         $person_block_costs += $person_block_cost * $person_count;
                     }
                 }
             }
         }
     }
     $this->applied_cost_rules = array();
     $block_duration = $this->product->get_duration();
     $block_unit = $this->product->get_duration_unit();
     $blocks_booked = isset($data['_duration']) ? absint($data['_duration']) : $block_duration;
     $block_timestamp = $data['_start_date'];
     if ($this->product->is_duration_type('fixed')) {
         $blocks_booked = ceil($blocks_booked / $block_duration);
     }
     // Evaluate costs for each booked block
     for ($block = 0; $block < $blocks_booked; $block++) {
         $block_cost = $base_block_cost + $person_block_costs;
         $block_start_time_offset = $block * $block_duration;
         $block_end_time_offset = ($block + 1) * $block_duration;
         $block_start_time = $this->get_formatted_times(strtotime("+{$block_start_time_offset} {$block_unit}", $block_timestamp));
         $block_end_time = $this->get_formatted_times(strtotime("+{$block_end_time_offset} {$block_unit}", $block_timestamp));
         foreach ($costs as $rule_key => $rule) {
             $type = $rule[0];
             $rules = $rule[1];
             if (strrpos($type, 'time') === 0) {
                 if (!in_array($this->product->get_duration_unit(), array('minute', 'hour'))) {
                     continue;
                 }
                 if (!empty($rules['day'])) {
                     if ($rules['day'] != $block_start_time['day_of_week']) {
                         continue;
                     }
                 }
                 $rule_start_time_hi = date("YmdHi", strtotime(str_replace(':', '', $rules['from']), $block_start_time['timestamp']));
                 $rule_end_time_hi = date("YmdHi", strtotime(str_replace(':', '', $rules['to']), $block_start_time['timestamp']));
                 $matched = false;
                 // Reverse time rule - The end time is tomorrow e.g. 16:00 today - 12:00 tomorrow
                 if ($rule_end_time_hi <= $rule_start_time_hi) {
                     if ($block_end_time['time'] > $rule_start_time_hi) {
                         $matched = true;
                     }
                     if ($block_start_time['time'] >= $rule_start_time_hi && $block_end_time['time'] >= $rule_end_time_hi) {
                         $matched = true;
                     }
                     if ($block_start_time['time'] <= $rule_start_time_hi && $block_end_time['time'] <= $rule_end_time_hi) {
                         $matched = true;
                     }
                     // Normal rule
                 } else {
                     if ($block_start_time['time'] >= $rule_start_time_hi && $block_end_time['time'] <= $rule_end_time_hi) {
                         $matched = true;
                     }
                 }
                 if ($matched) {
                     $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                     $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key);
                 }
             } else {
                 switch ($type) {
                     case 'months':
                     case 'weeks':
                     case 'days':
                         $check_date = $block_start_time['timestamp'];
                         while ($check_date < $block_end_time['timestamp']) {
                             $checking_date = $this->get_formatted_times($check_date);
                             $date_key = $type == 'days' ? 'day_of_week' : substr($type, 0, -1);
                             if (isset($rules[$checking_date[$date_key]])) {
                                 $rule = $rules[$checking_date[$date_key]];
                                 $block_cost = $this->apply_cost($block_cost, $rule['block'][0], $rule['block'][1]);
                                 $base_cost = $this->apply_base_cost($base_cost, $rule['base'][0], $rule['base'][1], $rule_key);
                             }
                             $check_date = strtotime("+1 {$type}", $check_date);
                         }
                         break;
                     case 'custom':
                         $check_date = $block_start_time['timestamp'];
                         while ($check_date < $block_end_time['timestamp']) {
                             $checking_date = $this->get_formatted_times($check_date);
                             if (isset($rules[$checking_date['year']][$checking_date['month']][$checking_date['day']])) {
                                 $rule = $rules[$checking_date['year']][$checking_date['month']][$checking_date['day']];
                                 $block_cost = $this->apply_cost($block_cost, $rule['block'][0], $rule['block'][1]);
                                 $base_cost = $this->apply_base_cost($base_cost, $rule['base'][0], $rule['base'][1], $rule_key);
                             }
                             $check_date = strtotime("+1 day", $check_date);
                         }
                         break;
                     case 'persons':
                         if (!empty($data['_persons'])) {
                             if ($rules['from'] <= array_sum($data['_persons']) && $rules['to'] >= array_sum($data['_persons'])) {
                                 $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                                 $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key);
                             }
                         }
                         break;
                     case 'blocks':
                         if (!empty($data['_duration'])) {
                             if ($rules['from'] <= $data['_duration'] && $rules['to'] >= $data['_duration']) {
                                 $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                                 $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key);
                             }
                         }
                         break;
                 }
             }
         }
         $total_block_cost += $block_cost;
     }
     // Person multiplier mutliplies all costs
     $this->booking_cost = max(0, $total_block_cost + $base_cost);
     if (!empty($data['_persons'])) {
         if ('yes' === $this->product->wc_booking_person_cost_multiplier) {
             $this->booking_cost = $this->booking_cost * array_sum($data['_persons']);
         }
     }
     return apply_filters('booking_form_calculated_booking_cost', $this->booking_cost, $this, $posted);
 }
 /**
  * Calculate costs from posted values
  * @param  array $posted
  * @return string cost
  */
 public function calculate_booking_cost($posted)
 {
     if (!empty($this->booking_cost)) {
         return $this->booking_cost;
     }
     // Base costs
     $base_cost = max(0, $this->product->wc_booking_cost);
     $booking_cost = max(0, $this->product->wc_booking_base_cost);
     // Get costs
     $costs = $this->product->get_costs();
     // Get posted data
     $data = $this->get_posted_data($posted);
     $validate = $this->is_bookable($data);
     if (is_wp_error($validate)) {
         return $validate;
     }
     // See if we have an auto_assigned_resource_id
     if (isset($this->auto_assigned_resource_id)) {
         $data['_resource_id'] = $this->auto_assigned_resource_id;
     }
     // Get resource cost
     if (isset($data['_resource_id'])) {
         $resource = $this->product->get_resource($data['_resource_id']);
         $booking_cost += $resource->get_block_cost();
         $base_cost += $resource->get_base_cost();
     }
     // Get cost for date
     foreach ($costs as $rule) {
         $type = $rule[0];
         $rules = $rule[1];
         switch ($type) {
             case 'months':
                 $check_date = $data['_start_date'];
                 while ($check_date < $data['_end_date']) {
                     $month = date('n', $check_date);
                     if (isset($rules[$month])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules[$month]['block'][0], $rules[$month]['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1]);
                         break 2;
                     }
                     $check_date = strtotime("+1 day", $check_date);
                 }
                 break;
             case 'weeks':
                 $check_date = $data['_start_date'];
                 while ($check_date < $data['_end_date']) {
                     $year = date('Y', $check_date);
                     $month = date('n', $check_date);
                     $day = date('j', $check_date);
                     $week = absint(date('W', strtotime(implode('-', array($year, $month, $day)))));
                     if (isset($rules[$week])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules[$month]['block'][0], $rules[$month]['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules[$month]['base'][0], $rules[$month]['base'][1]);
                         break 2;
                     }
                     $check_date = strtotime("+1 day", $check_date);
                 }
                 break;
             case 'days':
                 $check_date = $data['_start_date'];
                 while ($check_date < $data['_end_date']) {
                     $year = date('Y', $check_date);
                     $month = date('n', $check_date);
                     $day = date('j', $check_date);
                     $day_of_week = absint(date('N', strtotime(implode('-', array($year, $month, $day)))));
                     if (isset($rules[$day_of_week])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules[$day_of_week]['block'][0], $rules[$day_of_week]['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules[$day_of_week]['base'][0], $rules[$day_of_week]['base'][1]);
                         break 2;
                     }
                     $check_date = strtotime("+1 day", $check_date);
                 }
                 break;
             case 'custom':
                 $check_date = $data['_start_date'];
                 while ($check_date < $data['_end_date']) {
                     $year = date('Y', $check_date);
                     $month = date('n', $check_date);
                     $day = date('j', $check_date);
                     if (isset($rules[$year][$month][$day])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules[$year][$month][$day]['block'][0], $rules[$year][$month][$day]['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules[$year][$month][$day]['base'][0], $rules[$year][$month][$day]['base'][1]);
                         break 2;
                     }
                     $check_date = strtotime("+1 day", $check_date);
                 }
                 break;
             case 'time':
                 if (in_array($this->product->get_duration_unit(), array('minute', 'hour'))) {
                     $booked_from = date("Hi", $data['_start_date']);
                     $booked_to = date("Hi", $data['_end_date']);
                     if ($booked_from >= str_replace(':', '', $rules['from']) && $booked_to <= str_replace(':', '', $rules['to'])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1]);
                     }
                 }
                 break;
             case 'persons':
                 if (isset($data['_persons']) && $data['_persons']) {
                     if ($rules['from'] <= array_sum($data['_persons']) && $rules['to'] >= array_sum($data['_persons'])) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1]);
                     }
                 }
                 break;
             case 'blocks':
                 if (isset($data['_duration']) && $data['_duration']) {
                     if ($rules['from'] <= $data['_duration'] && $rules['to'] >= $data['_duration']) {
                         $booking_cost = $this->apply_cost($booking_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]);
                         $base_cost = $this->apply_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1]);
                     }
                 }
                 break;
         }
     }
     if (!empty($data['_persons'])) {
         // Person multiplier mutliplies all costs
         if ('yes' === $this->product->wc_booking_person_cost_multiplier) {
             $booking_cost = $booking_cost * array_sum($data['_persons']);
         }
         // Add base costs for person types if there are any set
         if ($this->product->has_person_types()) {
             foreach ($data['_persons'] as $person_id => $person_count) {
                 $person_cost = get_post_meta($person_id, 'cost', true);
                 // Only a single cost - multiplication comes later if wc_booking_person_cost_multiplier is enabled
                 if ($person_count > 0 && $person_cost > 0) {
                     $booking_cost += $person_cost * $person_count;
                 }
             }
         }
     }
     // Duration costs
     if (isset($data['_duration'])) {
         $booking_cost = $booking_cost * $data['_duration'];
     }
     $this->booking_cost = max(0, $booking_cost + $base_cost);
     return apply_filters('booking_form_calculated_booking_cost', $this->booking_cost, $this, $posted);
 }