<?php

class fieldNumber extends cmsFormField {

    public $title       = LANG_PARSER_NUMBER;
    public $sql         = 'DECIMAL({decimal_m},{decimal_d}) {unsigned} NULL DEFAULT NULL';
    public $filter_type = 'int';

    public function getOptions() {
        return [
            new fieldList('input_type', [
                'title'   => LANG_PARSER_NUMBER_TYPE,
                'default' => 'text',
                'items' => [
                    'text'   => 'text',
                    'number' => 'number'
                ]
            ]),
            new fieldString('placeholder', [
                'title' => LANG_PARSER_PLACEHOLDER,
                'can_multilanguage' => true
            ]),
            new fieldCheckbox('is_abs', [
                'title'   => LANG_PARSER_NUMBER_IS_ABS,
                'default' => false
            ]),
            new fieldCheckbox('save_zero', [
                'title'   => LANG_PARSER_NUMBER_SAVE_ZERO,
                'default' => true
            ]),
            new fieldNumber('decimal_int', [
                'title'   => LANG_PARSER_NUMBER_DECIMAL_INT,
                'default' => 7,
                'rules' => [
                    ['required'],
                    ['min', 1],
                    ['max', 35]
                ],
                'options' => [
                    'is_abs' => true,
                    'is_ceil' => true,
                ]
            ]),
            new fieldList('thousands_sep', [
                'title'   => LANG_PARSER_NUMBER_THOUSANDS_SEP,
                'default' => ' ',
                'items' => [
                    ' ' => LANG_SPACE,
                    ',' => LANG_COMMA,
                    '’' => LANG_APOSTROPHE,
                    '.' => LANG_DOT,
                    'another' => LANG_ANOTHER,
                ],
                'can_multilanguage' => true
            ]),
            new fieldString('thousands_sep_another', [
                'title' => LANG_PARSER_NUMBER_THOUSANDS_SEP,
                'visible_depend' => ['options:thousands_sep' => ['show' => ['another']]],
                'options' => [
                    'max_length' => 12
                ]
            ]),
            new fieldCheckbox('is_ceil', [
                'title'   => LANG_PARSER_NUMBER_IS_CEIL,
                'default' => false
            ]),
            new fieldList('dec_point', [
                'title'   => LANG_PARSER_NUMBER_DEC_POINT,
                'default' => '.',
                'items' => [
                    '.' => LANG_DOT,
                    ',' => LANG_COMMA,
                    ' ' => LANG_SPACE,
                    'another' => LANG_ANOTHER,
                ],
                'visible_depend' => ['options:is_ceil' => ['hide' => ['1']]]
            ]),
            new fieldString('dec_point_another', [
                'title' => LANG_PARSER_NUMBER_DEC_POINT,
                'visible_depend' => ['options:dec_point' => ['show' => ['another']], 'options:is_ceil' => ['hide' => ['1']]],
                'options' => [
                    'max_length' => 12
                ]
            ]),
            new fieldNumber('decimal_s', [
                'title'   => LANG_PARSER_NUMBER_DECIMAL_S,
                'default' => 2,
                'rules' => [
                    ['max', 30]
                ],
                'options' => [
                    'is_abs' => true,
                    'is_ceil' => true,
                ],
                'visible_depend' => ['options:is_ceil' => ['hide' => ['1']]]
            ]),
            new fieldCheckbox('trim_dec', [
                'title'   => LANG_PARSER_NUMBER_TRIM_ZERO,
                'default' => true,
                'visible_depend' => ['options:is_ceil' => ['hide' => ['1']]]
            ]),
            new fieldCheckbox('filter_range', [
                'title' => LANG_PARSER_NUMBER_FILTER_RANGE,
                'default' => false
            ]),
            new fieldCheckbox('filter_range_slide', [
                'title' => LANG_PARSER_NUMBER_FILTER_RANGE_SLIDE,
                'hint' => LANG_PARSER_NUMBER_FILTER_RANGE_SLIDE_HINT,
                'default' => false,
                'visible_depend' => ['options:filter_range' => ['show' => ['1']]]
            ]),
            new fieldCheckbox('filter_range_show_input', [
                'title' => LANG_PARSER_NUMBER_FILTER_RANGE_SI,
                'default' => false,
                'visible_depend' => ['options:filter_range' => ['show' => ['1']]]
            ]),
            new fieldNumber('filter_range_slide_step', [
                'title'   => LANG_PARSER_NUMBER_FILTER_STEP,
                'default' => 1,
                'options' => [
                    'is_abs' => true,
                    'is_ceil' => true,
                ],
                'visible_depend' => ['options:filter_range' => ['show' => ['1']]]
            ]),
            new fieldString('prefix', [
                'title' => LANG_PARSER_PREFIX,
                'can_multilanguage' => true
            ]),
            new fieldString('units', [
                'title' => LANG_PARSER_NUMBER_UNITS,
                'can_multilanguage' => true
            ]),
            new fieldList('units_sep', [
                'title'   => LANG_PARSER_NUMBER_UNITS_SEP,
                'default' => ' ',
                'items' => [
                    ' ' => LANG_SPACE,
                    'another' => LANG_ANOTHER,
                ]
            ]),
            new fieldString('units_sep_another', [
                'title' => LANG_PARSER_NUMBER_UNITS_SEP,
                'visible_depend' => ['options:units_sep' => ['show' => ['another']]],
                'options' => [
                    'max_length' => 12
                ]
            ])
        ];
    }

