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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Exception;

class BundleTypesHelper {

	private $_bundleTypes, $_impls = array();

	public function help( Application $application, $bundleType = null ) {
		if ( ! isset( $this->_bundleTypes ) ) {
			$this->_bundleTypes = array();
			foreach ( $application->InstalledComponentsByInterface( 'Entity\IBundleTypes' ) as $component_name ) {
				if ( ! $application->isComponentLoaded( $component_name ) ) {
					continue;
				}

				foreach ( $application->getComponent( $component_name )->entityGetBundleTypeNames() as $bundle_type_name ) {
					$this->_bundleTypes[ $bundle_type_name ] = $component_name;
				}
			}
		}

		return isset( $bundleType ) ? ( isset( $this->_bundleTypes[ $bundleType ] ) ? $this->_bundleTypes[ $bundleType ] : null ) : $this->_bundleTypes;
	}

	public function clearCache( Application $application ) {
		$this->_bundleTypes = null;
		$application->getPlatform()->deleteCache( 'entity_bundle_types_children_1', 'entity_bundle_types_children_0' );
	}

	public function byFeatures( Application $application, array $features, $entityType = null ) {
		$bundle_types = array();
		foreach ( array_keys( $this->help( $application ) ) as $bundle_type ) {
			$entity_type = $application->Entity_BundleTypeInfo( $bundle_type, 'entity_type' );
			foreach ( $features as $feature ) {
				if ( ! $application->Entity_BundleTypeInfo( $bundle_type, $feature ) ) {
					continue 2; // skip this bundle type
				}
			}
			$bundle_types[ $entity_type ][] = $bundle_type;
		}

		return isset( $entityType ) ? ( isset( $bundle_types[ $entityType ] ) ? $bundle_types[ $entityType ] : array() ) : $bundle_types;
	}

	/**
	 * Gets an implementation of Entity\BundleType\IBundleType interface for a given bundle_type name
	 *
	 * @param Application $application
	 * @param string      $bundleType
	 */
	public function impl( Application $application, $bundleType, $returnFalse = false ) {
		if ( ! isset( $this->_impls[ $bundleType ] ) ) {
			if ( ( ! $bundle_type_component = $this->help( $application, $bundleType ) )
				|| ! $application->isComponentLoaded( $bundle_type_component )
				|| ( ! $bundle_type = $application->getComponent( $bundle_type_component )->entityGetBundleType( $bundleType ) )
			) {
				if ( $returnFalse ) {
					return false;
				}
				throw new Exception\UnexpectedValueException( sprintf( 'Invalid bundle type: %s', $bundleType ) );
			}
			$this->_impls[ $bundleType ] = $bundle_type;
		}

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

	public function children( Application $application, $bundleType = null, $publicOnly = true, $useCache = true ) {
		$bundle_types = $this->help( $application );
		$cache_id     = 'entity_bundle_types_children_' . (int) $publicOnly;
		if ( ! $useCache
			|| ( ! $child_bundle_types = $application->getPlatform()->getCache( $cache_id ) )
		) {
			$child_bundle_types    = array();
			$child_bundle_features = $application->Filter( 'entity_child_bundle_features', array( 'claiming_enable', 'review_enable', 'booking_enable' ) );
			foreach ( array_keys( $bundle_types ) as $bundle_type ) {
				$bundle_type_info = $application->Entity_BundleTypeInfo( $bundle_type );
				if ( empty( $bundle_type_info['parent'] )
					|| ( $publicOnly && empty( $bundle_type_info['public'] ) )
				) {
					continue;
				}

				if ( in_array( $bundle_type_info['parent'], $child_bundle_features ) ) {
					$bundle_types_by_feature = $this->byFeatures( $application, array( $bundle_type_info['parent'] ) );
					foreach ( array_keys( $bundle_types_by_feature ) as $entity_type ) {
						foreach ( $bundle_types_by_feature[ $entity_type ] as $_parent ) {
							$child_bundle_types[ $_parent ][] = $bundle_type;
						}
					}
				} else {
					$child_bundle_types[ $bundle_type_info['parent'] ][] = $bundle_type;
				}
			}
			$application->getPlatform()->setCache( $child_bundle_types, $cache_id );
		}

		if ( ! isset( $bundleType ) ) {
			return array_intersect_key( $child_bundle_types, $bundle_types );
		}

		return isset( $child_bundle_types[ $bundleType ] ) && isset( $bundle_types[ $bundleType ] ) ? $child_bundle_types[ $bundleType ] : array();
	}
}
