<?php
 namespace MongoDB\GridFS;
 use Exception;
 use MongoDB\BSON\UTCDateTime;
 use stdClass;
 use function explode;
 use function get_class;
 use function in_array;
 use function is_integer;
 use function sprintf;
 use function stream_context_get_options;
 use function stream_get_wrappers;
 use function stream_wrapper_register;
 use function stream_wrapper_unregister;
 use function trigger_error;
 use const E_USER_WARNING;
 use const SEEK_CUR;
 use const SEEK_END;
 use const SEEK_SET;
 use const STREAM_IS_URL;
 class StreamWrapper {
 public $context;
 private $mode;
 private $protocol;
 private $stream;
 public function getFile() {
 return $this->stream->getFile();
 }
 public static function register($protocol = 'gridfs') {
 if (in_array($protocol, stream_get_wrappers())) {
 stream_wrapper_unregister($protocol);
 }
 stream_wrapper_register($protocol, static::class, STREAM_IS_URL);
 }
 public function stream_close() {
 $this->stream->close();
 }
 public function stream_eof() {
 if (! $this->stream instanceof ReadableStream) {
 return false;
 }
 return $this->stream->isEOF();
 }
 public function stream_open($path, $mode, $options, &$openedPath) {
 $this->initProtocol($path);
 $this->mode = $mode;
 if ($mode === 'r') {
 return $this->initReadableStream();
 }
 if ($mode === 'w') {
 return $this->initWritableStream();
 }
 return false;
 }
 public function stream_read($length) {
 if (! $this->stream instanceof ReadableStream) {
 return '';
 }
 try {
 return $this->stream->readBytes($length);
 }
 catch (Exception $e) {
 trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING);
 return false;
 }
 }
 public function stream_seek($offset, $whence = SEEK_SET) {
 $size = $this->stream->getSize();
 if ($whence === SEEK_CUR) {
 $offset += $this->stream->tell();
 }
 if ($whence === SEEK_END) {
 $offset += $size;
 }
 if ($this->stream instanceof WritableStream) {
 return $offset === $size;
 }
 if ($offset < 0 || $offset > $size) {
 return false;
 }
 $this->stream->seek($offset);
 return true;
 }
 public function stream_stat() {
 $stat = $this->getStatTemplate();
 $stat[2] = $stat['mode'] = $this->stream instanceof ReadableStream ? 0100444 : 0100222;
 $stat[7] = $stat['size'] = $this->stream->getSize();
 $file = $this->stream->getFile();
 if (isset($file->uploadDate) && $file->uploadDate instanceof UTCDateTime) {
 $timestamp = $file->uploadDate->toDateTime()->getTimestamp();
 $stat[9] = $stat['mtime'] = $timestamp;
 $stat[10] = $stat['ctime'] = $timestamp;
 }
 if (isset($file->chunkSize) && is_integer($file->chunkSize)) {
 $stat[11] = $stat['blksize'] = $file->chunkSize;
 }
 return $stat;
 }
 public function stream_tell() {
 return $this->stream->tell();
 }
 public function stream_write($data) {
 if (! $this->stream instanceof WritableStream) {
 return 0;
 }
 try {
 return $this->stream->writeBytes($data);
 }
 catch (Exception $e) {
 trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING);
 return false;
 }
 }
 private function getStatTemplate() {
 return [ 0 => 0, 'dev' => 0, 1 => 0, 'ino' => 0, 2 => 0, 'mode' => 0, 3 => 0, 'nlink' => 0, 4 => 0, 'uid' => 0, 5 => 0, 'gid' => 0, 6 => -1, 'rdev' => -1, 7 => 0, 'size' => 0, 8 => 0, 'atime' => 0, 9 => 0, 'mtime' => 0, 10 => 0, 'ctime' => 0, 11 => -1, 'blksize' => -1, 12 => -1, 'blocks' => -1, ];
 }
 private function initProtocol($path) {
 $parts = explode('://', $path, 2);
 $this->protocol = $parts[0] ?: 'gridfs';
 }
 private function initReadableStream() {
 $context = stream_context_get_options($this->context);
 $this->stream = new ReadableStream( $context[$this->protocol]['collectionWrapper'], $context[$this->protocol]['file'] );
 return true;
 }
 private function initWritableStream() {
 $context = stream_context_get_options($this->context);
 $this->stream = new WritableStream( $context[$this->protocol]['collectionWrapper'], $context[$this->protocol]['filename'], $context[$this->protocol]['options'] );
 return true;
 }
 }
 