    public function getRules() {

        if ($this->context === 'filter' && $this->getOption('filter_range')) {

            // Если в настройках поля в админке указали "только целые числа"
            $rules_number_exists = array_search(['digits'], $this->rules);
            if ($rules_number_exists !== false) {
                unset($this->rules[$rules_number_exists]);
            }

            $this->rules[] = ['number_range'];
        } else {
            $this->rules[] = ['number'];
        }

        return parent::getRules();
    }

    public function getOption($key, $default = null) {

        switch ($key) {
            case 'decimal_s':
                if (parent::getOption('is_ceil')) {
                    return 0;
                }
                break;
            case 'thousands_sep':
                if (parent::getOption('thousands_sep') === 'another') {
                    return parent::getOption('thousands_sep_another');
                }
                break;
            case 'dec_point':
                if (parent::getOption('dec_point') === 'another') {
                    return parent::getOption('dec_point_another');
                }
                break;
            case 'units_sep':
                if (parent::getOption('units_sep') === 'another') {
                    return parent::getOption('units_sep_another');
                }
                break;
            default:
                break;
        }

        return parent::getOption($key, $default);
    }

    public function getSQL() {

        $decimal_int = (int)$this->getOption('decimal_int');
        $decimal_s = (int)$this->getOption('decimal_s');

        return str_replace([
            '{decimal_m}',
            '{decimal_d}',
            '{unsigned}'
        ], [
            ($decimal_int + $decimal_s),
            $decimal_s,
            ($this->getOption('is_abs') ? 'UNSIGNED' : '')
        ], $this->sql);
    }

    public function parse($value) {

        if (is_empty_value($value)) {
            return '';
        }

        $units = $this->getProperty('units') ?: $this->getOption('units');
        $prefix = $this->getProperty('prefix') ?: $this->getOption('prefix', '');

        return $prefix . ' ' . $this->formatFloatValue($value) . $this->getOption('units_sep') . $units;
    }

    public function getStringValue($value) {

        if (is_empty_value($value)) {
            return '';
        }

        $units = $this->getProperty('units') ?: $this->getOption('units');

        if (is_array($value)) {

            $result_string = '';

            if (!empty($value['from'])) {
                $result_string .= LANG_FROM . ' ' . $this->getStringValue($value['from']) . ' ';
            }

            if (!empty($value['to'])) {
                $result_string .= LANG_TO . ' ' . $this->getStringValue($value['to']);
            }

            return $result_string;
        }

        return $this->formatFloatValue($value) . $this->getOption('units_sep') . $units;
    }

