<?php

namespace XF\Mvc\Entity;

class DeferredValue
{
	protected $handler;
	protected $assignTime;

	protected $assignableMatrix = [
		'get' => ['get' => true, 'preSave' => true],
		'preSave' => ['get' => false, 'preSave' => true],
		'save' => ['get' => false, 'preSave' => false],
	];

	public function __construct(\Closure $handler, $assignTime = 'preSave')
	{
		$this->handler = $handler;

		if ($this->isValidContext($assignTime))
		{
			$this->assignTime = $assignTime;
		}
		else
		{
			throw new \InvalidArgumentException("Assign time may only be get, preSave or save");
		}
	}

	public function __invoke(Entity $e, $context)
	{
		if (!$this->isValidContext($context))
		{
			throw new \InvalidArgumentException("Invalid deferred value invocation context");
		}

		$handler = $this->handler;
		return $handler($e, $context);
	}

	public function getHandler()
	{
		return $this->handler;
	}

	public function getAssignTime()
	{
		return $this->assignTime;
	}

	public function isAssignableAt($time)
	{
		if (!$this->isValidContext($time))
		{
			throw new \InvalidArgumentException("Time may only be get, preSave or save");
		}

		if ($time == 'save')
		{
			// all values must be assigned by save so they're not in the matrix
			return true;
		}

		$assignable = $this->assignableMatrix[$this->assignTime];
		return $assignable[$time];
	}

	protected function isValidContext($context)
	{
		switch ($context)
		{
			case 'get':
			case 'preSave':
			case 'save':
				return true;

			default:
				return false;
		}
	}
}
