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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Entity\Model\Bundle;

/**
 * ColorHelper provides various utility methods for color manipulation.
 */
class ColorHelper {

	/**
	 * Returns color options for UI elements.
	 *
	 * @param Application $application
	 * @param array       $options
	 * @return array
	 */
	public function options( Application $application, array $options = array() ) {
		$ret      = array();
		$options += array(
			'as_buttons'      => false,
			'include_link'    => false,
			'include_outline' => true,
			'colors'          => array( 'primary', 'secondary', 'info', 'success', 'warning', 'danger', 'light', 'dark' ),
			'as_background'   => false,
		);
		if ( ! empty( $options['as_background'] ) ) {
			$options['include_outline'] = false;
			$options['colors']          = array( 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'dark' );
		}
		$custom_colors = $application->getComponent( 'System' )->getConfig( 'color' );
		if ( $options['as_buttons'] ) {
			$btn_class = DRTS_BS_PREFIX . 'btn';
			foreach ( $options['colors'] as $value ) {
				if ( isset( $custom_colors[ $value ] ) ) {
					$custom_color  = $custom_colors[ $value ];
					$ret[ $value ] = sprintf( '<button type="button" class="%1$s %1$s-sm" onclick="return false;" style="color:#fff;background-color:%2$s;border-color:%2$s"> </button>', $btn_class, $custom_color );
					if ( $options['include_outline'] ) {
						$ret[ 'outline-' . $value ] = sprintf( '<button type="button" class="%1$s %1$s-sm" onclick="return false;" style="color:%2$s;border-color:%2$s"> </button>', $btn_class, $custom_color );
					}
				} else {
					$ret[ $value ] = sprintf( '<button type="button" class="%1$s %1$s-sm %1$s-%2$s" onclick="return false;"> </button>', $btn_class, $value );
					if ( $options['include_outline'] ) {
						$ret[ 'outline-' . $value ] = sprintf( '<button type="button" class="%1$s %1$s-sm %1$s-outline-%2$s" onclick="return false;"> </button>', $btn_class, $value );
					}
				}
			}
			if ( $options['include_link'] ) {
				$ret['link'] = __( 'Link', 'directories-pro' );
			}
		} else {
			foreach ( $options['colors'] as $value ) {
				if ( isset( $custom_colors[ $value ] ) ) {
					$ret[ $value ] = '<span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . '" style="background-color:' . $custom_colors[ $value ] . '">&nbsp;</span>';
				} else {
					$ret[ $value ] = '<span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-' . $value . '">&nbsp;</span>';
				}
			}
		}

		return $ret;
	}

