<?php

namespace YahnisElsts\AdminMenuEditor\ProCustomizable\Controls;

use YahnisElsts\AdminMenuEditor\Customizable\Controls\AbstractNumericControl;
use YahnisElsts\AdminMenuEditor\Customizable\Controls\ChoiceControlOption;
use YahnisElsts\AdminMenuEditor\Customizable\Controls\PopupSlider;
use YahnisElsts\AdminMenuEditor\Customizable\HtmlHelper;
use YahnisElsts\AdminMenuEditor\Customizable\Rendering\Renderer;
use YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractStructSetting;
use YahnisElsts\AdminMenuEditor\ProCustomizable\HasUnit;
use YahnisElsts\AdminMenuEditor\ProCustomizable\Settings\CssBoxDimensions;
use YahnisElsts\AdminMenuEditor\WireDSL\EvaluationContext;

class BoxDimensions extends AbstractNumericControl {
	const DEFAULT_DIMENSION_NAMES = [
		'top'         => 'Top',
		'bottom'      => 'Bottom',
		'left'        => 'Left',
		'right'       => 'Right',
		//Border radius specific:
		'topLeft'     => 'Top left',
		'topRight'    => 'Top right',
		'bottomRight' => 'Bottom right',
		'bottomLeft'  => 'Bottom left',
	];

	protected $type = 'boxDimensions';
	protected $koComponentName = 'ame-box-dimensions';

	const INPUT_ID_PREFIX = '_ame-box-dimensions-input-';
	const UNIT_SELECTED_PREFIX = '_ame-box-dimensions-unit-';
	protected static int $generatedIdCounter = 0;

	protected $dimensionNames = null;

	/**
	 * @var bool Whether to merge the top/bottom and left/right dimensions into a single dimension.
	 *           This is currently only implemented for the KO component.
	 */
	protected $symmetricModeEnabled = false;

	public function __construct($settings = [], $params = [], $children = []) {
		parent::__construct($settings, $params, $children);

		if ( isset($params['dimensionNames']) ) {
			$this->dimensionNames = $params['dimensionNames'];
		} else if ( $this->mainBinding instanceof AbstractStructSetting ) {
			//Detect available dimensions.
			$dimensionNames = [];
			foreach (self::DEFAULT_DIMENSION_NAMES as $key => $name) {
				$child = $this->mainBinding->getChild($key);
				if ( $child ) {
					$dimensionNames[$key] = $name;
				}
			}
			if ( !empty($dimensionNames) ) {
				$this->dimensionNames = $dimensionNames;
			}
		}

		if ( isset($params['symmetricMode']) ) {
			$this->symmetricModeEnabled = $params['symmetricMode'];
		}
	}

