<?php
/**
 * Please see weepie-framework.php for more details.
 */

namespace WpieFw;

use WpieFw\Wpie\WpieGlobals;
use WpieFw\Exceptions\WpieException;
use WpieFw\Exceptions\WpieExceptionInterface;
use WpieFw\Exceptions\WpieRuntimeException;
use WpieFw\Exceptions\WpieReflectionException;
use WpieFw\Exceptions\WpieInvalidArgumentException;
use WpieFw\Framework\Patterns\Iterator\WpieCollection;

if( ! defined( 'ABSPATH' ) ) exit;

/**
 * WpieSrc class
 *
 * This class provides a layer with plugin data for all src classes
 *
 * @author $Author: Vincent Weber <weepie-plugins@outlook.com> $
 *
 * @since 1.4.0
 */
final class WpieSrc extends WpieCollection
{
	/**
	 * Constructor
	 *
	 * @access public
	 *
	 * @param WpieGlobals $globals
	 *
	 * @since 1.4.0
	 */
	public function __construct( WpieGlobals &$globals  )
	{
		$this->add( 'globals', $globals );
	}

	/**
	 * Create an instance
	 *
	 * @access public
	 *
	 * @param string $class
	 * @param array $args
	 * @param string $name
	 * @param bool $return
	 * @param bool $addGlobals
	 *
	 * @throws WpieInvalidArgumentException if input parameters are not valid
	 *
	 * @return object
	 */
	public function create( $class = '', $args = [], $name = '', $return = true, $addGlobals = true )
	{
		if( '' === $class ) {
			throw new WpieInvalidArgumentException( 'Class input parameter is empty in WpieSrc::create().' );
		}
		if( !class_exists( $class ) ) {
			throw new WpieInvalidArgumentException( sprintf( 'Class %s does not exist. Could not create instance in WpieSrc::create().', $class ) );
		}

		$args = (array) $args;

		// Loop over the passed args and replace string with repsective instances
		if( 0 < count( $args ) ) {
			foreach ( $args as $k => $arg ) {
				if( is_string( $arg ) && $this->exists( $arg ) ) {
					$args[$k] = $this->get( $arg );
				}
			}
		}

		// Add WpieGlobals instance
		if( $addGlobals ) {
			$args[] = $this->get( 'globals' );
		}

		$rfc = new \ReflectionClass( $class );

		try {
			$instance = $rfc->newInstanceArgs( $args );
		} catch( \ReflectionException $e ) {
			throw new WpieReflectionException( $e->getMessage(), $e->getCode(), $e->getPrevious() );
		} catch( WpieExceptionInterface $e ) {
			throw new WpieReflectionException( $e->getMessage(), $e->getCode(), $e->getPrevious() );
		} catch ( \Exception $e ) {
			throw new WpieException( $e->getMessage(), $e->getCode(), $e->getPrevious() );
		}

		if( is_a( $instance, $class ) ) {
			if( '' === $name ) {
				$name = $class;
			}

			if( false !== strpos( $name, '\\' ) ) {
					$parts = explode( '\\', $name);
					$name = array_pop( $parts );
			}
			if( 0 === strpos( $name,  'WpiePlugin' ) ) {
				$name = strtolower( str_replace( 'WpiePlugin' , '', $name ) );
			} elseif( 0 === strpos( $name,  'Wpie' ) ) {
				$name = strtolower( str_replace( 'Wpie' , '', $name ) );
			}

			$name = ( '' === $name ) ? $class : $name;
			$this->add( $name, $instance );
		}

		if( $return ) {
			return $instance;
		} else {
			unset( $instance );
		}
	}


	/**
	 * Add an instance
	 *
	 * @param string $key
	 * @param mixed $instance
	 * @param bool $return
	 *
	 * @since 1.4.0
	 *
	 * @throws WpieInvalidArgumentException if input parameters are not valid
	 */
	public function add( $key = '', &$instance = null, $return = false )
	{
		if( '' === trim( $key ) ) {
			throw new WpieInvalidArgumentException( 'Key input parameter is empty in WpieSrc::set().' );
		}
		if( !is_object( $instance ) ) {
			throw new WpieInvalidArgumentException( 'Instance input parameter is not an object in WpieSrc::set().' );
		}

		//$this->collection[$key] = &$instance;
		$this->set( $key, $instance );

		if( $return ) {
			return $instance;
		}
	}

	/**
	 * Get an instance
	 *
	 * @param string $key
	 * @param mixed $default
	 *
	 * @uses parent::get()
	 *
	 * @throws WpieRuntimeException if instance does not exist
	 *
	 * @since 1.4.0
	 *
	 * @return mixed|boolean, the instance or False if $key not exist and default is null
	 */
	public function get( $key = '', $default = null )
	{
		if( $this->exists( $key ) ) {
			return parent::get( $key );
		} elseif( !$this->exists( $key ) && null !== $default ) {
			return $default;
		} else {
			throw new WpieRuntimeException( sprintf( 'Instance %s does not exist.', $key ) );
		}
	}

	/**
	 * Get all saved source instances
	 *
	 * @since 2.0
	 */
	public function getAll()
	{
		return $this->collection;
	}

	/**
	 * Determine if instance exists for key
	 *
	 * @param string $key
	 *
	 * @since 1.4.0
	 * @since 2.0 uses self::offsetExists()
	 *
	 * @return boolean
	 */
	public function exists( $key = '' )
	{
		return $this->offsetExists( $key );
	}
}