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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Field\IField;
use SabaiApps\Directories\Exception\RuntimeException;
use SabaiApps\Directories\Exception\InvalidArgumentException;
use SabaiApps\Directories\Context;
use SabaiApps\Framework\Criteria\IsOrSmallerThanCriteria;

class MapHelper {

	protected static $_markerDefaultImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAQAAAAAlWljAAABIklEQVR42u3RAQ0AAAjDMK4c6aAD0klYM116XAADFmABFmABFmABBizAAizAAizAAgxYgAVYgAVYgAVYgAELsAALsAALsAADFmABFmABFmABBizAAizAAizAAizAgAVYgAVYgAVYgAELsAALsAALsAADBgxYgAVYgAVYgAUYsAALsAALsAALMGABFmABFmABFmABBizAAizAAizAAgxYgAVYgAVYgAUYsAALsAALsAALsAADFmABFmABFmABBizAAizAAizAAgzYBMACLMACLMACLMCABViABViABViAAQuwAAuwAAuwAAswYAEWYAEWYAEWYMACLMACLMACLMCABViABViABViABRiwAAuwAAuwAAswYAEWYAEWYAEWYMACrKst65UNXM2XNOgAAAAASUVORK5CYII=';
	protected static $_loadMarkerIcon;

	public function marker( Application $application, IEntity $entity, $fieldName, array $settings, $content = null, $checkGlobalLocationTerm = true ) {
		$term_ids = array();
		if ( $checkGlobalLocationTerm
			&& isset( $GLOBALS['drts_entity'] )
			&& $GLOBALS['drts_entity'] instanceof IEntity
			&& $GLOBALS['drts_entity']->isTaxonomyTerm()
			&& $GLOBALS['drts_entity']->getBundleType() === 'location_location'
		) {
			$term_ids[] = $GLOBALS['drts_entity']->getId();
			try {
				foreach ( $application->Entity_Types_impl( $GLOBALS['drts_entity']->getType() )->entityTypeDescendantEntityIds( $GLOBALS['drts_entity'] ) as $parent_term_id ) {
					$term_ids[] = $parent_term_id;
				}
			} catch ( RuntimeException $e ) {
				$application->logError( $e );
			}
		}

		$markers = array();
		if ( $values = $entity->getFieldValue( $fieldName ) ) {
			foreach ( $values as $key => $value ) {
				if ( ! $value['lat'] || ! $value['lng'] ) {
					continue;
				}

				if ( ! empty( $term_ids )
					&& ! in_array( $value['term_id'], $term_ids )
				) {
					continue;
				}

				$icon = null;
				if ( ! isset( self::$_loadMarkerIcon ) ) {
					self::$_loadMarkerIcon = (bool) $application->getComponent( 'Map' )->getConfig( 'map', 'marker_custom' );
				}
				if ( self::$_loadMarkerIcon
					&& ! isset( $icons )
				) {
					$icons = array();
					if ( empty( $settings['view_marker_icon'] )
						|| $settings['view_marker_icon'] === 'image'
					) {
						// Icon is entity image
						$size = isset( $settings['marker_size'] ) ? $this->_getMarkerSize( $settings['marker_size'] ) : 'icon';
						if ( $icon = $application->Entity_Image( $entity, $size ) ) {
							$icons[] = array( 'url' => $icon );
						} elseif ( $image_url = $application->System_NoImage_url( 'icon', $entity ) ) {
								$icons[] = array( 'url' => $image_url );
						}
					} else {
						// Icon is taxonomy term image/icon
						$term_bundle_type = $settings['view_marker_icon'];
						if ( $terms = $entity->getFieldValue( $term_bundle_type ) ) {
							$is_map_location_taxonomy = $application->Entity_BundleTypeInfo( $term_bundle_type, 'map_location_taxonomy' );
							foreach ( $terms as $term ) {
								if ( ! $icon = $this->_getMarkerTaxonomyTermIcon( $term ) ) {
									continue;
								}

								$icons[ $term->getId() ] = $icon;
								if ( ! $is_map_location_taxonomy ) {
									break;
								}
							}
						}
					}
				}
				if ( ! empty( $icons ) ) {
					if ( ! empty( $value['term_id'] )
						&& isset( $icons[ $value['term_id'] ] )
					) {
						$icon = $icons[ $value['term_id'] ];
					} elseif ( empty( $is_map_location_taxonomy ) ) {
							$icon = array_values( $icons )[0];
					}
				}

				if ( ! isset( $content ) ) {
					if ( ! isset( $image ) ) {
						$image_size = isset( $settings['marker_image_size'] ) ? $settings['marker_image_size'] : 'thumbnail';
						if ( ! $image = $application->Entity_Image( $entity, $image_size ) ) {
							$image = false;
						}
					}
					if ( ! isset( $permalink ) ) {
						if ( ! isset( $settings['marker_link'] ) || $settings['marker_link'] ) {
							$permalink = $application->Entity_Permalink(
								$entity,
								array(
									'atts' => array( 'class' => DRTS_BS_PREFIX . 'text-white' ),
									'skip_entity_title_filter' => ! isset( $settings['skip_entity_title_filter'] ) || ! empty( $settings['skip_entity_title_filter'] ),
								)
							);
						} else {
							$permalink = $application->H( $entity->getTitle() );
						}
					}
					$address  = $application->Filter(
						'map_marker_address',
						isset( $value['display_address'] ) ? $value['display_address'] : ( isset( $value['address'] ) ? $value['address'] : '' ),
						array( $entity, $value, $settings )
					);
					$_content = $this->_getMarkerContent( $application, $entity, $permalink, $image, $address );
				} else {
					$_content = $content;
				}

				$markers[ $key ] = array(
					'index'     => $key,
					'entity_id' => $entity->getId(),
					'content'   => $_content,
					'lat'       => $value['lat'],
					'lng'       => $value['lng'],
					'icon'      => $icon,
				);
			}
		}
		return $markers;
	}

