<?php

namespace WpieFw\Framework\Patterns\Definition;

use WpieFw\Framework\Patterns\Iterator\WpieCollection;
use WpieFw\Framework\Patterns\Definition\WpieDefinitionInterface;
use WpieFw\Exceptions\WpieInvalidArgumentException;

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

abstract class WpieDefinition extends WpieCollection implements WpieDefinitionInterface
{
	/**
	 * @var array
	 */
	protected $shortCutMap = [];

	/**
	 * @var array
	 */
	protected $placeholders = [];

	/**
	 * Constructor
	 *
	 * @param array $map
	 * @param array $definitions
	 */
	public function __construct( array $map = [], array $definitions = [], array $placeholders = [] )
	{
		if( 0 < count( $definitions ) && count( $map ) !== count( $definitions ) ) {
			throw new WpieInvalidArgumentException( sprintf( 'The ShortCut Map array should contain %d items. %d items found.', count( $definitions ), count( $map ) ) );
		}

		// set passed mapping values
		if( 0 < count( $map ) ) {
			$this->setShortCutMap( $map );
		}

		// set passed definitions
		if( 0 < count( $definitions ) ) {
			$definitionsNew = [];
			foreach ( $definitions as $name => $value ) {
				if( isset( $this->shortCutMap[$name] ) ) {
					$definitionsNew[$this->getIdx( $name )] = $value;
				}
			}
			$this->setDefinitions( $definitionsNew );
		}

		// set passed placeholders
		if( 0 < count( $placeholders ) ) {
			$this->registerPlaceholders( $placeholders );
		}
	}

	/**
	 * @param array $definitions
	 */
	public function setDefinitions( array $definitions = [] )
	{
		$this->setMultiple( $definitions );
	}

	/**
	 * {@inheritDoc}
	 * @see \WpieFw\Framework\Patterns\Definition\WpieDefinitionInterface::setShortCutMap()
	 */
	public function setShortCutMap( array $map = [] )
	{
		$this->shortCutMap = $map;
	}

	/**
	 * Get the defined name for a definition by shortcut
	 *
	 * @param string $name
	 * @throws WpieInvalidArgumentException
	 * @return string
	 */
	public function getIdx( $name = '' )
	{
		if( isset( $this->shortCutMap[$name] ) ) {
			return $this->shortCutMap[$name];
		} else {
			throw new WpieInvalidArgumentException( sprintf( 'Could not get definition index for name: %s.', $name ) );
		}
	}

	/**
	 * Get the definition value for give key
	 *
	 * @param string $key
	 * @param array $replace
	 *
	 * @throws WpieInvalidArgumentException
	 * @return string
	 */
	public function getValue( $key = '', array $replace = [] )
	{
		if( !$this->offsetExists( $key ) ) {
			throw new WpieInvalidArgumentException( sprintf( 'Could not get definition for key: %s.', $key ) );
		}

		// Get the value from the collection
		$value = $this->offsetGet( $key );

		// Replace registered placeholders if passed
		if( !empty( $replace ) ) {
			$value = $this->replacePlaceHolders( $value, $replace );
		}

		return $value;
	}

	/**
	 * Get the definition value for give key by shortcut name
	 *
	 * @param string $name
	 * @param array $replace
	 * @return string
	 */
	public function getValueForShortcut( $name = '', array $replace = [] )
	{
		return $this->getValue( $this->getIdx( $name ), $replace );
	}

	/**
	 * {@inheritDoc}
	 * @see \WpieFw\Framework\Patterns\Definition\WpieDefinitionInterface::registerPlaceholder()
	 */
	public function registerPlaceholder( $placeHolder = '' )
	{
		if( isset( $this->placeholders[$placeHolder] ) ) {
			unset( $this->placeholders[$placeHolder] );
		}

		$this->placeholders[] = $placeHolder;
	}

	/**
	 * Register placeholders
	 *
	 * @param array $placeHolder
	 *
	 * @uses self::registerPlaceholder()
	 */
	public function registerPlaceholders( array $placeHolders = [] )
	{
		foreach ( $placeHolders as $placeHolder ) {
			$this->registerPlaceholder( $placeHolder );
		}
	}

	/**
	 * Replace placeholders if registered
	 *
	 * @param string $value
	 * @param array $replace
	 * @return string
	 */
	private function replacePlaceHolders( string $value, array $replace )
	{
		foreach ( $replace as $placeholder => $replacement ) {
			if( !is_string( $placeholder ) ) {
				throw new WpieInvalidArgumentException( sprintf( 'Could not replace placeholder in value "%s". Placeholder is not a string.', $value ) );
			}
			if( !is_string( $replacement ) ) {
				throw new WpieInvalidArgumentException( sprintf( 'Could not replace placeholder "%s" in value "%s". Replacement is not a string.', $placeholder, $value ) );
			}

			if( in_array( $placeholder, $this->placeholders ) ) {
				$placeholderStr = '{'.$placeholder.'}';
				$replacement = trim( $replacement );
				$replacement = str_replace( '-', '_', $replacement );
				$value = str_replace( $placeholderStr, $replacement, $value );
			}
		}

		return $value;
	}
}