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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Component\View;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\CSV;
use SabaiApps\Directories\Component\Faker;
use SabaiApps\Directories\Context;

class FieldsetComponent extends AbstractComponent implements
	Entity\ITypes,
	Entity\IBundleTypes,
	Form\IFields,
	Field\ITypes,
	Field\IWidgets,
	Field\IRenderers,
	Display\IElements,
	View\IModes,
	System\IMainRouter,
	CSV\IExporters,
	CSV\IImporters,
	Faker\IGenerators {

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

	public static function description() {
		return 'Adds features to create custom repeatable field collections.';
	}

	public function entityGetTypeNames() {
		return array( 'fieldset' );
	}

	public function entityGetType( $name ) {
		if ( $this->_application->getPlatform()->getName() === 'WordPress' ) {
			return new EntityType\WordPressFieldsetEntityType( $this->_application, $name );
		}
	}

	public function getFieldsets() {
		return isset( $this->_config['fieldsets'] ) ? $this->_config['fieldsets'] : array();
	}

	public function setFieldsets( array $fieldsets ) {
		ksort( $fieldsets );
		$this->_application->System_Component_saveConfig( $this->_name, array( 'fieldsets' => $fieldsets ) );
		$this->_application->clearComponentInfoCache();
		$this->_application->Entity_BundleTypes_clearCache();
	}

	public function entityGetBundleTypeNames() {
		$ret = array();
		foreach ( array_keys( $this->getFieldsets() ) as $slug ) {
			$ret[] = 'fieldset_' . $slug;
		}

		return $ret;
	}

	public function entityGetBundleType( $name ) {
		$slug      = substr( $name, strlen( 'fieldset_' ) );
		$fieldsets = $this->getFieldsets();
		if ( ! empty( $fieldsets[ $slug ] ) ) {
			return new EntityBundleType\FieldsetEntityBundleType( $this->_application, $name, $slug, $fieldsets[ $slug ] );
		}
	}

	public function formGetFieldTypes() {
		return array( 'fieldset_fields' );
	}

	public function formGetField( $name ) {
		return new FormField\FieldsFormField( $this->_application, $name );
	}

	public function fieldGetTypeNames() {
		return array( 'fieldset_fieldset' );
	}

	public function fieldGetType( $name ) {
		switch ( $name ) {
			case 'fieldset_fieldset':
				return new FieldType\FieldsetFieldType( $this->_application, $name );
		}
	}

	public function fieldGetWidgetNames() {
		return array( 'fieldset_fieldset' );
	}

	public function fieldGetWidget( $name ) {
		return new FieldWidget\FieldsetFieldWidget( $this->_application, $name );
	}

	public function fieldGetRendererNames() {
		return array( 'fieldset_fieldset', 'fieldset_count', 'fieldset_template' );
	}

	public function fieldGetRenderer( $name ) {
		switch ( $name ) {
			case 'fieldset_fieldset':
				return new FieldRenderer\FieldsetFieldRenderer( $this->_application, $name );
			case 'fieldset_count':
				return new FieldRenderer\CountFieldRenderer( $this->_application, $name );
			case 'fieldset_template':
				return new FieldRenderer\TemplateFieldRenderer( $this->_application, $name );
		}
	}

	public function onFormScripts( $options ) {
		if ( empty( $options ) || in_array( 'fieldset_fields', $options ) ) {
			$this->_application->getPlatform()
				->addCssFile( 'fieldset-fields.min.css', 'drts-fieldset-fields', 'drts', 'directories-pro' )
				->addJsFile( 'fieldset-fields.min.js', 'drts-fieldset-fields', 'drts-form', 'directories-pro' );
		}
	}

	public function onDisplayAdminAddElementResultsFilter( &$results, $display ) {
		if ( ! in_array( $display->type, array( 'form', 'entity' ) )
			|| ! isset( $results['attr']['data-entity-field-type'] )
			|| $results['attr']['data-entity-field-type'] !== 'fieldset_fieldset'
		) {
			return;
		}

		if ( $display->type === 'form' ) {
			if ( ( ! $element = $this->_application->getModel( 'Element', 'Display' )->fetchById( $results['id'] ) )
				|| ( ! $field_name = $element->data['settings']['field_name'] )
			) {
				return;
			}
		} elseif ( $display->type === 'entity' ) {
			$field_name = substr( $results['name'], 13 );
		} else {
			return;
		}

		$this->_application->Entity_Field_reset();
		if ( ( ! $field = $this->_application->Entity_Field( $display->bundle_name, $field_name ) )
			|| ( ! $settings = $field->getFieldSettings() )
			|| empty( $settings['bundle'] )
		) {
			return;
		}

		// Fetch or create display
		if ( ! $fieldset_display = $this->_application->getModel( 'Display', 'Display' )
			->type_is( $display->type )
			->name_is( $display->name )
			->bundleName_is( $settings['bundle'] )
			->fetchOne()
		) {
			$fieldset_display = $this->_application->Display_Create( $settings['bundle'], $display->type, $display->name, array() );
		}

		$results['alt_display_id'] = $fieldset_display->id;
	}

	public function onDisplayAdminListElementsFilter( &$result, $bundle, $display, $elementName, $elementInfo ) {
		return;
		// Do no allow containable element except for fieldset element if bundle type is fieldset
		if ( ! $result
			|| $bundle->entitytype_name !== 'fieldset'
			|| empty( $elementInfo['containable'] )
			|| ! in_array( $display->type, array( 'form', 'entity' ) )
			|| $elementInfo['_entity_field_type'] === 'fieldset_fieldset'
		) {
			return;
		}

		$result = false;
	}

	public function displayGetElementNames( Entity\Model\Bundle $bundle ) {
		return array();
		$ret         = array();
		$field_types = $this->_application->Field_Types();
		foreach ( $this->_application->Entity_Field( $bundle->name ) as $field ) {
			if ( $field->getFieldType() !== 'fieldset_fieldset' ) {
				continue;
			}

			$field_settings = $field->getFieldSettings();
			if ( empty( $field_settings['bundle'] )
				|| ( ! $fieldset_bundle = $this->_application->Entity_Bundle( $field_settings['bundle'] ) )
			) {
				continue;
			}

			// Fetch fields of fieldset bundle
			foreach ( $this->_application->Entity_Field( $fieldset_bundle->name ) as $fieldset_field ) {
				if ( ! $fieldset_field->isCustomField() ) {
					continue;
				}

				if ( empty( $field_types[ $fieldset_field->getFieldType() ] ) ) {
					continue;
				}

				$field_type = $field_types[ $fieldset_field->getFieldType() ];
				if ( isset( $field_type['entity_types'] )
					&& ! in_array( $fieldset_bundle->entitytype_name, $field_type['entity_types'] )
				) {
					// the field type does not support the entity type of the current bundle
					continue;
				}

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

		return $ret;
	}

	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 displayGetElement( $name ) {
		return new DisplayElement\ViewFilterDisplayElement( $this->_application, $name );
	}

	public function viewGetModeNames() {
		return array( 'fieldset_list' );
	}

	public function viewGetMode( $name ) {
		return new ViewMode\ListViewMode( $this->_application, $name );
	}

	public function onEntityDeleteFieldsetEntitySuccess( $bundle, $entity, $key, $extraArgs ) {
		foreach ( $bundle->Fields as $field ) {
			if ( ( $field->getFieldType() !== 'fieldset_fieldset' )
				|| ( ! $fieldset_ids = $entity->getFieldValue( $field->getFieldName() ) )
			) {
				continue;
			}

			$fieldset_to_delete = array();
			foreach ( $fieldset_ids as $id ) {
				if ( ! $fieldset_entity = $this->_application->Entity_Entity( 'fieldset', $id ) ) {
					continue;
				}

				$fieldset_to_delete[ $id ] = $fieldset_entity;
			}
			if ( ! empty( $fieldset_to_delete ) ) {
				$this->_application->getComponent( 'Entity' )->deleteEntities( 'fieldset', $fieldset_to_delete );
			}
		}
	}

	public function onEntitySortsFilter( &$sorts, $bundle ) {
		if ( $bundle->entitytype_name !== 'fieldset' ) {
			return;
		}

		unset( $sorts['fieldset_published'], $sorts['fieldset_modified'], $sorts['fieldset_published,asc'], $sorts['fieldset_modified,asc'] );
	}

	public function systemMainRoutes( $lang = null ) {
		return array(
			'/_drts/fieldset/:fieldset_bundle' => array(
				'controller'           => 'ViewEntities',
				'controller_component' => 'View',
				'callback_path'        => 'fieldsets',
				'access_callback'      => true,
				'format'               => array( ':fieldset_bundle' => '[a-z0-9_]+' ),
			),
		);
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'fieldsets':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $fieldset_bundle_name = $context->getRequest()->asStr( 'fieldset_bundle' ) )
						|| ( ! $bundle_name = $context->getRequest()->asStr( 'bundle' ) )
						|| ( ! $field_name = $context->getRequest()->asStr( 'field' ) )
						|| ( ! $fieldset_bundle = $this->_application->Entity_Bundle( $fieldset_bundle_name ) )
						|| $fieldset_bundle->entitytype_name !== 'fieldset'
						|| ( ! $bundle = $this->_application->Entity_Bundle( $bundle_name ) )
						|| ( ! $field = $this->_application->Entity_Field( $bundle, $field_name ) )
						|| ( ! $field_settings = $field->getFieldSettings() )
						|| ! isset( $field_settings['bundle'] )
						|| $field_settings['bundle'] !== $fieldset_bundle_name
					) {
						$context->setNotFoundError();
						return false;
					}
					$context->bundle                = $fieldset_bundle;
					$context->field_name            = $field_name;
					$context->container_bundle_name = $bundle->name;
					return true;
				} else {
					return $this->_application->Entity_IsRoutable( $context->container_bundle_name, 'list' );
				}
		}
	}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {}

	public function onDirectoryAdminDirectoryImportBundlesFilter( &$bundles, $directory ) {
		$fieldsets     = $this->getFieldsets();
		$new_fieldsets = array();
		foreach ( array_keys( $bundles ) as $bundle_type ) {
			if ( strpos( $bundle_type, 'fieldset_' ) !== 0 ) {
				continue;
			}

			$slug = substr( $bundle_type, strlen( 'fieldset_' ) );
			if ( isset( $fieldsets[ $slug ] ) ) {
				continue;
			}

			$info                   =& $bundles[ $bundle_type ];
			$new_fieldsets[ $slug ] = array(
				'slug'           => $info['slug'],
				'label'          => $info['label'],
				'label_singular' => $info['label_singular'],
			);
		}
		if ( ! empty( $new_fieldsets ) ) {
			$this->setFieldsets( $fieldsets + $new_fieldsets );
		}
	}

	public function onEntityBundlesInfoFilter( &$bundles, $group ) {
		// Ensure existing fieldset bundles are in the array to preserve
		foreach ( $this->_application->getModel( 'Bundle', 'Entity' )->group_is( $group )->fetch() as $bundle ) {
			if ( $bundle->entitytype_name === 'fieldset' ) {
				if ( ! isset( $bundles[ $bundle->type ] ) ) {
					$bundles[ $bundle->type ] = array();
				}
			}
		}
		// Add missing fieldset bundle info if any
		foreach ( array_keys( $bundles ) as $bundle_type ) {
			if ( strpos( $bundle_type, 'fieldset_' ) === 0 ) {
				if ( $bundle_type_impl = $this->entityGetBundleType( $bundle_type ) ) {
					$bundles[ $bundle_type ] += $bundle_type_impl->entityBundleTypeInfo();
				}
			}
		}
	}

	public function csvGetExporterNames() {
		return array( 'fieldset_fieldset' );
	}

	public function csvGetExporter( $name ) {
		return new CSVExporter\FieldsetCSVExporter( $this->_application, $name );
	}

	public function csvGetImporterNames() {
		return array( 'fieldset_fieldset' );
	}

	public function csvGetImporter( $name ) {
		return new CSVImporter\FieldsetCSVImporter( $this->_application, $name );
	}

	public function onCSVImportFieldValuesFilter( &$values, $bundle ) {
		CSVImporter\FieldsetCSVImporter::onCSVImportFieldValuesFilter( $this->_application, $values, $bundle );
	}

	public function fakerGetGeneratorNames() {
		return array( 'fieldset_fieldset' );
	}

	public function fakerGetGenerator( $name ) {
		return new FakerGenerator\FieldsetFakerGenerator( $this->_application, $name );
	}
}