	protected function _getMarkerTaxonomyTermIcon( IEntity $term ) {
		if ( $icon = $term->getCustomProperty( 'image_src' ) ) {
			return array( 'url' => $icon );
		}
		if ( $icon = $term->getCustomProperty( 'icon_src' ) ) {
			return array(
				'url'     => $icon,
				'is_full' => true,
			);
		}
		$icon  = $term->getCustomProperty( 'icon' );
		$color = $term->getCustomProperty( 'color' );
		if ( $icon || $color ) {
			return array(
				'icon'       => $icon,
				'icon_color' => $color,
			);
		}
	}

	protected function _getMarkerContent( Application $application, IEntity $entity, $permalink, $imageSrc, $address ) {
		if ( ! $content = $application->Filter( 'map_marker_content', null, array( $entity, $permalink, $imageSrc, $address ) ) ) {
			$image_html = '<img class="' . DRTS_BS_PREFIX . 'card-img drts-no-image" src="' . self::$_markerDefaultImage . '" alt="" />';
			if ( $imageSrc ) {
				$image_html .= '<img class="' . DRTS_BS_PREFIX . 'card-img" src="' . $imageSrc . '" alt="' . $application->H( $entity->getTitle() ) . '" />';
			}
			$content = sprintf(
				'<div class="%1$scard %1$sborder-0 %1$sbg-dark %1$stext-white drts-map-marker-content">
%2$s
<div class="%1$scard-img-overlay %1$sp-2">
<div class="%1$scard-title">%3$s</div>
<address class="drts-map-marker-address %1$scard-text">%4$s</address>
%5$s
</div></div>',
				DRTS_BS_PREFIX,
				$image_html,
				$permalink,
				$application->Htmlize( $address, true ),
				$application->Filter( 'map_marker_content_html', '', array( $entity ) )
			);
		}
		return $content;
	}

	protected function _getMarkerSize( $size ) {
		if ( ! is_numeric( $size ) ) {
			return $size;
		}

		if ( $size > 54 ) {
			return 'icon_xl';
		}

		return $size <= 38 ? 'icon' : 'icon_lg';
	}

	public function markerIconOptions( Application $application, Bundle $bundle ) {
		$ret = array();
		if ( $bundle->info['entity_image'] ) {
			$ret['image'] = __( 'Show image', 'directories-pro' );
		}
		if ( ! empty( $bundle->info['taxonomies'] ) ) {
			foreach ( $bundle->info['taxonomies'] as $taxonomy_bundle_type => $taxonomy ) {
				if ( ! $taxonomy_bundle = $application->Entity_Bundle( $taxonomy ) ) {
					continue;
				}

				if ( empty( $taxonomy_bundle->info['entity_image'] )
					&& empty( $taxonomy_bundle->info['entity_icon'] )
				) {
					continue;
				}

				$ret[ $taxonomy_bundle_type ] = __( 'Show taxonomy image/icon', 'directories-pro' )
					. ' - ' . $taxonomy_bundle->getLabel( 'singular' );
			}
		}
		if ( ! empty( $ret ) ) {
			$ret['default'] = __( 'Default', 'directories-pro' );
		}

		return $ret;
	}

