<?php
namespace SabaiApps\Directories\Component\View;

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Application;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\Entity\Model\Bundle;

class ViewComponent extends AbstractComponent implements
	Display\IElements,
	System\IMainRouter,
	System\IAdminRouter,
	IModes,
	Field\IFilters,
	Display\IIcons {

	const VERSION = '2.0.6', PACKAGE = 'directories-pro';

	public static function description() {
		return 'Provides functions and features to display content in various formats and layouts.';
	}

	public static function interfaces() {
		return array( 'WordPress\IBlocks' );
	}

	public function getDefaultConfig() {
		return array(
			'filters' => array(
				'facet_count' => true,
				'btn_icon'    => 'fas fa-sliders',
				'btn_label'   => '',
			),
		);
	}

	public function systemMainRoutes( $lang = null ) {
		$routes = array();

		foreach ( $this->_application->Entity_Bundles() as $bundle ) {
			if ( empty( $bundle->info['parent'] ) ) {
				if ( empty( $bundle->info['public'] )
					|| $this->_application->Entity_BundleTypeInfo( $bundle, 'dashboard_disable' )
				) {
					continue;
				}

				$routes[ $base_path               = $bundle->getPath( false, $lang ) ] = array(
					'controller'      => 'ViewEntities',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => empty( $bundle->info['is_taxonomy'] ) ? 'entities' : 'taxonomy_entities',
					'data'            => array(
						'bundle_type'  => $bundle->type,
						'bundle_group' => $bundle->group,
					),
					'priority'        => 3,
				);
				$routes[ $base_path . '/filter' ] = array(
					'controller' => 'FilterEntities',
					'priority'   => 3,
				);
				$route_key                        = ':slug...';
				if ( ! empty( $bundle->info['is_taxonomy'] ) ) {
					$controller    = 'ViewEntities';
					$callback_path = 'taxonomy_entity';
				} else {
					if ( ! empty( $bundle->info['is_user'] ) ) {
						$route_key = ':slug';
					}
					$controller    = 'ViewEntity';
					$callback_path = 'entity';
				}
				$routes[ $bundle->getPath( true, $lang ) . '/' . $route_key ] = array(
					'controller'      => $controller,
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => $callback_path,
					'format'          => array( $route_key => '[a-z0-9~\.:_\-%]+' ),
					'priority'        => 3,
				);
			} else {
				if ( ! $this->_application->Entity_Bundle( $bundle->info['parent'] ) ) {
					return $routes;
				}

				if ( ! empty( $bundle->info['public'] )
					|| ! $this->_application->Entity_BundleTypeInfo( $bundle, 'dashboard_disable' )
				) {
					$routes[ $bundle->getPath( false, $lang ) ] = array(
						'controller'      => 'ViewEntities',
						'access_callback' => true,
						'callback_path'   => 'child_entities',
						'data'            => array(
							'bundle_type'  => $bundle->type,
							'bundle_group' => $bundle->group,
							// 'embed_only' => true,
						),
						'priority'        => 3,
					);
				}
				if ( ! empty( $bundle->info['public'] ) ) {
					$routes[ $bundle->getPath( true, $lang ) . '/:entity_id' ] = array(
						'controller'      => 'ViewEntity',
						'format'          => array( ':entity_id' => '\d+' ),
						'access_callback' => true,
						'title_callback'  => true,
						'callback_path'   => 'child_entity',
						'priority'        => 3,
					);
				}
			}
		}

		return $routes;
	}

	protected function _getBundle( array $route ) {
		return $this->_application->Entity_Bundle( $route['data']['bundle_type'], $route['data']['bundle_group'] );
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'entities':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $bundle = $this->_getBundle( $route ) ) {
						return false;
					}

					$context->bundle = $bundle;
					// Set the default error URL
					$context->setErrorUrl( $route['path'] );

					return true;
				} else {
					return $this->_application->Entity_IsRoutable( $context->bundle, 'list' );
				}
			case 'taxonomy_entities':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $bundle = $this->_getBundle( $route ) )
						|| empty( $bundle->info['is_taxonomy'] )
					) {
						return false;
					}

					if ( $_bundle = $bundle->isAlias() ) {
						$context->original_taxonomy_bundle = $bundle;
						$context->taxonomy_bundle          = $_bundle;
					} else {
						$context->taxonomy_bundle = $bundle;
					}
				}
				return true;
			case 'child_entities':
				if ( ! $bundle = $this->_getBundle( $route ) ) {
					return false;
				}

				$context->child_bundle = $bundle;

				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					return true;
				}

				return $this->_application->Entity_IsRoutable( $bundle, 'list', isset( $context->entity ) ? $context->entity : null );
			case 'entity':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					$slug = $context->getRequest()->asStr( 'slug' );
					if ( ! strlen( $slug )
						|| ( ! $slug = rawurldecode( $slug ) )
						|| ( ! $entity = $this->_application->Entity_Types_impl( $context->bundle->entitytype_name )->entityTypeEntityBySlug( $context->bundle->name, $slug ) )
					) {
						if ( ! is_numeric( $slug )
							|| ( ! $entity = $this->_application->Entity_Types_impl( $context->bundle->entitytype_name )->entityTypeEntityById( $slug ) )
						) {
							$context->setNotFoundError();
							return false;
						}
					}

					if ( ! $this->_application->Entity_IsRoutable( $context->bundle, 'link', $entity ) ) {
						// Suppress error message by default
						$error = $this->_application->Filter( 'view_entity_unroutable_error', '', array( $entity, $this->_application->getUser(), 'link' ) );
						if ( $this->_application->getUser()->isAnonymous() ) {
							$context->setUnauthorizedError( null, $error );
						} else {
							$context->setError( $error );
						}
						return false;
					}

					$this->_application->Entity_Field_load( $entity );

					if ( ( $slugs = $context->getRequest()->asStr( 'slug...' ) )
						&& ! empty( $context->bundle->info['permalink_path'] )
					) {
						$permalink_path  = $context->bundle->info['permalink_path'];
						$permalink_paths = explode( '/', $permalink_path );
						array_pop( $permalink_paths );
						if ( empty( $permalink_paths ) ) {
							$context->setNotFoundError();
							return false;
						}
						$slugs = explode( '/', $slugs );
						array_pop( $slugs );
						$_slug      = array_pop( $slugs );
						$term_found = false;
						while ( $permalink_path_part = array_pop( $permalink_paths ) ) {
							$child_term_found     = null;
							$taxonomy_bundle_type = trim( $permalink_path_part, '%' );
							if ( ! $taxonomy_bundle = $this->_application->Entity_Bundle( $taxonomy_bundle_type, $context->bundle->group ) ) {
								$context->setNotFoundError();
								return false;
							}
							if ( $_taxonomy_bundle = $taxonomy_bundle->isAlias() ) {
								$taxonomy_bundle = $_taxonomy_bundle;
							}

							while ( $term_found = $this->_application->Entity_Types_impl( $taxonomy_bundle->entitytype_name )->entityTypeEntityBySlug( $taxonomy_bundle->name, $_slug ) ) {
								if ( isset( $child_term_found ) ) {
									// Make sure valid parent term
									if ( $term_found->getId() !== $child_term_found->getParentId() ) {
										$context->setNotFoundError();
										return false;
									}
								} else {
									// First term, make sure entity belongs to this term
									if ( ! $entity_terms = $entity->getFieldValue( $taxonomy_bundle_type ) ) {
										$context->setNotFoundError();
										return false;
									}
									$entity_term_found = false;
									foreach ( $entity_terms as $entity_term ) {
										if ( $entity_term->getId() === $term_found->getId() ) {
											$entity_term_found = true;
											break;
										}
									}
									if ( ! $entity_term_found ) {
										$context->setNotFoundError();
										return false;
									}
								}
								$child_term_found = $term_found;
								if ( ! $_slug = array_pop( $slugs ) ) {
									break 2;
								}
							}
						}
						if ( ! $term_found ) {
							$context->setNotFoundError();
							return false;
						}
					}

					$route['permalink_url'] = $this->_application->Entity_PermalinkUrl( $entity );
					$context->entity        = $entity;
					if ( in_array( $context->getContainer(), array( '#drts-content', '#drts-modal' ) ) ) {
						$GLOBALS['drts_entity'] = $entity;
						if ( isset( $context->original_taxonomy_bundle ) ) {
							$GLOBALS['drts_original_taxonomy_bundle_name'] = $context->original_taxonomy_bundle->name;
						}
					}
					return true;
				} else {
					if ( ! $this->_application->Entity_IsRoutable( $context->bundle, 'view', $context->entity ) ) {
						// Suppress error message by default
						$error = $this->_application->Filter( 'view_entity_unroutable_error', '', array( $context->entity, $this->_application->getUser(), 'view' ) );
						if ( $this->_application->getUser()->isAnonymous() ) {
							$context->setUnauthorizedError( null, $error );
						} else {
							$context->setError( $error );
						}
						return false;
					}
					return true;
				}
			case 'taxonomy_entity':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					$slug = $context->getRequest()->asStr( 'slug' );
					if ( ! strlen( $slug )
						|| ( ! $slug = rawurldecode( $slug ) )
						|| ( ! $entity = $this->_application->Entity_Types_impl( $context->taxonomy_bundle->entitytype_name )->entityTypeEntityBySlug( $context->taxonomy_bundle->name, $slug ) )
						|| ! $entity->isPublished()
					) {
						if ( ! is_numeric( $slug )
							|| ( ! $entity = $this->_application->Entity_Types_impl( $context->taxonomy_bundle->entitytype_name )->entityTypeEntityById( $slug ) )
							|| ! $entity->isPublished()
						) {
							$context->setNotFoundError();
							return false;
						}
					}

					$this->_application->Entity_Field_load( $entity );
					$route['permalink_url'] = $this->_application->Entity_PermalinkUrl( $entity, '', null, array(), $context->original_taxonomy_bundle ?? null );
					$context->entity        = $entity;

					if ( $slugs = $context->getRequest()->asStr( 'slug...' ) ) {
						$slugs = explode( '/', $slugs );
						array_pop( $slugs );
						foreach ( array_reverse( $slugs ) as $_slug ) {
							if ( ! $term = $this->_application->Entity_Types_impl( $context->taxonomy_bundle->entitytype_name )->entityTypeEntityBySlug( $context->taxonomy_bundle->name, $_slug ) ) {
								$context->setNotFoundError();
								return false;
							}
							if ( isset( $parent_term )
								&& $parent_term->getParentId() !== $term->getId()
							) {
								$context->setNotFoundError();
								return false;
							}
							$parent_term = $term;
						}
					}

					if ( in_array(
						$context->getContainer(),
						array(
							'#drts-content',
							'#drts-content-view-term-entities', // requested via AJAX on single term page
						)
					) ) {
						$GLOBALS['drts_entity'] = $entity;
					}
					$context->_taxonomy_bundle = $context->taxonomy_bundle;
					unset( $context->taxonomy_bundle );
				}
				return true;
			case 'child_entity':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $id = $context->getRequest()->asInt( 'entity_id' ) )
						|| ( ! $entity = $this->_application->Entity_Types_impl( $context->child_bundle->entitytype_name )->entityTypeEntityById( $id ) )
						|| $entity->getBundleName() !== $context->child_bundle->name
					) {
						$context->setNotFoundError();
						return false;
					}

					if ( ! $this->_application->Entity_IsRoutable( $context->child_bundle, 'link', $entity ) ) {
						$context->setError( $this->_application->Filter( 'view_entity_unroutable_error', '', array( $entity, 'link' ) ) ); // suppress error message by default
						return false;
					}

					$this->_application->Entity_Field_load( $entity );
					$context->entity = $entity;
					if ( $context->getContainer() === '#drts-content' ) {
						$GLOBALS['drts_entity'] = $entity;
					}
					return true;
				} else {
					if ( ! $this->_application->Entity_IsRoutable( $context->child_bundle, 'view', $context->entity ) ) {
						$context->setError( $this->_application->Filter( 'view_entity_unroutable_error', '', array( $context->entity, 'view' ) ) ); // suppress error message by default
						return false;
					}
					return true;
				}
		}
	}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'entities':
				return $titleType === Application::ROUTE_TITLE_TAB
					? $context->bundle->getLabel()
					: $this->_application->getComponent( $context->bundle->component )->getTitle( $context->bundle->group );
			case 'taxonomy_entities':
				// taxonomy_bundle is null if viewing single term and called for breadcrumb
				return $context->taxonomy_bundle ? $context->taxonomy_bundle->getLabel() : $context->_taxonomy_bundle->getLabel();
			case 'entity':
			case 'taxonomy_entity':
				return $this->_application->Entity_Title( $context->entity );
			case 'child_entity':
				// Child entities do not have an archive index, so render with bundle label
				return sprintf( $context->child_bundle->getLabel( 'page' ), $this->_application->Entity_Title( $context->entity ) );
		}
	}

	protected function _hasSupportedFilter( array $filters, Field\IField $field ) {
		foreach ( array_keys( $filters ) as $filter_type ) {
			if ( ( $filter = $this->_application->Field_Filters_impl( $filter_type, true ) )
				&& $filter->fieldFilterSupports( $field )
			) {
				return true;
			}
		}
		return false;
	}

	public function displayGetElementNames( Bundle $bundle ) {
		$ret         = array();
		$field_types = $this->_application->Field_Types();
		foreach ( $this->_application->Entity_Field( $bundle->name ) as $field ) {
			if ( ! $field_type = @$field_types[ $field->getFieldType() ] ) {
				continue;
			}
			if ( isset( $field_type['entity_types'] )
				&& ! in_array( $bundle->entitytype_name, $field_type['entity_types'] )
			) {
				// the field type does not support the entity type of the current bundle
				continue;
			}

			if ( ! empty( $bundle->info['no_title'] )
				&& $field->getFieldType() === 'entity_title'
			) {
				continue;
			}

			if ( ! empty( $field_type['filters'] )
				&& $this->_hasSupportedFilter( $field_type['filters'], $field )
			) {
				$ret[] = 'view_filter_' . $field->getFieldName();
			}
		}

		if ( empty( $bundle->info['is_taxonomy'] ) ) {
			if ( empty( $bundle->info['parent'] )
				&& empty( $bundle->info['parent_field'] )
			) {
				foreach ( $this->_application->Entity_BundleTypes_children( $bundle->type ) as $bundle_type ) {
					try {
						$this->_application->Entity_Bundle( $bundle_type, $bundle->group, true );
					} catch ( Exception\IException $e ) {
						continue;
					}

					$ret[] = 'view_child_entities_' . $bundle_type;
				}
				foreach ( $this->_application->Entity_Bundles_referencing( $bundle->name ) as $field_name => $bundle_names ) {
					foreach ( $bundle_names as $bundle_name ) {
						$ret[] = 'view_referencing_entities_' . $bundle_name . '-' . $field_name;
					}
				}
			}
		} elseif ( $content_bundle_name = $this->_application->Entity_TaxonomyContentBundleTypes( $bundle->type, $bundle->group ) ) {
				$ret[] = 'view_term_entities_' . $content_bundle_name;
		}

		return $ret;
	}

	public function displayGetElement( $name ) {
		if ( strpos( $name, 'view_filter_' ) === 0 ) {
			return new DisplayElement\FilterDisplayElement( $this->_application, $name );
		} elseif ( strpos( $name, 'view_child_entities_' ) === 0 ) {
			return new DisplayElement\ChildEntitiesDisplayElement( $this->_application, $name );
		} elseif ( strpos( $name, 'view_referencing_entities_' ) === 0 ) {
			return new DisplayElement\ReferencingEntitiesDisplayElement( $this->_application, $name );
		} elseif ( strpos( $name, 'view_term_entities_' ) === 0 ) {
			return new DisplayElement\TermEntitiesDisplayElement( $this->_application, $name );
		}
	}

	public function viewGetModeNames() {
		return array( 'list', 'masonry', 'table' );
	}

	public function viewGetMode( $name ) {
		switch ( $name ) {
			case 'list':
				return new Mode\ListMode( $this->_application, $name );
			case 'masonry':
				return new Mode\MasonryMode( $this->_application, $name );
			case 'table':
				return new Mode\TableMode( $this->_application, $name );
		}
	}

	public function systemAdminRoutes() {
		$routes = array();
		foreach ( array_keys( $this->_application->Entity_BundleTypes() ) as $bundle_type ) {
			if ( ( ! $admin_path = $this->_application->Entity_BundleTypeInfo( $bundle_type, 'admin_path' ) )
				|| isset( $routes[ $admin_path . '/views' ] ) // path added already
				|| ! $this->_application->Entity_BundleTypeInfo( $bundle_type, 'public' )
			) {
				continue;
			}

			$routes += array(
				$admin_path . '/views'                 => array(
					'controller'      => 'Views',
					'title_callback'  => true,
					'access_callback' => true,
					'callback_path'   => 'views',
					'type'            => Application::ROUTE_TAB,
					'weight'          => 15,
				),
				$admin_path . '/views/:view_id'        => array(
					'controller'      => 'EditView',
					'title_callback'  => true,
					'access_callback' => true,
					'callback_path'   => 'edit_view',
					'format'          => array( ':view_id' => '\d+' ),
				),
				$admin_path . '/views/:view_id/delete' => array(
					'controller'     => 'DeleteView',
					'title_callback' => true,
					'callback_path'  => 'delete_view',
				),
				$admin_path . '/views/:view_id/clone'  => array(
					'controller'     => 'CloneView',
					'title_callback' => true,
					'callback_path'  => 'clone_view',
				),
				$admin_path . '/views/add'             => array(
					'controller'     => 'AddView',
					'title_callback' => true,
					'callback_path'  => 'add_view',
					'type'           => Application::ROUTE_MENU,
					'data'           => array(
						'link_options' => array(
							'icon'      => 'fas fa-plus',
							'container' => 'modal',
							'modalSize' => 'xl',
							'cache'     => 'drts-view-add-view',
						),
						'link_attr'    => array(
							'class' => DRTS_BS_PREFIX . 'btn ' . DRTS_BS_PREFIX . 'btn-success',
						),
					),
					'weight'         => 1,
				),
				$admin_path . '/views/filters'         => array(
					'controller'      => 'Filters',
					'title_callback'  => true,
					'access_callback' => true,
					'callback_path'   => 'filters',
					'type'            => Application::ROUTE_MENU,
					'weight'          => 2,
				),
				$admin_path . '/views/set_default'     => array(
					'controller' => 'SetDefaultView',
					'type'       => Application::ROUTE_CALLBACK,
					'method'     => 'post',
				),
			);
		}
		return $routes;
	}

	public function systemOnAccessAdminRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'views':
				if ( $context->bundle->isAlias() ) {
					return false;
				}
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					return ! empty( $context->bundle->info['public'] );
				}
				return true;
			case 'edit_view':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $view_id = $context->getRequest()->asInt( 'view_id' ) )
						|| ( ! $view = $this->getModel( 'View' )->fetchById( $view_id ) )
						|| $view->bundle_name !== $context->bundle->name
					) {
						return false;
					}

					// Compat for <1.2.42
					if ( $view->mode === 'photoslider' ) {
						$view->mode = 'slider_photos';
						$view->commit();
					}

					$context->view = $view;
				}
				return true;
			case 'filters':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					return $this->isFilterable( $context->bundle );
				}
				return true;
		}
	}

	public function systemAdminRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'views':
				return __( 'Manage Views', 'directories-pro' );
			case 'edit_view':
				return $context->view->getLabel();
			case 'delete_view':
				return __( 'Delete View', 'directories-pro' );
			case 'clone_view':
				return __( 'Clone View', 'directories-pro' );
			case 'add_view':
				return __( 'Add View', 'directories-pro' );
			case 'filters':
				return __( 'Manage Filters', 'directories-pro' );
		}
	}

	public function fieldGetFilterNames() {
		return array( 'view_term_list', 'view_term_select', 'view_glossary' );
	}

	public function fieldGetFilter( $name ) {
		switch ( $name ) {
			case 'view_term_list':
				return new FieldFilter\TermListFieldFilter( $this->_application, $name );
			case 'view_term_select':
				return new FieldFilter\TermSelectFieldFilter( $this->_application, $name );
			case 'view_glossary':
				return new FieldFilter\GlossaryFieldFilter( $this->_application, $name );
		}
	}

	public function isFilterable( $bundle ) {
		$bool = ! empty( $bundle->info['public'] )
			&& empty( $bundle->info['is_taxonomy'] );
		return $this->_application->Filter( 'view_is_filterable', $bool, array( $bundle ) );
	}

	public function displayGetIconNames( Bundle $bundle ) {
		return array( 'view_top_ranking' );
	}

	public function displayGetIcon( $name ) {
		return new DisplayIcon\TopRankingImageDisplayIcon( $this->_application, $name );
	}

	public function onEntityBundleAdminLinksFilter( &$links, $bundle ) {
		if ( empty( $bundle->info['public'] )
			|| $bundle->isAlias()
		) {
			return;
		}

		$links['views'] = array(
			'weight' => 15,
			'link'   => array(
				'path'  => '/views',
				'label' => __( 'Manage Views', 'directories-pro' ),
			),
		);
	}

	public function onDirectoryShortcodesFilter( &$shortcodes ) {
		$shortcodes['view']        = array( 'path' => '' );
		$shortcodes['view-filter'] = array( 'path' => '/filter' );
	}

	public function onDirectoryDoShortcodeFilter( &$ret, $name, $bundle ) {
		if ( ! in_array( $name, array( 'view', 'view-filter' ) ) ) {
			return;
		}

		$atts            =& $ret['atts'];
		$is_default_view = false;
		if ( $name === 'view-filter'
			|| ! isset( $atts['name'] )
			|| ! strlen( $atts['name'] = trim( $atts['name'] ) )
		) {
			$is_default_view = true;
		} elseif ( $view = $this->getModel( 'View', 'View' )->bundleName_is( $bundle->name )->name_is( $atts['name'] )->fetchOne() ) {
			if ( $view->isDefault() ) {
				$is_default_view = true;
			} else {
				$atts['name']            = $view->name;
				$atts['mode']            = $view->mode;
				$atts['view_settings']   = $view->data['settings'];
				$atts['is_default_view'] = false;
			}
		} else {
			throw new Exception\RuntimeException( 'Directory shortcode [view]: invalid "name" attribute.' );
		}
		if ( $is_default_view ) {
			if ( $name === 'view' ) {
				if ( ! empty( $atts['filter_only'] ) ) {
					$ret = '';
					if ( empty( $atts['hide_on_mobile'] )
						|| ! $this->_application->isMobile()
					) {
						$ret = '<div class="drts-view-entities-filter-form-placeholder" data-bundle-group="' . $atts['directory'] . '" data-bundle-type="' . $atts['type'] . '"></div>';
					}
					return;
				}
				$atts = array();
			} elseif ( $name === 'view-filter' ) {
				$_atts = $atts;
				$atts  = array();
				foreach ( array( 'style', 'class', 'btn_size', 'btn_color' ) as $key ) {
					if ( isset( $_atts[ $key ] ) && is_scalar( $_atts[ $key ] ) && strlen( $_atts[ $key ] = trim( $_atts[ $key ] ) ) ) {
						$atts[ $key ] = $_atts[ $key ];
					}
				}
			}
			return;
		}

		unset( $atts['view'] );

		// Override with custom settings if any
		foreach ( array_keys( $atts ) as $key ) {
			if ( in_array( $key, array( 'is_default_view', 'view_settings', 'filter_only', 'hide_on_mobile', 'btn_size', 'btn_color' ) )
				|| ! is_scalar( $atts[ $key ] )
				|| ( ! $pos = strpos( $key, '_' ) )
			) {
				continue;
			}

			$value = trim( $atts[ $key ] );
			$type  = substr( $key, 0, $pos );
			$_key  = substr( $key, $pos + 1 );
			switch ( $key ) {
				case 'sort_default':
				case 'sort_secondary':
				case 'filter_display':
					$atts['view_settings'][ $type ][ $_key ] = $value;
					break;
				case 'pagination_no_pagination':
				case 'pagination_allow_perpage':
				case 'filter_show':
				case 'filter_shown':
				case 'filter_show_modal':
				case 'sort_stick_featured':
				case 'sort_stick_featured_term_only':
				case 'sort_payment_plans':
					$atts['view_settings'][ $type ][ $_key ] = empty( $value ) ? 0 : 1;
					break;
				case 'pagination_perpage':
				case 'query_limit':
					$atts['view_settings'][ $type ][ $_key ] = (int) $value;
					break;
				case 'sort_options':
					if ( $value = explode( ',', $value ) ) {
						$atts['view_settings'][ $type ][ $_key ] = $value;
					}
					break;
				case 'pagination_perpages':
				case 'sort_payment_plans_order':
					if ( $value = explode( ',', $value ) ) {
						$atts['view_settings'][ $type ][ $_key ] = array_filter( array_map( 'intval', $value ) );
					}
					break;
				case 'query_fields':
					parse_str( str_replace( '&amp;', '&', $value ), $fields );
					if ( ! empty( $fields ) ) {
						foreach ( $fields as $field_name => $query_str ) {
							if ( is_array( $query_str ) ) {
								$query_str = array_pop( $query_str );
							}
							$atts['view_settings'][ $type ][ $_key ][ $field_name ] = $query_str;
						}
					}
					break;
				default:
			}
		}
	}

	public function onEntityDeleteBundlesCommitted( array $bundles, $deleteContent ) {
		// Delete views
		foreach ( $this->getModel( 'View' )->bundleName_in( array_keys( $bundles ) )->fetch() as $view ) {
			$view->markRemoved();
		}
		// Delete filters
		foreach ( $this->getModel( 'Filter' )->bundleName_in( array_keys( $bundles ) )->fetch() as $filter ) {
			$filter->markRemoved();
		}
		$this->getModel()->commit();
	}

	public function onViewQueryEntities( Context $context ) {
		if ( ! isset( $context->entity ) || ! $context->entity->isTaxonomyTerm() ) {
			return;
		}

		if ( $this->_application->Filter( 'view_term_entities_query_by_term_id', true, array( $context->entity, $context->query, $context ) ) ) {
			// Query by term
			$bundle_type       = $context->entity->getBundleType();
			$ignore_auto_terms = ! $this->_application->Entity_BundleTypeInfo( $bundle_type, 'is_hierarchical' );
			$context->query->taxonomyTermIdIs(
				$bundle_type,
				$context->entity->getId(),
				$this->_application->Filter( 'view_term_entities_ignore_auto_terms', $ignore_auto_terms, array( $bundle_type ) ),
				$bundle_type . '_view_term', // table alias
				$bundle_type . '_view_term' // criteria name
			);
		}

		// Show detailed display of term and list of entities
		$context->setTitle( $this->_application->Entity_PageTitle( $context->entity, true ) );
		if ( ! $context->getRequest()->isAjax() ) {
			$display = 'detailed';
			$context->addTemplate( 'view_term_entities' )
				->setAttributes(
					array(
						'display' => $display,
					)
				);
			// Invoke other components
			$this->_application->Action( 'view_entity', array( $context->entity, $display, $context ) );
		}
		if ( ! strpos( $context->getContainer(), 'drts-platform-render-' ) ) { // make sure not requested from term entities display element
			if ( ! strpos( $context->getContainer(), '-view-term-entities' ) ) {
				$context->settings['filter']['target'] = $context->getContainer() . '-view-term-entities';
			}
		}
	}

	public function onDirectoryAdminSettingsFormFilter( &$form ) {
		// Moved from Directory to System component
		$this->onSystemAdminSettingsFormFilter( $form );
	}

	public function onSystemAdminSettingsFormFilter( &$form ) {
		$form['fields'][ $this->_name ] = array(
			'#submit'    => array(
				9 => array( // weight
					function ( Form\Form $form ) {
						if ( $btn_label = $form->getValue( $this->_name, 'filters', 'btn_label' ) ) {
							$this->_application->getPlatform()->registerString(
								$btn_label,
								'nav_filter_btn_label',
								'view'
							);
						}
					},
				),
			),
			'#component' => $this->_name,
			'#tab'       => 'Directory',
			'filters'    => array(
				'#title'      => __( 'Filter Settings', 'directories-pro' ),
				'facet_count' => array(
					'#type'          => 'checkbox',
					'#title'         => __( 'Enable facet count', 'directories-pro' ),
					'#default_value' => ! empty( $this->_config['filters']['facet_count'] ),
					'#horizontal'    => true,
				),
				'btn_icon'    => array(
					'#type'          => 'iconpicker',
					'#title'         => __( 'Filter button icon', 'directories-pro' ),
					'#default_value' => $this->_config['filters']['btn_icon'],
					'#horizontal'    => true,
				),
				'btn_label'   => array(
					'#type'          => 'textfield',
					'#title'         => __( 'Filter button label', 'directories-pro' ),
					'#default_value' => $this->_config['filters']['btn_label'],
					'#horizontal'    => true,
				),
				'auto_submit' => array(
					'#title'         => __( 'Auto submit filter form', 'directories-pro' ),
					'#type'          => 'checkbox',
					'#default_value' => ! isset( $this->_config['filters']['auto_submit'] ) || $this->_config['filters']['auto_submit'],
					'#horizontal'    => true,
				),
			),
		);
	}

	public function upgrade( $current, $newVersion, ?System\Progress $progress = null ) {
		parent::upgrade( $current, $newVersion, $progress );
		if ( version_compare( $current->version, '1.2.37-dev.1', '<' ) ) {
			$db  = $this->_application->getDB();
			$sql = sprintf(
				'UPDATE %1$sview_filter SET filter_display_name = %2$s WHERE filter_display_name = \'\'',
				$db->getResourcePrefix(),
				$db->escapeString( 'default' )
			);
			try {
				$db->exec( $sql );
			} catch ( \Exception $e ) {
			}
		}
		return $this;
	}

	public function wordpressGetBlockNames() {
		return array( 'view', 'view_filter' );
	}

	public function wordpressGetBlock( $name ) {
		switch ( $name ) {
			case 'view':
				return new WordPressBlock\ViewWordPressBlock( $this->_application, $name );
			case 'view_filter':
				return new WordPressBlock\ViewFilterWordPressBlock( $this->_application, $name );
		}
	}

	public function onDisplayAdminElementsFormFilter( &$form, $bundle, $display, $parents ) {
		$this->_application->callHelper( 'View_AdminView_onDisplayAdminElementsFormFilter', array( &$form, $bundle, $display, $parents ) );
	}

	public function onDisplayAdminDisplayDataKeysFilter( &$keys, Bundle $bundle, $display ) {
		$this->_application->callHelper( 'View_AdminView_onDisplayAdminDisplayDataKeysFilter', array( &$keys, $bundle, $display ) );
	}

	public function onDisplayRenderFilter( &$rendered, $display, $bundle, $var, $options ) {
		if ( $display['type'] !== 'entity'
			|| strpos( $display['name'], 'summary' ) !== 0
		) {
			return;
		}

		if ( ! isset( $options['view_add_border'] ) || $options['view_add_border'] ) {
			$add_border = true;
			if ( $bundle->entitytype_name === 'post' ) {
				// If the entity is a post, we need to check if the border is explicitly disabled
				if ( isset( $display['data']['view_add_border'] ) && ! $display['data']['view_add_border'] ) {
					$add_border = false;
				}
			} elseif ( empty( $display['data']['view_add_border'] ) ) {
				$add_border = false;
			}

			if ( $add_border ) {
				$border_color               = empty( $display['data']['view_border_color'] ) ? 'secondary' : $display['data']['view_border_color'];
				$border_opacity             = empty( $display['data']['view_border_opacity'] ) ? 20 : (int) $display['data']['view_border_opacity'];
				$rendered['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'border ' . DRTS_BS_PREFIX . 'border-' . $border_color;
				if ( $border_opacity < 100 ) {
					$rendered['attr']['style'] .= '--bs-border-opacity: ' . ( $border_opacity / 100 ) . ' !important;';
				}
				if ( ! empty( $display['data']['view_border_rounded'] ) ) {
					$rendered['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'rounded';
				}
			}
		}
		if ( ! empty( $display['data']['view_add_shadow'] ) ) {
			$rendered['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'shadow' . ( $display['data']['view_add_shadow'] !== 'md' ? '-' . $display['data']['view_add_shadow'] : '' );
		}
		if ( ! empty( $display['data']['enable_border_radius'] ) ) {
			$rendered['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'overflow-hidden ' . DRTS_BS_PREFIX . 'rounded';
		}
	}
}
