<?php namespace JsonApi;
class Request {

	/** @var string The type of the resource. **/
	private $type;

	/** @var string	The resource id. **/
	private $id;

	/** @var array	The raw path, splited by forward slash. **/
	private $path;

	/**
	 * Initailize the JSON API request.
	 *
	 * @param	string	The HTTP(S) method used.
	 * @param	string	The raw path requested.
	 * @param	array	The data supplied by the client, as key->value array.
	 */
	public function __construct($method, $path, $data) {
		$this->path = $this->extractPath($path);
		$this->method = $this->extractMethod($method);
		$this->data = $this->extractData($data);
	}

	/**
	 * Get the HTTP method used.
	 *
	 * @param	string	$method	The method, as received from the client.
	 *
	 * @return	string	The method used, as lowercase string.
	 */
	public function extractMethod($method) {
		$method = strtolower($method);
		if($method === 'get' && $this->id === null) {
			$method = 'list';
		}

		return $method;
	}

	/**
	 * Get the path, as received from the
	 *
	 * @param	string	$path	The path, as string, which is requested.
	 *
	 * @return	array	The separated path values.
	 */
	public function extractPath($path) {
		$path = trim($path, '/ ');
		$path = array_reverse(array_filter(explode('/', $path)));

		# Remove "relationships" from array
		foreach($path as $i => $item) {
			if($item === 'relationships') {
				unset($path[$i]);
			}
		}

		# Add the null value for the
		$len = sizeof($path);
		if(sizeof($path) % 2) {
			array_unshift($path, null);
			$len++;
		}

		# Handle the types
		$list = [];
		for($i = 0; $i < $len; $i+=2) {
			$list[floor($i / 2.0)] = [
				'type' => $path[$i+1],
				'id' => $path[$i]
			];
		}

		# Set the primary type and id
		$primary = array_shift($list);
		$this->type = $primary['type'];
		$this->id = $primary['id'];

		return $list;
	}

	/**
	 * Basic verification of the request.
	 *
	 * @param	array		The data, as received from the client.
	 * @throws	Exception	On verification failed.
	 */
	public function extractData($data) {
		if($data) {
			$this->data = Data\DataItem::parse($data);

			# Mandatory id
			if(empty($this->id)) {
				if(in_array($this->method, [ 'get', 'patch', 'delete' ])) {
					$method	 = strtoupper($this->method);
					$this->error("Missing resource id for {$method} method.", 400);
				}

			# Mandatory empty id
			} else {
				if(in_array($this->method, [ 'list', 'post' ])) {
					$method	 = strtoupper($this->method);
					$this->error("Supplied {$method} method must not have id in the URL.", 400);
				}
			}

			# Match data for PATCH
			if($this->method === 'patch' && !$this->data->getId()) {
				$this->error('Every PATCH request must have defined `id` parameter.', 400);
			}

			# Match types
			$type = $this->data->getType();
			if($type) {
				$type === $this->data->getType()
					or $this->error('Mismatch between URL and supplied `data.type` parameter.', 400);
			}

			# Match id
			if($this->data->getId()) {
				$this->data->getId() === $this->id
					or $this->error('Mismatch between URL and supplied `data.id` parameter.', 400);
			}

		} else {
			$this->data = null;
		}

		return $this->data;
	}

	/**
	 * Raise an exception.
	 *
	 * @param	string	$error	The message for the exception.
	 */
	private function error($error) {
		throw new \Exception($error);
	}


	public function getId() { return $this->id; }
	public function getType() { return $this->type; }
	public function getPath() { return $this->path; }
	public function getData() { return $this->data; }
	public function getMethod() { return $this->method; }


}
