<?php
namespace SabaiApps\Directories\Component\Display\Helper;

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Display\Label\ILabel;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\Type\IEntity;

class LabelsHelper {

	public function help( Application $application, Bundle $bundle, $useCache = true ) {
		if ( ! $useCache
			|| ( ! $labels = $application->getPlatform()->getCache( 'display_labels_' . $bundle->type ) )
		) {
			$labels = array();
			foreach ( $application->InstalledComponentsByInterface( 'Display\ILabels' ) as $component_name ) {
				if ( ! $application->isComponentLoaded( $component_name ) ) {
					continue;
				}

				foreach ( $application->getComponent( $component_name )->displayGetLabelNames( $bundle ) as $label_name ) {
					if ( ( ! $label = $application->getComponent( $component_name )->displayGetLabel( $label_name ) )
						|| ( false === $label->displayLabelInfo( $bundle ) )
					) {
						continue;
					}

					$labels[ $label_name ] = $component_name;
				}
			}
			$labels = $application->Filter( 'display_labels', $labels, array( $bundle ) );
			$application->getPlatform()->setCache( $labels, 'display_labels_' . $bundle->type, 0 );
		}

		return $labels;
	}

	private $_impls = array();

	/**
	 * Gets an implementation of Display\ILabel interface for a given label name
	 *
	 * @param Application $application
	 * @param Bundle      $bundle
	 * @param string      $label
	 * @param bool        $returnFalse
	 * @return ILabel
	 */
	public function impl( Application $application, Bundle $bundle, $label, $returnFalse = false ) {
		if ( ! isset( $this->_impls[ $label ] ) ) {
			if ( ( ! $labels = $application->Display_Labels( $bundle ) )
				|| ! isset( $labels[ $label ] )
				|| ! $application->isComponentLoaded( $labels[ $label ] )
			) {
				if ( $returnFalse ) {
					return false;
				}
				throw new Exception\UnexpectedValueException( sprintf( 'Invalid label: %s', $label ) );
			}
			$this->_impls[ $label ] = $application->getComponent( $labels[ $label ] )->displayGetLabel( $label );
		}

		return $this->_impls[ $label ];
	}

	public function options( Application $application, Bundle $bundle ) {
		$options = array();
		foreach ( $application->Display_Labels( $bundle ) as $label_name => $component_name ) {
			if ( ! $label = $application->Display_Labels_impl( $bundle, $label_name, true ) ) {
				continue;
			}

			$info = $label->displayLabelInfo( $bundle );
			if ( ! empty( $info['multiple'] ) ) {
				foreach ( $info['multiple'] as $_label_name => $_label_info ) {
					$_label_name             = $label_name . '-' . $_label_name;
					$options[ $_label_name ] = $_label_info['label'];
				}
			} else {
				$options[ $label_name ] = $info['label'];
			}
		}
		return $options;
	}

	public function settingsForm( Application $application, Bundle $bundle, array $settings, array $parents, array $arrangementSelector ) {
		$form                 = array();
		$labels_available     = $this->help( $application, $bundle );
		$arrangement_selector = sprintf( 'input[name="%s[]"]', $application->Form_Form_fieldName( $arrangementSelector ) );
		foreach ( array_keys( $labels_available ) as $label_name ) {
			if ( ! $label = $application->Display_Labels_impl( $bundle, $label_name, true ) ) {
				continue;
			}

			$info = $label->displayLabelInfo( $bundle );
			if ( ! empty( $info['multiple'] ) ) {
				foreach ( $info['multiple'] as $_label_name => $_label_info ) {
					$_label_name          = $label_name . '-' . $_label_name;
					$label_settings       = isset( $settings[ $_label_name ]['settings'] ) ? (array) $settings[ $_label_name ]['settings'] : array();
					$form[ $_label_name ] = $this->_getLabelSettingsForm(
						$application,
						$bundle,
						$_label_name,
						$_label_info['label'],
						$label,
						$label_settings,
						array_merge( $parents, array( $_label_name ) ),
						$arrangement_selector
					);
				}
			} else {
				$label_settings      = isset( $settings[ $label_name ]['settings'] ) ? (array) $settings[ $label_name ]['settings'] : array();
				$form[ $label_name ] = $this->_getLabelSettingsForm(
					$application,
					$bundle,
					$label_name,
					$label->displayLabelInfo( $bundle, 'label' ),
					$label,
					$label_settings,
					array_merge( $parents, array( $label_name ) ),
					$arrangement_selector
				);
			}
		}
		return $form;
	}

