<?php
namespace SabaiApps\Directories\Component\Field\Filter;

use SabaiApps\Directories\Component\Field\IField;
use SabaiApps\Directories\Component\Field\Query;
use SabaiApps\Directories\Application;

class DateRangeListFilter extends AbstractOptionFilter {

	protected $_minMaxSeparator = ',';

	public function __construct( Application $application, $name ) {
		parent::__construct( $application, $name );
		$this->_label           = __( 'Date range list', 'directories-pro' );
		$this->_fieldTypes      = array( 'date', 'entity_published', 'entity_modified' );
		$this->_defaultSettings = array(
			'ranges'    => array(
				'options'       => null,
				'custom_ranges' => null,
			),
			'form_type' => 'radios',
		);
	}

	public function fieldFilterSettingsForm( IField $field, array $settings, array $parents = array() ) {
		$form = parent::fieldFilterSettingsForm( $field, $settings, $parents ) + array(
			'ranges' => array(
				'#weight'       => 2,
				'options'       => array(
					'#type'          => 'sortablecheckboxes',
					'#title'         => __( 'Date ranges', 'directories-pro' ),
					'#default_value' => isset( $settings['ranges']['options'] ) ? $settings['ranges']['options'] : null,
					'#options'       => $this->_getDateRangeOptions(),
					'#columns'       => 3,
				),
				'custom'        => array(
					'#type'          => 'checkbox',
					'#title'         => __( 'Custom', 'directories-pro' ),
					'#default_value' => ! empty( $settings['ranges']['custom'] ),
					'#switch'        => false,
				),
				'custom_ranges' => array(
					'#type'          => 'rangelist',
					'#default_value' => $settings['ranges']['custom_ranges'],
					'#input_type'    => 'text',
					'#min_title'     => _x( 'From', 'date', 'directories-pro' ),
					'#max_title'     => _x( 'To', 'date', 'directories-pro' ),
					'#states'        => array(
						'visible' => array(
							sprintf( 'input[name="%s[]"]', $this->_application->Form_Form_fieldName( array_merge( $parents, array( 'ranges', 'custom' ) ) ) ) => array(
								'type'  => 'checked',
								'value' => true,
							),
						),
					),
					'#description'   => __( 'From and To values must be recognizable by the PHP strtotime function.', 'directories-pro' ),
				),
			),
		);
		if ( $other_date_fields = $this->_application->Entity_Field_options(
			$field->getBundleName(),
			array(
				'type'        => 'date',
				'exclude'     => array( $field->getFieldName() ),
				'empty_value' => '',
			)
		) ) {
			$form['end_date_field'] = array(
				'#type'          => 'select',
				'#title'         => __( 'End date field', 'directories-pro' ),
				'#description'   => __( 'Select another field to use as end date.', 'directories-pro' ),
				'#default_value' => $settings['end_date_field'],
				'#options'       => $other_date_fields,
			);
		}

		return $form;
	}

	protected function _getOptions( IField $field, array $settings, &$noEscape = false ) {
		$options_available = $this->_getDateRangeOptions();
		$options           = array();
		foreach ( $settings['ranges']['options'] as $key ) {
			if ( isset( $options_available[ $key ] ) ) {
				$options[ $key ] = $options_available[ $key ];
			}
		}
		if ( ! empty( $settings['ranges']['custom'] )
			&& ! empty( $settings['ranges']['custom_ranges'] )
		) {
			foreach ( $settings['ranges']['custom_ranges'] as $range ) {
				$key             = $range['min'] . $this->_minMaxSeparator . $range['max'];
				$options[ $key ] = $range['label'];
			}
		}

		return $options;
	}

