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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Display\Icon\IIcon;
use SabaiApps\Directories\Component\Display\Model\Display;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\Type\IEntity;

class IconsHelper {

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

				foreach ( $application->getComponent( $component_name )->displayGetIconNames( $bundle ) as $icon_name ) {
					if ( ! $icon = $application->getComponent( $component_name )->displayGetIcon( $icon_name ) ) {
						continue;
					}
					$icons[ $icon_name ] = array(
						'component' => $component_name,
						'weight'    => ( null !== $weight = $icon->displayIconInfo( $bundle, 'weight' ) ) ? $weight : 9,
					);
				}
			}
			// Sort panels by weight
			uasort(
				$icons,
				function ( $a, $b ) {
					return $a['weight'] < $b['weight'] ? -1 : 1;
				}
			);
			foreach ( array_keys( $icons ) as $icon_name ) {
				$icons[ $icon_name ] = $icons[ $icon_name ]['component'];
			}
			$icons = $application->Filter( 'display_icons', $icons, array( $bundle ) );
			$application->getPlatform()->setCache( $icons, 'display_icons_' . $bundle->type, 0 );
		}

		return $icons;
	}

	private $_impls = array();

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

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

	public function options( Application $application, Bundle $bundle, Display $display ) {
		$options = $defaults = array();
		foreach ( $application->Display_Icons( $bundle ) as $icon_name => $component_name ) {
			if ( ( ! $icon = $application->Display_Icons_impl( $bundle, $icon_name, true ) )
				|| ! $icon->displayIconSupports( $bundle, $display )
				|| ( ! $info = $icon->displayIconInfo( $bundle ) )
			) {
				continue;
			}

			if ( ! empty( $info['multiple'] ) ) {
				foreach ( $info['multiple'] as $_icon_name => $_icon_info ) {
					$_icon_name             = $icon_name . '-' . $_icon_name;
					$options[ $_icon_name ] = $_icon_info['label'];
					if ( ! empty( $_icon_info['default_checked'] ) ) {
						$defaults[] = $_icon_name;
					}
				}
			} else {
				$options[ $icon_name ] = $info['label'];
				if ( ! empty( $info['default_checked'] ) ) {
					$defaults[] = $icon_name;
				}
			}
		}
		return array( $options, $defaults );
	}

	public function settingsForm( Application $application, Bundle $bundle, array $settings, Display $display, array $parents, array $arrangementSelector ) {
		$form                 = array();
		$icons_available      = $this->help( $application, $bundle );
		$arrangement_selector = sprintf( 'input[name="%s[]"]', $application->Form_Form_fieldName( $arrangementSelector ) );
		foreach ( array_keys( $icons_available ) as $icon_name ) {
			if ( ( ! $icon = $application->Display_Icons_impl( $bundle, $icon_name, true ) )
				|| ! $icon->displayIconSupports( $bundle, $display )
				|| ( ! $info = $icon->displayIconInfo( $bundle ) )
			) {
				continue;
			}

			if ( ! empty( $info['multiple'] ) ) {
				foreach ( $info['multiple'] as $_icon_name => $_icon_info ) {
					$_icon_name          = $icon_name . '-' . $_icon_name;
					$icon_settings       = isset( $settings[ $_icon_name ]['settings'] ) ? (array) $settings[ $_icon_name ]['settings'] : array();
					$form[ $_icon_name ] = $this->_getIconSettingsForm(
						$application,
						$bundle,
						$_icon_name,
						$_icon_info['label'],
						$icon,
						$icon_settings,
						$display,
						array_merge( $parents, array( $_icon_name ) ),
						$arrangement_selector
					);
				}
			} else {
				$icon_settings      = isset( $settings[ $icon_name ]['settings'] ) ? (array) $settings[ $icon_name ]['settings'] : array();
				$form[ $icon_name ] = $this->_getIconSettingsForm(
					$application,
					$bundle,
					$icon_name,
					$icon->displayIconInfo( $bundle, 'label' ),
					$icon,
					$icon_settings,
					$display,
					array_merge( $parents, array( $icon_name ) ),
					$arrangement_selector
				);
			}
		}
		return $form;
	}

	protected function _getIconSettingsForm( Application $application, Bundle $bundle, $iconName, $iconLabel, IIcon $icon, array $settings, Display $display, array $parents, $arrangementSelector ) {
		$parents[] = 'settings';
		if ( $default_settings = $icon->displayIconInfo( $bundle, 'default_settings' ) ) {
			$settings += $default_settings;
		}

		$form = array();
		if ( $icon->displayIconInfo( $bundle, 'iconable' ) !== false ) {
			$form['_icon'] = array(
				'#type'          => 'iconpicker',
				'#title'         => __( 'Icon', 'directories-pro' ),
				'#default_value' => isset( $settings['_icon'] ) ? $settings['_icon'] : null,
				'#horizontal'    => true,
				'#weight'        => -3,
			);
		}
		if ( $icon->displayIconInfo( $bundle, 'colorable' ) !== false ) {
			$form['_color'] = array(
				'#weight' => -2,
				'type'    => array(
					'#type'               => 'radios',
					'#title'              => __( 'Icon color', 'directories-pro' ),
					'#default_value'      => isset( $settings['_color']['type'] ) ? $settings['_color']['type'] : null,
					'#options'            => $application->System_Color_options() + array( 'custom' => __( 'Custom', 'directories-pro' ) ),
					'#option_no_escape'   => true,
					'#default_value_auto' => true,
					'#horizontal'         => true,
					'#columns'            => 5,
				),
				'value'   => array(
					'#type'          => 'colorpicker',
					'#default_value' => isset( $settings['_color']['value'] ) ? $settings['_color']['value'] : null,
					'#horizontal'    => true,
					'#states'        => array(
						'visible' => array(
							sprintf( 'input[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( '_color', 'type' ) ) ) ) => array( 'value' => 'custom' ),
						),
					),
				),
				'bg'      => array(
					'#type'          => 'colorpicker',
					'#default_value' => isset( $settings['_color']['bg'] ) ? $settings['_color']['bg'] : null,
					'#horizontal'    => true,
					'#field_prefix'  => __( 'Background color', 'directories-pro' ),
					'#states'        => array(
						'visible' => array(
							sprintf( 'input[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( '_color', 'type' ) ) ) ) => array( 'value' => 'custom' ),
						),
					),
				),
			);
		}
		if ( $icon->displayIconInfo( $bundle, 'linkable' ) !== false ) {
			$form['_link'] = $application->Entity_Link_settingsForm( $bundle, isset( $settings['_link'] ) ? $settings['_link'] : array(), array_merge( $parents, array( '_link' ) ) );
		}
		if ( $icon_settings_form = $icon->displayIconSettingsForm( $bundle, $settings, $display, $parents, -1 ) ) {
			$form += $icon_settings_form;
		}
		if ( ! empty( $form ) ) {
			$form = array(
				'#title'   => $iconLabel,
				'#states'  => array(
					'enabled' => array(
						$arrangementSelector => array( 'value' => $iconName ),
					),
				),
				'settings' => $form,
			);
		}

		return $form;
	}

	public function renderIcons( Application $application, Bundle $bundle, array $settings, array $iconSettings, IEntity $entity ) {
		$icons = array();
		$size  = isset( $settings['size'] ) ? $settings['size'] : 'md';
		$color = array( 'type' => 'secondary' );
		foreach ( $settings['arrangement'] as $icon_name ) {
			$_icon_name = $icon_name;
			if ( $pos = strpos( $icon_name, '-' ) ) {
				$icon_name = substr( $icon_name, 0, $pos );
			}
			if ( ! $icon_impl = $application->Display_Icons_impl( $bundle, $icon_name, true ) ) {
				continue;
			}

			$icon_settings = isset( $iconSettings[ $_icon_name ]['settings'] ) ? $iconSettings[ $_icon_name ]['settings'] : array();
			if ( ! $output = $icon_impl->displayIconOutput( $bundle, $entity, $icon_settings ) ) {
				continue;
			}

			if ( is_array( $output ) ) {
				if ( ! array_key_exists( 0, $output ) ) {
					$output = array( $output );
				}
				if ( $icon_impl->displayIconInfo( $bundle, 'iconable' ) !== false ) {
					if ( empty( $icon_settings['_icon'] ) ) {
						continue;
					}
					$icon = $icon_settings['_icon'];
				}
				if ( $icon_impl->displayIconInfo( $bundle, 'colorable' ) !== false ) {
					if ( ! empty( $icon_settings['_color'] ) ) {
						$color = $icon_settings['_color'];
					}
				}
				foreach ( array_keys( $output ) as $output_key ) {
					$_output = $output[ $output_key ];
					if ( empty( $_output['icon'] ) && empty( $icon ) ) {
						continue;
					}
					$icon = $_output['icon'];
					if ( ! empty( $_output['color'] ) ) {
						$color = $_output['color'];
					}
					$attr          = isset( $_output['attr'] ) ? $_output['attr'] : array();
					$icon_rendered = $this->_renderIcon( $application, $_icon_name, $icon, $color, $size, $attr );
					if ( $icon_impl->displayIconInfo( $bundle, 'linkable' ) !== false ) {
						if ( ! empty( $icon_settings['_link'] ) ) {
							$icon_rendered = $application->Entity_Link( $entity, $icon_settings['_link'], $icon_rendered, array( 'no_escape' => true ) );
						}
					}
					$icons[ $_icon_name . '_' . $output_key ] = $icon_rendered;
				}
			} elseif ( is_bool( $output ) ) {
				if ( $icon_impl->displayIconInfo( $bundle, 'iconable' ) !== false ) {
					if ( empty( $icon_settings['_icon'] ) ) {
						continue;
					}
				}
				if ( $icon_impl->displayIconInfo( $bundle, 'colorable' ) !== false ) {
					if ( ! empty( $icon_settings['_color'] ) ) {
						$color = $icon_settings['_color'];
					}
				}
				$icon_rendered = $this->_renderIcon( $application, $_icon_name, $icon_settings['_icon'], $color, $size );
				if ( $icon_impl->displayIconInfo( $bundle, 'linkable' ) !== false ) {
					if ( ! empty( $icon_settings['_link'] ) ) {
						$icon_rendered = $application->Entity_Link( $entity, $icon_settings['_link'], $icon_rendered, array( 'no_escape' => true ) );
					}
				}
				$icons[ $_icon_name ] = $icon_rendered;
			}
		}

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

	protected function _renderIcon( Application $application, $name, $icon, $color, $size = 'sm', array $attr = array() ) {
		$attr['class'] = 'drts-icon drts-icon-' . $size;
		if ( strpos( $icon, 'http' ) === 0 ) {
			$tag         = 'img';
			$attr['src'] = $icon;
			$attr['alt'] = '';
		} else {
			$attr['class'] .= ' ' . $icon;
			$tag            = 'i';
			if ( $color['type'] === 'custom' ) {
				$attr['style'] = 'color:' . $application->H( $color['value'] ) . ';';
				if ( ! empty( $color['bg'] ) ) {
					$attr['style'] .= 'background-color:' . $application->H( $color['bg'] ) . ';';
				}
			} else {
				$attr['class'] .= ' ' . DRTS_BS_PREFIX . 'bg-' . $color['type'] . ' ' . DRTS_BS_PREFIX . 'text-' . ( $color['type'] === 'light' ? 'dark' : 'light' );
			}
		}
		$attr['data-icon-name'] = $name;
		return '<' . $tag . ' ' . $application->Attr( $attr ) . '></' . $tag . '>';
	}

	public function iconLabels( Application $application, Bundle $bundle, array $icons ) {
		$ret = array();
		foreach ( $icons as $icon_name ) {
			if ( $pos = strpos( $icon_name, '-' ) ) {
				$icon_name = substr( $icon_name, 0, $pos );
			}
			if ( ! $icon = $application->Display_Icons_impl( $bundle, $icon_name, true ) ) {
				continue;
			}

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