<?php

namespace XF;

use function strlen;

class MemoryFile
{
	protected static $buckets = [];

	protected $bucket;
	protected $position;

	public function stream_open($path, $mode, $options, &$opened_path)
	{
		$this->bucket = $bucket = self::getBucketFromUri($path);
		$this->position = 0;

		if (!isset(self::$buckets[$this->bucket]))
		{
			self::$buckets[$this->bucket] = '';
		}

		return true;
	}

	public function stream_stat()
	{
		return self::getBucketStat($this->bucket);
	}

	public function stream_read($count)
	{
		$data = substr(self::$buckets[$this->bucket], $this->position, $count);
		$this->position += strlen($data);

		return $data;
	}

	public function stream_write($data)
	{
		$length = strlen($data);
		$before = substr(self::$buckets[$this->bucket], 0, $this->position);
		$after = substr(self::$buckets[$this->bucket], $this->position + $length);

		self::$buckets[$this->bucket] = $before . $data . $after;
		$this->position += $length;

		return $length;
	}

	public function stream_truncate($new_size)
	{
		self::$buckets[$this->bucket] = $new_size ? substr(self::$buckets[$this->bucket], 0, $new_size) : '';
		return true;
	}

	public function stream_tell()
	{
		return $this->position;
	}

	public function stream_eof()
	{
		return $this->position >= strlen(self::$buckets[$this->bucket]);
	}

	public function stream_seek($offset, $whence)
	{
		switch ($whence)
		{
			case SEEK_SET:
				if ($offset < strlen(self::$buckets[$this->bucket]) && $offset >= 0)
				{
					$this->position = $offset;
					return true;
				}

				return false;

			case SEEK_CUR:
				if ($offset >= 0)
				{
					$this->position += $offset;
					return true;
				}

				return false;

			case SEEK_END:
				$newOffset = strlen(self::$buckets[$this->bucket]) + $offset;
				if ($newOffset >= 0)
				{
					$this->position = $newOffset;
					return true;
				}

				return false;

			default:
				return false;
		}
	}

	public function stream_metadata($path, $option, $var)
	{
		if ($option == STREAM_META_TOUCH)
		{
			$bucket = self::getBucketFromUri($path);
			if (!isset(self::$buckets[$bucket]))
			{
				self::$buckets[$bucket] = '';
			}
		}
	}

	public function url_stat($path, $flags)
	{
		$bucket = self::getBucketFromUri($path);
		return self::getBucketStat($bucket);
	}

	protected static function getBucketFromUri($uri)
	{
		$url = parse_url($uri);
		return $url['host'];
	}

	protected static function getBucketStat($bucket)
	{
		$size = isset(self::$buckets[$bucket]) ? strlen(self::$buckets[$bucket]) : 0;
		$now = time();

		return [
			'dev' => 0,
			'ino' => 0,
			'mode' => 0666,
			'nlink' => 0,
			'uid' => 0,
			'gid' => 0,
			'rdev' => 0,
			'size' => $size,
			'atime' => $now,
			'mtime' => $now,
			'ctime' => $now,
			'blksize' => -1,
			'blocks' => -1,
		];
	}

	public static function clear($bucket)
	{
		self::$buckets[$bucket] = '';
	}

	public static function clearAll()
	{
		self::$buckets = [];
	}

	public static function register($protocol)
	{
		stream_wrapper_register($protocol, self::class);
	}

	public static function unregister($protocol)
	{
		stream_wrapper_unregister($protocol);
	}
}
