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

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

class OptionFilter extends AbstractFilter
{
    protected $_emptyValue = '', $_valueColumn = 'value';
    
    protected function _fieldFilterInfo()
    {
        return [
            'field_types' => ['choice'],
            'default_settings' => [
                'type' => 'checkboxes',
                'columns' => 1,
                'show_more' => ['num' => 10],
                'andor' => 'AND',
                'default_text' => _x('Any', 'option', 'directories'),
                'no_fancy' => false,
                'show_icon' => true,
                'hide_empty' => false,
                'hide_count' => false,
                'sort_by_count' => false,
            ],
            'facetable' => true,
        ];
    }

    public function fieldFilterSettingsForm(IField $field, array $settings, array $parents = [])
    {
        $form = [
            'type' => [
                '#title' => __('Form field type', 'directories'),
                '#type' => 'select',
                '#options' => [
                    'checkboxes' => __('Checkboxes', 'directories'),
                    'radios' => __('Radio buttons', 'directories'),
                    'select' => __('Select list', 'directories')
                ],
                '#default_value' => $settings['type'],
                '#weight' => 5,
            ],
            'columns' => [
                '#type' => 'select',
                '#title' => __('Number of columns', 'directories'),
                '#options' => [1 => 1, 2 => 2, 3 => 3, 4 => 4],
                '#default_value' => $settings['columns'],
                '#weight' => 10,
                '#states' => [
                    'invisible' => [
                        sprintf('[name="%s[type]"]', $this->_application->Form_FieldName($parents)) => ['value' => 'select'],
                    ],
                ],
            ],
            'show_more' => [
                '#states' => [
                    'invisible' => [
                        sprintf('[name="%s[type]"]', $this->_application->Form_FieldName($parents)) => ['value' => 'select'],
                    ],
                ],
                'num' => [
                    '#type' => 'slider',
                    '#integer' => true,
                    '#min_value' => 0,
                    '#max_value' => 50,
                    '#min_text' => __('Show all', 'directories'),
                    '#title' => __('Number of options to display', 'directories'),
                    '#description' => __('If there are more options than the number specified, those options are hidden until "more" link is clicked.', 'directories'),
                    '#default_value' => $settings['show_more']['num'],
                ],
                '#weight' => 15,
            ],
            'andor' => [
                '#title' => __('Match any or all', 'directories'),
                '#type' => 'select',
                '#options' => ['OR' => __('Match any', 'directories'), 'AND' => __('Match all', 'directories')],
                '#default_value' => $settings['andor'],
                '#states' => [
                    'visible' => [
                        sprintf('[name="%s[type]"]', $this->_application->Form_FieldName($parents)) => ['value' => 'checkboxes'],
                    ],
                ],
                '#weight' => 20,
            ],
            'default_text' => [
                '#type' => 'textfield',
                '#title'=> __('Default text', 'directories'),
                '#default_value' => $settings['default_text'],
                '#placeholder' => _x('Any', 'option', 'directories'),
                '#weight' => 25,
                '#states' => [
                    'invisible' => [
                        sprintf('[name="%s[type]"]', $this->_application->Form_FieldName($parents)) => ['value' => 'checkboxes'],
                    ],
                ],
            ],
            'no_fancy' => [
                '#type' => 'checkbox',
                '#title' => __('Disable fancy dropdown', 'directories'),
                '#default_value' => !empty($settings['no_fancy']),
                '#weight' => 30,
                '#states' => [
                    'visible' => [
                        sprintf('[name="%s[type]"]', $this->_application->Form_FieldName($parents)) => ['value' => 'select'],
                    ],
                ],
            ],
        ];
        if ($this->_application->getComponent('View')->getConfig('filters', 'facet_count')) {
            $form += [
                'hide_empty' => [
                    '#type' => 'checkbox',
                    '#title' => __('Hide empty', 'directories'),
                    '#default_value' => !empty($settings['hide_empty']),
                    '#weight' => 34,
                ],
                'hide_count' => [
                    '#type' => 'checkbox',
                    '#title' => __('Hide count', 'directories'),
                    '#default_value' => $settings['hide_count'],
                    '#weight' => 35,
                ],
                'sort_by_count' => [
                    '#type' => 'checkbox',
                    '#title' => __('Sort options by count', 'directories'),
                    '#default_value' => $settings['sort_by_count'],
                    '#weight' => 40,
                ],
            ];
        }
        return $form;
    }
    