	protected function _getFacetOptions( IField $field, array $settings ) {
		$ranges = $this->_getDateRangeValues( $settings['ranges']['options'] );

		if ( ! empty( $settings['ranges']['custom'] )
			&& ! empty( $settings['ranges']['custom_ranges'] )
		) {
			foreach ( $settings['ranges']['custom_ranges'] as $range ) {
				if ( ! is_numeric( $range['min'] ) ) {
					if ( strlen( $range['min'] ) ) {
						if ( false === $min = $this->_strtotime( $range['min'] ) ) {
							continue;
						}
					} else {
						$min = '';
					}
				} else {
					$min = $range['min'];
				}
				if ( ! is_numeric( $range['max'] ) ) {
					if ( strlen( $range['max'] ) ) {
						if ( false === $max = $this->_strtotime( $range['max'] ) ) {
							continue;
						}
					} else {
						$max = '';
					}
				} else {
					$max = $range['max'];
				}
				if ( $min && $max && $min > $max ) {
					continue;
				}

				$key            = $range['min'] . $this->_minMaxSeparator . $range['max'];
				$ranges[ $key ] = array(
					'min' => $min,
					'max' => $max,
				);
			}
		}

		return array(
			'facet_type' => 'range',
			'column'     => $this->_valueColumn,
			'ranges'     => $ranges,
		);
	}

	public function fieldFilterDoFilter( Query $query, IField $field, array $settings, $value, array &$sorts ) {
		if ( ! $ranges = $this->_getDateRangeValues( $value ) ) {
			return;
		}

		if ( empty( $settings['end_date_field'] )
			|| ( ! $end_date_field = $this->_application->Entity_Field( $field->getBundleName(), $settings['end_date_field'] ) )
		) {
			$end_date_field = $field;
		}

		if ( $settings['type'] !== 'checkboxes'
			|| count( $ranges ) === 1
		) {
			$range = array_shift( $ranges );
			if ( isset( $range['min'] ) ) {
				$query->fieldIsOrGreaterThan( $end_date_field, $range['min'] );
			}
			if ( isset( $range['max'] ) ) {
				$query->fieldIsSmallerThan( $field, $range['max'] );
			}
			return;
		}

		if ( $settings['andor'] === 'OR' ) {
			$query->startCriteriaGroup( 'OR' );
			foreach ( $ranges as $range ) {
				$query->startCriteriaGroup();
				if ( isset( $range['min'] ) ) {
					$query->fieldIsOrGreaterThan( $end_date_field, $range['min'] );
				}
				if ( isset( $range['max'] ) ) {
					$query->fieldIsOrSmallerThan( $field, $range['max'] );
				}
				$query->finishCriteriaGroup();
			}
			$query->finishCriteriaGroup();
		} else {
			foreach ( $ranges as $range ) {
				if ( isset( $range['min'] ) ) {
					$query->fieldIsOrGreaterThan( $end_date_field, $range['min'] );
				}
				if ( isset( $range['max'] ) ) {
					$query->fieldIsOrSmallerThan( $field, $range['max'] );
				}
			}
		}
	}

	protected function _getDateRangeOptions() {
		return $this->_application->Filter(
			'field_filter_date_range_options',
			array(
				'today'        => __( 'Today', 'directories-pro' ),
				'yesterday'    => __( 'Yesterday', 'directories-pro' ),
				'tomorrow'     => __( 'Tomorrow', 'directories-pro' ),
				'last_2_days'  => sprintf( __( 'Last %d days', 'directories-pro' ), 2 ),
				'last_3_days'  => sprintf( __( 'Last %d days', 'directories-pro' ), 3 ),
				'last_5_days'  => sprintf( __( 'Last %d days', 'directories-pro' ), 5 ),
				'this_week'    => __( 'This week', 'directories-pro' ),
				'last_week'    => __( 'Last week', 'directories-pro' ),
				'next_week'    => __( 'Next week', 'directories-pro' ),
				'this_month'   => __( 'This month', 'directories-pro' ),
				'last_month'   => __( 'Last month', 'directories-pro' ),
				'next_month'   => __( 'Next month', 'directories-pro' ),
				'this_year'    => __( 'This year', 'directories-pro' ),
				'last_year'    => __( 'Last year', 'directories-pro' ),
				'next_year'    => __( 'Next year', 'directories-pro' ),
				'all_upcoming' => __( 'All upcoming', 'directories-pro' ),
				'all_past'     => __( 'All past', 'directories-pro' ),
			)
		);
	}

