<?php

namespace Stax;

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

/**
 * Underscore.php
 * https: *github.com/bdelespierre/underscore.php
 * (c) 2013-2014 Benjamin Delespierre
 * Underscore may be freely distributed under the LGPL license.
 *
 * Bridge helps you to chain modifications on a value using a set of functions. These fonctions can be either
 * bundled into an instance or in a simple hashmap.
 */
class Bridge {
	/**
	 * @internal
	 * @var mixed
	 */
	protected $value;

	/**
	 * @internal
	 * @var object,array
	 */
	protected $_service;

	/**
	 * Constructor
	 *
	 * @param mixed $initialValue the initial state
	 * @param object,array $service the service object
	 */
	public function __construct( $initialValue, $service ) {
		$this->value   = $initialValue;
		$this->service = $service;
	}

	/**
	 * Call any service method providing internal value as first parameter. The bridge instance is always
	 *
	 * @param string $method the service method to call
	 * @param array $args the method's arguments
	 *
	 * @return mixed
	 */
	public function __call( $method, $args ) {
		if ( method_exists( $this->service, $method ) || is_callable( [ $this->service, $method ] ) ) {
			$method = [ $this->service, $method ];
		} elseif ( isset( $this->service[ $method ] ) && is_callable( $this->service[ $method ] ) ) {
			$method = $this->service[ $method ];
		} elseif ( isset( $this->service->$method ) && is_callable( $this->service->$method ) ) {
			$method = $this->service->$method;
		} else {
			throw new  \BadMethodCallException( "no such method {$method}" );
		}

		// reminder: call_user_func does _not_ allow passing by reference !
		// this will be fixed soon (as PHP 5.5 becomes LTS) with variadics
		switch ( count( $args ) ) {
			case 0:
				$this->value = $method( $this->value );
				break;
			case 1:
				$this->value = $method( $this->value, $args[0] );
				break;
			case 2:
				$this->value = $method( $this->value, $args[0], $args[1] );
				break;
			case 3:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2] );
				break;
			case 4:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3] );
				break;
			case 5:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3], $args[4] );
				break;
			case 6:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
				break;
			case 7:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
				break;
			case 8:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7] );
				break;
			case 9:
				$this->value = $method( $this->value, $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8] );
				break;
			default:
				$this->value = call_user_func_array( $method, array_merge( $this->value, $args ) );
				break;
		}

		return $this;
	}

	/**
	 * Retrieve the current value state.
	 *
	 * @return mixed
	 */
	public function value() {
		return $this->value;
	}
}