    public function hookAfterUpdate($content_table_name, $field, $field_old, $model) {

        // Для свойств не меняем ничего
        if (!isset($field_old['name'])) {
            return parent::hookAfterUpdate($content_table_name, $field, $field_old, $model);
        }

        $new_decimal_int = $field['options']['decimal_int'] ?? 7;
        $new_decimal_s   = empty($field['options']['is_ceil']) ? ($field['options']['decimal_s'] ?? 2) : 0;
        $new_unsigned    = $field['options']['is_abs'] ?? false;

        $old_decimal_int = $field_old['parser']->options['decimal_int'] ?? 7;
        $old_decimal_s   = empty($field_old['parser']->options['is_ceil']) ? ($field_old['parser']->options['decimal_s'] ?? 2) : 0;
        $old_unsigned    = $field_old['parser']->options['is_abs'] ?? false;

        if ($field_old['type'] === $field['type'] && ($new_decimal_int != $old_decimal_int ||
            $new_decimal_s != $old_decimal_s ||
            $new_unsigned != $old_unsigned
        )) {

            $field_sql = str_replace([
                '{decimal_m}',
                '{decimal_d}',
                '{unsigned}'
            ], [
                ($new_decimal_int + $new_decimal_s),
                $new_decimal_s,
                ($new_unsigned ? 'UNSIGNED' : '')
            ], $this->sql);

            $sql = "ALTER TABLE `{#}{$content_table_name}` CHANGE `{$field_old['name']}` `{$field_old['name']}` {$field_sql}";

            $model->db->query($sql);
        }

        return parent::hookAfterUpdate($content_table_name, $field, $field_old, $model);
    }

    public function getDefaultVarType($is_filter = false) {

        if ($this->context === 'filter') {
            $is_filter = true;
        }

        if ($is_filter && $this->getOption('filter_range')) {
            $this->var_type = 'array';
        }

        return parent::getDefaultVarType($is_filter);
    }

    public function getFilterInput($value) {

        if ($this->getOption('filter_range')) {

            $from = !empty($value['from']) ? $value['from'] : false;
            $to = !empty($value['to']) ? $value['to'] : false;

            if (!$this->show_filter_input_title) {
                $this->title = false;
            }

            $this->data['type'] = $this->getOption('input_type') ?: 'text';

            $this->data['units'] = $this->getProperty('units') ?: $this->getOption('units');

            $tpl_name = $this->class . '_range';

            // С массивами и свойствами не работает
            if ($this->getOption('filter_range_slide') && strpos($this->name, ':') === false && !preg_match('/^p\d+$/', $this->name)) {

                // получаем минимум и максимум
                if (!empty($this->item['ctype_name'])) {

                    $tpl_name = $this->class . '_range_slide';

                    $controller_name = 'content';

                    if (cmsCore::isModelExists($this->item['ctype_name'])) {
                        $controller_name = $this->item['ctype_name'];
                    }

                    $model = cmsCore::getModel($controller_name);

                    $max_value = $model->getMax($model->getContentTypeTableName($this->item['ctype_name']), $this->name, null);
                    $min_value = $model->getMin($model->getContentTypeTableName($this->item['ctype_name']), $this->name, null);

                    // Нет ничего, не показываем в фильтре
                    if ($max_value === null && $min_value === null) {
                        return '';
                    }
                    // одно значение
                    if ($max_value === $min_value) {
                        return '';
                    }

                    $this->data['slide_params'] = [
                        'min'  => $min_value,
                        'max'  => $max_value,
                        'step' => $this->getOption('filter_range_slide_step', 1),
                        'values' => [($from ?: $min_value), ($to ?: $max_value)]
                    ];
                }
            }

            return cmsTemplate::getInstance()->renderFormField($tpl_name, [
                'field' => $this,
                'from'  => $from,
                'to'    => $to
            ]);

        } elseif ($value && !is_array($value)) {

            return parent::getFilterInput($value);
        }

        return parent::getFilterInput('');
    }