	protected function _getDateRangeValues( $ranges ) {
		$ret = array();
		foreach ( (array) $ranges as $range ) {
			switch ( $range ) {
				case 'today':
					$ret[ $range ] = array(
						'min' => $today = $this->_strtotime( 'today' ),
						'max' => $today + 86399,
					);
					break;
				case 'this_week':
					$ret[ $range ] = array(
						'min' => $this_week = $this->_strtotime( 'this week 00:00:00' ),
						'max' => $this_week + ( 86400 * 7 ) - 1,
					);
					break;
				case 'this_month':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'first day of this month 00:00:00' ),
						'max' => $this->_strtotime( 'last day of this month 23:59:59' ),
					);
					break;
				case 'this_year':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'this year January 1st 00:00:00' ),
						'max' => $this->_strtotime( 'this year December 31st 23:59:59' ),
					);
					break;
				case 'yesterday':
					$ret[ $range ] = array(
						'min' => $yesterday = $this->_strtotime( 'yesterday' ),
						'max' => $yesterday + 86399,
					);
					break;
				case 'last_2_days':
					$ret[ $range ] = array(
						'min' => $two_days_ago = $this->_strtotime( '-2 days' ),
						'max' => $two_days_ago + ( 86400 * 2 ) - 1,
					);
					break;
				case 'last_3_days':
					$ret[ $range ] = array(
						'min' => $five_days_ago = $this->_strtotime( '-3 days' ),
						'max' => $five_days_ago + ( 86400 * 3 ) - 1,
					);
					break;
				case 'last_5_days':
					$ret[ $range ] = array(
						'min' => $five_days_ago = $this->_strtotime( '-5 days' ),
						'max' => $five_days_ago + ( 86400 * 5 ) - 1,
					);
					break;
				case 'last_week':
					$ret[ $range ] = array(
						'min' => $last_week = $this->_strtotime( 'last week 00:00:00' ),
						'max' => $last_week + ( 86400 * 7 ) - 1,
					);
					break;
				case 'last_month':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'first day of last month 00:00:00' ),
						'max' => $this->_strtotime( 'last day of last month 23:59:59' ),
					);
					break;
				case 'last_year':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'last year January 1st 00:00:00' ),
						'max' => $this->_strtotime( 'last year December 31st 23:59:59' ),
					);
					break;
				case 'tomorrow':
					$ret[ $range ] = array(
						'min' => $tomorrow = $this->_strtotime( 'tomorrow' ),
						'max' => $tomorrow + 86399,
					);
					break;
				case 'next_week':
					$ret[ $range ] = array(
						'min' => $next_week = $this->_strtotime( 'next week 00:00:00' ),
						'max' => $next_week + ( 86400 * 7 ) - 1,
					);
					break;
				case 'next_month':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'first day of next month 00:00:00' ),
						'max' => $this->_strtotime( 'last day of next month 23:59:59' ),
					);
					break;
				case 'next_year':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( 'next year January 1st 00:00:00' ),
						'max' => $this->_strtotime( 'next year December 31st 23:59:59' ),
					);
					break;
				case 'all_upcoming':
					$ret[ $range ] = array(
						'min' => $this->_strtotime( time() ),
						'max' => null,
					);
					break;
				case 'all_past':
					$ret[ $range ] = array(
						'min' => null,
						'max' => $this->_strtotime( time() ),
					);
					break;
				default:
					if ( $_range = explode( $this->_minMaxSeparator, $range ) ) {
						$ret[ $range ] = array(
							'min' => strlen( $_range[0] ) ? $this->_strtotime( $_range[0] ) : null,
							'max' => strlen( $_range[1] ) ? $this->_strtotime( $_range[1] ) : null,
						);
					}
			}
		}
		return $ret;
	}

	protected function _strtotime( $str ) {
		return $this->_application->System_Date_strToTime( $str );
	}
}