	public function renderEntityMarkers( Application $application, array $entities, $fieldName, array $settings, $displayName = null, ?array $context = null, array $currentMarkerIds = array() ) {
		$markers = array();

		// Check for existing markers on the map
		if ( ! empty( $currentMarkerIds ) ) {
			foreach ( array_keys( $entities ) as $entity_key ) {
				if ( $existing_markers = $this->_markerFullyExistsOnMap( $entities[ $entity_key ], $fieldName, $currentMarkerIds ) ) {
					foreach ( $existing_markers as $marker ) {
						$markers[] = $marker;
					}
					unset( $entities[ $entity_key ] );
					continue;
				}
			}
		}

		if ( ! empty( $displayName )
			&& isset( $context )
		) {
			$pre_rendered = $application->Entity_Display_preRender( $entities, $displayName );
			foreach ( $pre_rendered['entities'] as $entity ) {
				ob_start();
				$application->Entity_Display( $entity, $displayName, $context );
				$content = ob_get_clean();
				foreach ( $application->Map_Map_marker( $entity, $fieldName, $settings, $content ) as $marker ) {
					$markers[] = $marker;
				}
			}
		} else {
			foreach ( $entities as $entity ) {
				foreach ( $application->Map_Map_marker( $entity, $fieldName, $settings ) as $marker ) {
					$markers[] = $marker;
				}
			}
		}
		return $markers;
	}

	protected function _markerFullyExistsOnMap( IEntity $entity, $fieldName, array $currentMarkerIds ) {
		if ( ! $values = $entity->getFieldValue( $fieldName ) ) {
			return false;
		}

		$existing_markers = array();
		foreach ( $values as $key => $value ) {
			if ( ! in_array( $entity->getId() . '-' . $key, $currentMarkerIds ) ) {
				return false;
			}
			$existing_markers[] = array(
				'index'     => $key,
				'entity_id' => $entity->getId(),
			);
		}
		return $existing_markers;
	}

	public function controlLabels() {
		return array(
			'label_control_fullscreen'         => __( 'Full screen', 'directories-pro' ),
			'label_control_exit_fullscreen'    => __( 'Exit full screen', 'directories-pro' ),
			'label_control_search_this_area'   => __( 'Search this area', 'directories-pro' ),
			'label_control_search_my_location' => __( 'Search my location', 'directories-pro' ),
		);
	}

	public function isGdprConsentRequired( Application $application ) {
		if ( ! $application->getComponent( 'Map' )->getConfig( 'map', 'require_consent' ) ) {
			return false;
		}

		if ( ! $this->_isGdprConsentRequired( $application ) ) {
			return false;
		}

		return $application->Filter( 'map_gdpr_consent_required', true );
	}

	protected function _isGdprConsentRequired( Application $application ) {
		if ( $application->getUser()->isAnonymous() ) {
			return ! (bool) $application->System_Cookie( 'map_gdpr_accept' );
		}

		if ( $application->getPlatform()->getEntityMeta( 'user', $application->getUser()->id, 'map_gdpr_accept' ) ) {
			return false;
		}
		if ( (bool) $application->System_Cookie( 'map_gdpr_accept' ) ) {
			$application->getPlatform()->setEntityMeta( 'user', $application->getUser()->id, 'map_gdpr_accept', true );
			return false;
		}

		return true;
	}

	public function gdprConsentForm( Application $application ) {
		$pp                    = $application->Map_Api()->mapApiInfo( 'privacy_policy' );
		return sprintf(
			'<div class="%1$salert %1$salert-warning">
    <div class="%1$smb-2">%2$s</div>
    <div class="%1$smb-2">%3$s</div>
    <div class="%1$sd-grid">
        <button type="button" class="%1$smt-3 %1$sbtn %1$sbtn-secondary" onclick="DRTS.setCookie(\'%4$s\', true, 7, true);">%5$s</button>
    </div>
</div>',
			DRTS_BS_PREFIX,
			$application->H( __( 'To protect your personal data, your connection to the embedded map has been blocked.', 'directories-pro' ) ),
			$application->Htmlize(
				sprintf(
					__( 'Click the <strong>%1$s</strong> button below to load the map. By loading the map you accept the privacy policy of %2$s.', 'directories-pro' ),
					$btn_label = $application->H( __( 'Load map', 'directories-pro' ) ),
					'<a href="' . $pp['url'] . '" target = "_blank" rel = "nofollow">' . $application->H( $pp['provider'] ) . '</a>'
				),
				true
			),
			$application->System_Cookie_name( 'map_gdpr_accept' ),
			$btn_label
		);
	}