    public function applyFilter($model, $value) {

        if (!is_array($value)) {

            return $model->filterEqual($this->name, $value);

        } elseif (!empty($value['from']) || !empty($value['to'])) {

            if (!empty($value['from'])) {
                $model->filterGtEqual($this->name . '+0', $value['from']);
            }
            if (!empty($value['to'])) {
                $model->filterLtEqual($this->name . '+0', $value['to']);
            }

            return $model;
        }

        return parent::applyFilter($model, $value);
    }

    public function store($value, $is_submitted, $old_value = null) {

        if (!is_array($value)) {

            if (!$this->getOption('save_zero') && !$value) {
                return null;
            }

            if ($this->getOption('is_ceil')) {
                $func = 'intval';
            } else {
                $func = 'floatval';
            }

            $value = $func(str_replace(',', '.', trim((string)$value)));

            return $this->getOption('is_abs') ? abs($value) : $value;

        } elseif (!empty($value['from']) || !empty($value['to'])) {

            if (!empty($value['from'])) {
                $value['from'] = $this->store($value['from'], $is_submitted, $old_value);
            }
            if (!empty($value['to'])) {
                $value['to'] = $this->store($value['to'], $is_submitted, $old_value);
            }

            return $value;
        }

        return null;
    }

    public function storeFilter($value) {
        return $this->store($value, false);
    }

    public function getInput($value) {

        $this->data['type'] = $this->getOption('input_type') ?: 'text';

        $this->data['units'] = $this->getProperty('units') ?: $this->getOption('units');
        $this->data['prefix'] = $this->getProperty('prefix') ?: $this->getOption('prefix', '');

        $this->data['attributes']             = $this->getProperty('attributes') ?: [];
        $this->data['attributes']['size']     = ($this->getOption('size') ?: $this->getProperty('size')) ?: 5;
        $this->data['attributes']['id']       = $this->id;
        $this->data['attributes']['step']     = 'any';
        $this->data['attributes']['inputmode'] = $this->getOption('is_ceil') ? 'numeric' : 'decimal';
        $this->data['attributes']['required'] = (array_search(['required'], $this->getRules()) !== false);
        $this->data['attributes']['placeholder'] = $this->data['attributes']['placeholder'] ?? $this->getOption('placeholder', false);

        if(empty($this->data['attributes']['class'])){
            $this->data['attributes']['class'] = 'input-number';
        } else {
            $this->data['attributes']['class'] .= ' input-number';
        }

        return parent::getInput(!empty($this->options['is_digits']) ? (int)$value : $value);
    }

    private function formatFloatValue($value) {

        $dec_point = $this->getOption('dec_point');
        $decimal_s = $this->getOption('decimal_s');

        $value = number_format(($decimal_s ? (float)$value : (int)$value), $decimal_s, $dec_point, ($this->getOption('thousands_sep') ?: ''));

        if ($decimal_s && $this->getOption('trim_dec')) {
            return rtrim(rtrim($value, '0'), $dec_point);
        }

        return $value;
    }

    public function validate_number_range($value) {

        if (empty($value)) {
            return true;
        }

        if (!in_array(gettype($value), ['array'])) {
            return ERR_VALIDATE_NUMBER;
        }

        if (empty($value['from']) && empty($value['to'])) {
            return true;
        }

        if (!empty($value['from'])) {

            if (is_array($value['from'])) {
                return ERR_VALIDATE_NUMBER;
            }

            if (!is_numeric($value['from'])) {
                return ERR_VALIDATE_NUMBER;
            }
        }

        if (!empty($value['to']) && !is_array($value['to'])) {

            if (is_array($value['to'])) {
                return ERR_VALIDATE_NUMBER;
            }

            if (!is_numeric($value['to'])) {
                return ERR_VALIDATE_NUMBER;
            }
        }

        return true;
    }

}