	/**
	 * Returns color settings form for UI elements.
	 *
	 * @param Application $application
	 * @param mixed       $value
	 * @param array       $parents
	 * @param array       $options
	 * @return array
	 */
	public function settingsForm( Application $application, $value = null, array $parents = array(), array $options = array() ) {
		$options += array(
			'title'              => null,
			'horizontal'         => true,
			'bundle'             => null,
			'include_term_color' => false,
		);
		$form     = array(
			'type'  => array(
				'#type'          => 'select',
				'#title'         => isset( $options['title'] ) ? $options['title'] : __( 'Color', 'directories-pro' ),
				'#default_value' => isset( $value['type'] ) ? $value['type'] : '',
				'#options'       => array(
					''       => __( 'Default', 'directories-pro' ),
					'custom' => __( 'Custom', 'directories-pro' ),
				),
				'#horizontal'    => $options['horizontal'],
			),
			'name'  => array(
				'#type'             => 'radios',
				'#default_value'    => $value['name'] ?? '',
				'#options'          => array( '' => __( 'Default', 'directories-pro' ) ) + $this->options( $application, $options ),
				'#option_no_escape' => true,
				'#horizontal'       => $options['horizontal'],
				'#states'           => array(
					'visible' => array(
						sprintf( 'select[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( 'type' ) ) ) ) => array( 'value' => '' ),
					),
				),
				'#columns'          => 4,
			),
			'value' => array(
				'#type'          => 'colorpicker',
				'#default_value' => $value['value'] ?? null,
				'#horizontal'    => $options['horizontal'],
				'#states'        => array(
					'visible' => array(
						sprintf( 'select[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( 'type' ) ) ) ) => array( 'value' => 'custom' ),
					),
				),
			),
		);
		if ( isset( $options['bundle'] )
			&& $options['bundle'] instanceof Bundle
		) {
			$color_field_options = $application->Entity_Field_options(
				$options['bundle'],
				array(
					'interface'       => 'Field\Type\ColorType',
					'return_disabled' => true,
				)
			);
			if ( ! empty( $color_field_options[0] ) ) {
				$form['type']['#options']['field'] = __( 'Field - use color field value', 'directories-pro' );
				$form['field']                     = array(
					'#type'          => 'radios',
					'#default_value' => $value['field'] ?? null,
					'#options'       => $color_field_options[0],
					'#horizontal'    => $options['horizontal'],
					'#states'        => array(
						'visible' => array(
							sprintf( 'select[name="%s"]', $application->Form_Form_fieldName( array_merge( $parents, array( 'type' ) ) ) ) => array( 'value' => 'field' ),
						),
					),
				);
			}
			if ( ! empty( $options['include_term_color'] )
				&& ! empty( $options['bundle']->info['taxonomies'] )
			) {
				foreach ( $options['bundle']->info['taxonomies'] as $taxonomy_name ) {
					$tax_bundle = $application->Entity_Bundle( $taxonomy_name );
					if ( empty( $tax_bundle->info['entity_color'] ) ) {
						continue;
					}

					$form['type']['#options'][ 'tax-' . $tax_bundle->type ] = $tax_bundle->getLabel( 'singular' );
				}
			}
		} else {
			unset( $form['field'] );
		}

		return $form;
	}

	/**
	 * Converts a hex color to HSL.
	 *
	 * @param Application $application
	 * @param string      $hex
	 * @return array
	 */
	public function hexToHsl( Application $application, $hex ) {
		$rgb = $this->hexToRgb( $application, $hex );
		$max = max( $rgb );
		$min = min( $rgb );
		$l   = ( $max + $min ) / 2;

		if ( $max === $min ) {
			$h = $s = 0;
		} else {
			$diff = $max - $min;
			$s    = $l > 0.5 ? $diff / ( 2 - $max - $min ) : $diff / ( $max + $min );
			switch ( $max ) {
				case $rgb[0]:
					$h = ( $rgb[1] - $rgb[2] ) / $diff + ( $rgb[1] < $rgb[2] ? 6 : 0 );
					break;
				case $rgb[1]:
					$h = ( $rgb[2] - $rgb[0] ) / $diff + 2;
					break;
				case $rgb[2]:
					$h = ( $rgb[0] - $rgb[1] ) / $diff + 4;
					break;
			}
			$h /= 6;
		}

		return array( $h, $s, $l );
	}

	/**
	 * Converts HSL to a hex color.
	 *
	 * @param Application $application
	 * @param array       $hsl
	 * @return string
	 */
	public function hslToHex( Application $application, $hsl ) {
		list($h, $s, $l) = $hsl;

		if ( $s === 0 ) {
			$r = $g = $b = 1;
		} else {
			$q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
			$p = 2 * $l - $q;

			$r = $this->hueToRgb( $application, $p, $q, $h + 1 / 3 );
			$g = $this->hueToRgb( $application, $p, $q, $h );
			$b = $this->hueToRgb( $application, $p, $q, $h - 1 / 3 );
		}

		return $this->rgbToHex( $application, array( $r, $g, $b ) );
	}

	/**
	 * Helper for HSL to RGB conversion.
	 *
	 * @param Application $application
	 * @param float       $p
	 * @param float       $q
	 * @param float       $t
	 * @return float
	 */
	public function hueToRgb( Application $application, $p, $q, $t ) {
		if ( $t < 0 ) {
			$t += 1;
		}
		if ( $t > 1 ) {
			$t -= 1;
		}
		if ( $t < 1 / 6 ) {
			return $p + ( $q - $p ) * 6 * $t;
		}
		if ( $t < 1 / 2 ) {
			return $q;
		}
		if ( $t < 2 / 3 ) {
			return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
		}

		return $p;
	}

	/**
	 * Converts a hex color to RGB.
	 *
	 * @param Application $application
	 * @param string      $hex
	 * @param bool        $asInt
	 * @return array
	 */
	function hexToRgb( Application $application, $hex, $asInt = false ) {
		$hex = array( $hex[0] . $hex[1], $hex[2] . $hex[3], $hex[4] . $hex[5] );
		return array_map(
			function ( $part ) use ( $asInt ) {
				$part = hexdec( $part );
				return $asInt ? $part : $part / 255;
			},
			$hex
		);
	}

	/**
	 * Converts an RGB color to hex.
	 *
	 * @param Application $application
	 * @param array       $rgb
	 * @return string
	 */
	public function rgbToHex( Application $application, $rgb ) {
		$ret = '';
		foreach ( (array) $rgb as $_rgb ) {
			$ret .= str_pad( dechex( (int) round( $_rgb * 255 ) ), 2, '0', STR_PAD_LEFT );
		}
		return $ret;
	}
}