	protected function _getLabelSettingsForm( Application $application, Bundle $bundle, $labelName, $labelLabel, ILabel $label, array $settings, array $parents, $arrangementSelector ) {
		$parents[] = 'settings';
		if ( $default_settings = $label->displayLabelInfo( $bundle, 'default_settings' ) ) {
			$settings += $default_settings;
		}

		$form = array();
		if ( $label->displayLabelInfo( $bundle, 'labellable' ) !== false ) {
			$form['_label'] = array(
				'#type'          => 'textfield',
				'#title'         => __( 'Label text', 'directories-pro' ),
				'#default_value' => $settings['_label'],
				'#horizontal'    => true,
				'#weight'        => -3,
			);
		}
		if ( $label->displayLabelInfo( $bundle, 'colorable' ) !== false ) {
			$form['_color'] = array(
				'#weight' => -2,
			) + $application->System_Color_settingsForm(
				$settings['_color'] ?? array(),
				array_merge( $parents, array( '_color' ) ),
				array(
					'title'              => __( 'Label color', 'directories-pro' ),
					'horizontal'         => true,
					'bundle'             => $bundle,
					'include_term_color' => true,
				)
			) + array(
				'text' => array(
					'#type'          => 'colorpicker',
					'#default_value' => $settings['_color']['text'] ?? null,
					'#horizontal'    => true,
					'#field_prefix'  => __( 'Label text color', 'directories-pro' ),
					'#states'        => array(
						'visible' => array(
							sprintf( 'select[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( '_color', 'type' ) ) ) ) => array( 'value' => 'custom' ),
						),
					),
				),
			);
		}
		if ( $label->displayLabelInfo( $bundle, 'conditionable' ) ) {
			$form['_conditions']            = $application->Entity_Field_conditionSettingsForm(
				$bundle->name,
				empty( $settings['_conditions'] ) ? ( empty( $settings['conditions'] ) ? array() : $settings['conditions'] ) : $settings['_conditions'],
				array_merge( $parents, array( '_conditions' ) ),
				true
			);
			$form['_conditions']['#weight'] = 99;
		}
		if ( $label_settings_form = $label->displayLabelSettingsForm( $bundle, $settings, $parents ) ) {
			$form += $label_settings_form;
		}
		if ( ! empty( $form ) ) {
			$form = array(
				'#title'   => $labelLabel,
				'#states'  => array(
					'enabled' => array(
						$arrangementSelector => array( 'value' => $labelName ),
					),
				),
				'settings' => $form,
			);
		}

		return $form;
	}

	public function renderLabels( Application $application, Bundle $bundle, array $settings, array $labelSettings, IEntity $entity ) {
		$labels = array();
		$style  = empty( $settings['style'] ) ? null : $settings['style'];
		foreach ( $settings['arrangement'] as $label_name ) {
			$_label_name = $label_name;
			if ( $pos = strpos( $label_name, '-' ) ) {
				$label_name = substr( $label_name, 0, $pos );
			}
			if ( ! $label = $application->Display_Labels_impl( $bundle, $label_name, true ) ) {
				continue;
			}

			$label_settings  = isset( $labelSettings[ $_label_name ]['settings'] ) ? $labelSettings[ $_label_name ]['settings'] : array();
			$label_settings += (array) $label->displayLabelInfo( $bundle, 'default_settings' );

			// Check conditions?
			$conditions = ! empty( $label_settings['_conditions'] )
				? $label_settings['_conditions']
				: ( ! empty( $label_settings['conditions'] ) ? $label_settings['conditions'] : null ); // for compat with v1.3
			if ( ! empty( $conditions )
				&& $label->displayLabelInfo( $bundle, 'conditionable' )
				&& ! $application->Entity_Field_checkConditions( $conditions, $entity )
			) {
				continue;
			}

			if ( ! $text = $label->displayLabelText( $bundle, $entity, $label_settings ) ) {
				continue;
			}

			$labellable = $label->displayLabelInfo( $bundle, 'labellable' ) !== false;
			$color      = array(
				'type' => '',
				'name' => 'secondary',
			);
			if ( is_array( $text ) ) {
				if ( array_key_exists( 0, $text ) ) {
					foreach ( array_keys( $text ) as $text_key ) {
						$_text                  = $text[ $text_key ];
						$_label_name           .= '_' . $text_key;
						$labels[ $_label_name ] = $this->_renderLabel(
							$application,
							$_label_name,
							$this->_getLabelText( $application, $_label_name, $_text, $labellable ),
							isset( $_text['color'] ) && is_array( $_text['color'] ) ? $_text['color'] : $color,
							$style,
							$entity,
							isset( $_text['attr'] ) ? $_text['attr'] : null
						);
					}
				} else {
					$labels[ $_label_name ] = $this->_renderLabel(
						$application,
						$_label_name,
						$this->_getLabelText( $application, $_label_name, $text, $labellable ),
						isset( $text['color'] ) && is_array( $text['color'] ) ? $text['color'] : $color,
						$style,
						$entity,
						isset( $text['attr'] ) ? $text['attr'] : null
					);
				}
			} elseif ( is_bool( $text ) ) {
				$_label_text            = $application->System_TranslateString( $label_settings['_label'], 'label_' . $label_name, 'display_element' );
				$labels[ $_label_name ] = $this->_renderLabel( $application, $_label_name, $_label_text, $color, $style, $entity );
			}
		}

		return empty( $labels ) ? '' : implode( PHP_EOL, $labels );
	}

	protected function _getLabelText( Application $application, $name, array $text, $labellable ) {
		$_text = isset( $text['label'] ) ? (string) $text['label'] : '';
		if ( strlen( $_text ) ) {
			if ( $labellable
				|| ! empty( $text['translate'] )
			) {
				$_text = $application->System_TranslateString( $_text, 'label_' . $name, 'display_element' );
			}
		}
		return $_text;
	}

	protected function _renderLabel( Application $application, $name, $text, $color, $style, IEntity $entity, ?array $attr = null ) {
		$classes     = array( DRTS_BS_PREFIX . 'badge' );
		$color_style = '';
		switch ( $color['type'] ?? '' ) {
			case 'custom':
				$color_style = 'background-color:' . $application->H( $color['value'] ) . ';';
				if ( ! empty( $color['text'] ) ) {
					$color_style .= 'color:' . $application->H( $color['text'] ) . ';';
				}
				break;
			case 'field':
				if ( ! empty( $color['field'] )
					&& ( $field_color = $entity->getSingleFieldValue( $color['field'] ) )
				) {
					$color_style = 'background-color:' . $application->H( $field_color ) . ';';
				}
				break;
			default:
				if ( isset( $color['type'] )
					&& strpos( $color['type'], 'tax-' ) === 0
				) {
					$tax_name = substr( $color['type'], 4 );
					if ( ( $term = $entity->getSingleFieldValue( $tax_name ) )
						&& ( $term instanceof IEntity )
						&& ( $term_color = $term->getCustomProperty( 'color' ) )
					) {
						$color_style = 'background-color:' . $application->H( $term_color ) . ';';
					}
				} elseif ( ! empty( $color['name'] ) ) {
						$classes[] = DRTS_BS_PREFIX . 'bg-' . $color['name'];
				}
		}
		if ( $style === 'pill' ) {
			$classes[] = DRTS_BS_PREFIX . 'badge-pill';
		}
		$attr = isset( $attr ) ? $application->Attr( $attr ) : '';
		return '<span style="' . $color_style . '" class="' . implode( ' ', $classes ) . '" data-label-name="' . $name . '"' . $attr . '>'
			. $application->H( $text )
			. '</span>';
	}

	public function labelLabels( Application $application, Bundle $bundle, array $labels ) {
		$ret = array();
		foreach ( $labels as $label_name ) {
			if ( $pos = strpos( $label_name, '-' ) ) {
				$label_name = substr( $label_name, 0, $pos );
			}
			if ( ! $label = $application->Display_Labels_impl( $bundle, $label_name, true ) ) {
				continue;
			}

			$info = $label->displayLabelInfo( $bundle );
			if ( ! empty( $info['multiple'] )
				&& $pos
				&& ( $key = substr( $label_name, $pos + 1 ) )
				&& isset( $info['multiple'][ $key ]['label'] )
			) {
				$ret[] = $info['multiple'][ $key ]['label'];
			} else {
				$ret[] = $info['label'];
			}
		}
		return $ret;
	}
}
