<?php
/**
 * Streamwrapper Implementation (PoC covering include)
 */

declare(strict_types=1);

class FileStreamWrapper
{
    /**
     * @var resource
     */
    public $context;

    /**
     * @var resource
     */
    private $handle;

    /**
     * @var string
     */
    const protocol = 'file';

    public static function init()
    {
        $result = stream_wrapper_unregister(self::protocol);
        if (false === $result) {
            throw new UnexpectedValueException('Failed to unregister');
        }
        stream_wrapper_register(self::protocol, FileStreamWrapper::class, 0);
    }

    private static function restore()
    {
        $result = stream_wrapper_restore(self::protocol);
        if (false === $result) {
            throw new UnexpectedValueException('Failed to restore');
        }
    }

    public function stream_open($path, $mode, $options, &$opened_path): bool
    {

        if (isset($this->handle)) {
            throw new UnexpectedValueException('Handle congruency');
        }
        $use_include_path = null;
        // TODO(hakre): implenent based on options
        $use_include_path = true;

        $context = $this->context;
        if (null === $context) {
            $context = stream_context_get_default();
        }
        self::restore();
        $handle = fopen($path, $mode, $use_include_path, $context);
        self::init();
        if (false === $handle) {
            return false;
        }
        $meta = stream_get_meta_data($handle);
        if (!isset($meta['uri'])) {
            throw new UnexpectedValueException('Uri not in meta data');
        }

        $opened_path = $meta['uri'];

        $this->handle = $handle;
        return true;
    }

    /**
     * @return array
     */
    public function stream_stat(): array
    {
        self::restore();
        $array = fstat($this->handle);
        self::init();

        return $array;
    }

    private $declaredTicks = false;

    /**
     * @param $count
     *
     * @return string
     */
    public function stream_read(int $count): string
    {
        self::restore();
        $result = fread($this->handle, $count);
        self::init();

        if (!$this->declaredTicks) {
            $result = preg_replace(
                '~^(<\?php\s*)$~m',
                "\\0\ndeclare(ticks=1);\n",
                $result,
                1
            );
            $this->declaredTicks = true;
        }

        return $result;
    }

    public function stream_eof(): bool
    {
        self::restore();
        $result = feof($this->handle);
        self::init();

        return $result;
    }
}