	public function isNearByCriteria( Application $application, $field, $lat, $lng, $radius = 100 ) {
		if ( $field instanceof IField ) {
			$field_type = $field->getFieldType();
			$field_name = $field->getFieldName();
		} elseif ( is_array( $field ) ) {
			if ( empty( $field[0] )
				|| empty( $field[1] )
			) {
				throw new InvalidArgumentException();
			}

			$field_type = $field[0];
			$field_name = $field[1];
		} elseif ( is_string( $field ) ) {
			$field_type = $field_name = $field;
		} else {
			throw new InvalidArgumentException();
		}

		$target = array(
			'tables'      => array(
				$application->getDB()->getResourcePrefix() . 'entity_field_' . $field_type  => array(
					'alias'      => $field_type,
					'on'         => null,
					'field_name' => $field_name,
				),
			),
			'column'      => sprintf(
				'(%1$d * acos(cos(radians(%2$.6F)) * cos(radians(%4$s.lat)) * cos(radians(%4$s.lng) - radians(%3$.6F)) + sin(radians(%2$.6F)) * sin(radians(%4$s.lat))))',
				$application->getComponent( 'Map' )->getConfig( 'map', 'distance_unit' ) === 'mi' ? 3959 : 6371,
				$lat,
				$lng,
				$field_type
			),
			'column_type' => Application::COLUMN_DECIMAL,
		);

		return new IsOrSmallerThanCriteria( $target, $radius );
	}

	public function controlBtn( Application $application, $control, array $options ) {
		$options += array(
			'class'     => DRTS_BS_PREFIX . 'mb-2',
			'btn_class' => null,
			'attr'      => array(),
		);
		switch ( $control ) {
			case 'geolocate':
				$icon_class               = 'fas fa-location-arrow';
				$options['attr']['style'] = 'display:none;';
				$options['attr']['title'] = __( 'Use my location', 'directories-pro' );
				break;
			case 'fullscreen':
				if ( ! empty( $options['is_fullscreen'] ) ) {
					$icon_class               = 'fas fa-compress';
					$options['attr']['title'] = __( 'Exit full screen', 'directories-pro' );
					break;
				}
				$icon_class               = 'fas fa-expand';
				$options['attr']['title'] = __( 'Full screen', 'directories-pro' );
				break;
			default:
				throw new InvalidArgumentException( 'Invalid map control specified: ' . $control );
		}

		return sprintf(
			'<button type="button" class="drts-map-map-control %1$s" data-action="%2$s"%4$s><i class="%3$s"></i></button>',
			isset( $options['btn_class'] ) ? $application->H( $options['btn_class'] ) : '',
			$control,
			$icon_class,
			empty( $options['attr'] ) ? '' : $application->Attr( $options['attr'] )
		);
	}

	public function getDrawOptions( Application $application, array $settings = array(), ?Context $context = null ) {
		if ( ! isset( $context->search_values['drts_search_location_address'] ) ) {
			return $settings;
		}

		// Searching by location
		$location_value = $context->search_values['drts_search_location_address'];
		if ( ! empty( $location_value['radius'] )
			&& ! empty( $location_value['center'] )
			&& ( $center = explode( ',', $location_value['center'] ) )
			&& count( $center ) === 2
		) {
			// Radius search
			$settings['center'] = array( $center[0], $center[1] );
			// Show radius circle?
			if ( ! isset( $settings['circle'] ) || $settings['circle'] !== false ) {
				$settings['circle'] = array(
					'stroke_color' => '#9999ff',
					'fill_color'   => '#9999ff',
					'radius'       => $location_value['radius'] * ( $application->getComponent( 'Map' )->getConfig( 'map', 'distance_unit' ) === 'mi' ? 1609.344 : 1000 ),
				);
			}
		} elseif ( ! empty( $location_value['viewport'] ) ) {
				// Location search with auto radius
				$settings['center'] = array(
					( $location_value['viewport'][0] + $location_value['viewport'][2] ) / 2,
					( $location_value['viewport'][1] + $location_value['viewport'][3] ) / 2,
				);
				if ( ! empty( $location_value['zoom'] ) ) {
					$settings['zoom'] = intval( $location_value['zoom'] );
				}
				// $settings['fit_bounds'] = isset($location_value['radius']); // location search with auto radius if radius is set

		}

		return $settings;
	}
}