    public function fieldFilterForm(IField $field, $filterName, array $settings, $request = null, Entity\Type\Query $query = null, array $current = null, array $parents = [])
    {
        if ($query->view_enable_facet_count
            && empty($settings['hide_count'])
        ) {
            if ($settings['type'] !== 'checkboxes' // always OR if not checkboxes
                || $settings['andor'] === 'OR'
            ) {
                // Clone field query and exclude queries for the current choice field and fetch facets
                $field_query = clone $query->getFieldQuery();
                $field_query->removeNamedCriteria($field->getFieldName());
                $facets = $query->facets($field->getFieldName(), $this->_valueColumn, $field_query);
            } else {
                $facets = $query->facets($field->getFieldName(), $this->_valueColumn);
            }
        }
        
        if (!isset($current)) {        
            $option_no_escape = false;
            if (!$options = $this->_getOptions($field, !empty($settings['show_icon']), $option_no_escape)) return; // no options
            
            switch ($settings['type']) {
                case 'radios':
                case 'select':
                    $options[$this->_emptyValue] = $settings['default_text'];
                    $default_value = $this->_emptyValue; 
                    break;
                case 'checkboxes':
                default:
                    $default_value = null;
                    $settings['type'] = 'checkboxes';
            }
            
            $current = [
                '#type' => $settings['type'],
                '#select2' => empty($settings['no_fancy']),
                '#placeholder' => $settings['default_text'],
                '#options' => $options,
                '#options_valid' => array_keys($options),
                '#options_visible_count' => $settings['show_more']['num'],
                '#option_no_escape' => !empty($option_no_escape),
                '#default_value' => $default_value,
                '#entity_filter_form_type' => $settings['type'],
                '#options_disabled' => [],
                '#columns' => !$this->_application->isRunning() || empty($settings['columns']) ? 1 : $settings['columns'],
            ];
        }
        
        if (isset($facets)) {
            $_request = isset($request) ? (array)$request : [];
            foreach (array_keys($current['#options']) as $value) {
                if (empty($facets[$value])) {
                    if ($value !== $this->_emptyValue) {
                        if (!empty($settings['hide_empty'])) {
                            unset($current['#options'][$value]);
                        } else {
                            if (!in_array($value, $_request)) {
                                // Disable only when the option is currently not selected
                                $current['#options_disabled'][] = $value;
                            }
                            $current['#options'][$value] = [
                                '#title' => $current['#options'][$value],
                                '#count' => 0,
                            ];
                        }
                    }
                } else {
                    $current['#options'][$value] = [
                        '#title' => $current['#options'][$value],
                        '#count' => $facets[$value],
                    ];
                }
            }
            if (!empty($settings['sort_by_count'])) {
                uasort($current['#options'], function ($a, $b) {
                    return isset($a['#count']) && isset($b['#count']) && $a['#count'] > $b['#count'] ? -1 : 1;
                });
            }
        }
        
        return empty($current['#options']) ? null : $current;
    }
    
    protected function _getOptions(IField $field, $showIcon, &$noEscape = false)
    {
        if (!$options = $this->_application->Field_ChoiceOptions($field)) return; // no options
        
        if (!empty($showIcon) && !empty($options['icons'])) {
            $noEscape = true;
            $ret = [];
            foreach (array_keys($options['options']) as $value) {
                if (isset($options['icons'][$value])) {
                    $ret[$value] = '<i class="fa-fw ' . $options['icons'][$value] . '"></i> ' . $this->_application->H($options['options'][$value]);
                } else {
                    $ret[$value] = $this->_application->H($options['options'][$value]);
                }
            }
        } else {
            $ret = $options['options'];
        }
        return $ret;
    }
    
    public function fieldFilterIsFilterable(IField $field, array $settings, &$value, array $requests = null)
    {
        return $settings['type'] === 'checkboxes' ? !empty($value) : $value != $this->_emptyValue;
    }
    
    public function fieldFilterDoFilter(Query $query, IField $field, array $settings, $value, array &$sorts)
    {
        $value = (array)$value;
        if (count($value) === 1) {
            $query->fieldIs($field, array_shift($value), $this->_valueColumn);
        } elseif ($settings['andor'] === 'OR' || !$this->_isMultipleChoiceField($field)) { // AND query does not make sense for non-multiple choice fields
            $query->fieldIsIn($field, $value, $this->_valueColumn);
        } else {
            $query->startCriteriaGroup('AND')->fieldIs($field, array_shift($value), $this->_valueColumn);
            $i = 1;
            foreach ($value as $_value) {
                $query->fieldIs($field, $_value, $this->_valueColumn, $field->getFieldName() . ++$i);
            }
            $query->finishCriteriaGroup();
        }
    }
    
    public function fieldFilterLabels(IField $field, array $settings, $value, $form, $defaultLabel)
    {
        $ret = [];
        if (empty($form['#option_no_escape'])) {
            foreach ((array)$value as $_value) {
                $ret[$_value] = $this->_application->H($form['#options'][$_value]);
            }
        } else {
            foreach ((array)$value as $_value) {
                $ret[$_value] = $form['#options'][$_value];
            }
        }
        
        return $ret;
    }
    
    protected function _isMultipleChoiceField(IField $field)
    {
        return $field->getFieldWidget() === 'checkboxes'
            || ($field->getFieldWidget() === 'select' && $field->getFieldMaxNumItems() !== 1);
    }
}