	public function renderContent(Renderer $renderer, EvaluationContext $context) {
		if ( !($this->mainBinding instanceof AbstractStructSetting) ) {
			throw new \InvalidArgumentException('The main setting for this control must be a struct setting.');
		}
		if ( !($this->mainBinding instanceof HasUnit) ) {
			throw new \InvalidArgumentException('The main setting for this control must implement HasUnit.');
		}

		HtmlHelper::outputTag('fieldset', [
			'class'              => array_merge(
				[
					'ame-container-with-popup-slider',
					'ame-box-dimensions-control',
				],
				$this->classes
			),
			'style'              => $this->styles,
			'data-ac-setting-id' => $this->mainBinding->getId(),
			'data-bind'          => $this->makeKoDataBind($this->getKoEnableBinding()),
		]);

		$unitElementId = self::UNIT_SELECTED_PREFIX . (self::$generatedIdCounter++);
		$numberConfig = $this->getNumberConfig($context);

		$order = $this->getDimensionNames();
		foreach ($order as $key => $label) {
			$setting = $this->mainBinding->getChild($key);
			if ( !$setting ) {
				continue;
			}

			$inputId = self::INPUT_ID_PREFIX . (self::$generatedIdCounter++);

			HtmlHelper::outputTag('div', [
				'class' => ['ame-single-box-dimension', 'ame-box-dimension-' . $key],
			]);

			HtmlHelper::outputTag(
				'input',
				array_merge(
					$this->getBasicInputAttributes($numberConfig),
					[
						'name'     => $this->getFieldName($context, $key),
						'value'    => $setting->getValue(),
						'id'       => $inputId,
						'class'    => [
							'ame-small-number-input',
							'ame-input-with-popup-slider',
							'ame-box-dimensions-input',
							'ame-box-dimensions-input-' . $key,
						],
						'disabled' => !$this->isEnabled($context),

						'data-unit-element-id'   => $unitElementId,
						'data-ame-box-dimension' => $key,
						'data-bind'              => $this->makeKoDataBind(array_merge([
							'value'                     =>
								$this->getKoObservableExpression($setting->getValue(), $setting),
							'ameObservableChangeEvents' => 'true',
						], $this->getKoEnableBinding())),
					]
				)
			);

			HtmlHelper::outputTag(
				'label',
				[
					'for'   => $inputId,
					'class' => 'ame-box-dimension-label',
					'title' => $label,
				],
				HtmlHelper::tag(
					'span',
					['class' => 'ame-box-dimension-label-text'],
					esc_html($label)
				)
			);
			echo '</div>';
		}

		//Unit selector.
		$unitSetting = $this->mainBinding->getUnitSetting();
		$this->renderUnitDropdown($unitSetting, $context, [
			'name'               => $this->getFieldName($context, 'unit'),
			'id'                 => $unitElementId,
			'class'              => 'ame-box-dimensions-unit-selector',
			'data-slider-ranges' => wp_json_encode($this->getSliderRanges($numberConfig)),
			'disabled'           => !$this->isEnabled($context),
		]);

		//"Link" button.
		//Enable it by default if all sides are the same. Do not enable if the value is an empty
		//string or null: overwriting all defaults with equal values leads to undesirable results
		//for elements that have different defaults for different sides, like admin menu items.
		$linkButtonEnabled = true;
		$firstSetting = $this->mainBinding->getChild(\ameUtils::getFirstKey($order));
		if ( $firstSetting ) {
			$firstValue = $firstSetting->getValue();
			if ( ($firstValue === '') || ($firstValue === null) ) {
				$linkButtonEnabled = false;
			} else {
				foreach ($order as $side => $label) {
					$setting = $this->mainBinding->getChild($side);
					if ( $setting && ($setting->getValue() !== $firstValue) ) {
						$linkButtonEnabled = false;
						break;
					}
				}
			}
		}

		$buttonClasses = ['button', 'button-secondary', 'ame-box-dimensions-link-button', 'hide-if-no-js'];
		if ( $linkButtonEnabled ) {
			$buttonClasses[] = 'active';
		}
		HtmlHelper::outputTag(
			'button',
			[
				'class'     => $buttonClasses,
				'title'     => 'Link values',
				'disabled'  => !$this->isEnabled($context),
				'data-bind' => $this->makeKoDataBind($this->getKoEnableBinding()),
			],
			'<span class="dashicons dashicons-admin-links"></span>'
		);

		$slider = new PopupSlider([
			'positionParentSelector' => '.ame-single-box-dimension',
			'verticalOffset'         => -2,
		]);
		$slider->render();
		echo '</fieldset>';

		static::enqueueDependencies();
	}

	/**
	 * @return array<string,string>
	 */
	protected function getDimensionNames(): array {
		if ( $this->dimensionNames === null ) {
			return self::DEFAULT_DIMENSION_NAMES;
		}
		return $this->dimensionNames;
	}

	protected function getKoComponentParams(EvaluationContext $context): array {
		$params = parent::getKoComponentParams($context);

		if ( ($this->mainBinding instanceof CssBoxDimensions) ) {
			$unitSetting = $this->mainBinding->getUnitSetting();
			$params['unitDropdownOptions'] = ChoiceControlOption::generateKoOptions(
				ChoiceControlOption::tryGenerateFromSetting($unitSetting)
			);
		}

		if ( $this->dimensionNames !== null ) {
			$dimensionNames = $this->getDimensionNames();
			//To preserve order, convert the associative array to a list of [key, value] pairs.
			$pairs = [];
			foreach ($dimensionNames as $key => $label) {
				$pairs[] = [$key, $label];
			}
			$params['dimensionNames'] = $pairs;
		}

		if ( $this->symmetricModeEnabled ) {
			$params['symmetricMode'] = true;
		}

		return $params;
	}

	public function enqueueKoComponentDependencies() {
		parent::enqueueKoComponentDependencies();

		//Enqueue the Popup Slider. Unlike regular rendering, this doesn't happen
		//automatically with KO components.
		PopupSlider::enqueueDependencies();
	}
}