<?php

namespace LassoLiteVendor\Http\Message\Encoding;

use LassoLiteVendor\Clue\StreamFilter as Filter;
use LassoLiteVendor\Http\Message\Decorator\StreamDecorator;
use LassoLiteVendor\Psr\Http\Message\StreamInterface;
/**
 * A filtered stream has a filter for filtering output and a filter for filtering input made to a underlying stream.
 *
 * @author Joel Wurtz <joel.wurtz@gmail.com>
 */
abstract class FilteredStream implements StreamInterface
{
    use StreamDecorator {
        rewind as private doRewind;
        seek as private doSeek;
    }
    const BUFFER_SIZE = 8192;
    /**
     * @var callable
     */
    protected $readFilterCallback;
    /**
     * @var resource
     *
     * @deprecated since version 1.5, will be removed in 2.0
     */
    protected $readFilter;
    /**
     * @var callable
     *
     * @deprecated since version 1.5, will be removed in 2.0
     */
    protected $writeFilterCallback;
    /**
     * @var resource
     *
     * @deprecated since version 1.5, will be removed in 2.0
     */
    protected $writeFilter;
    /**
     * Internal buffer.
     *
     * @var string
     */
    protected $buffer = '';
    /**
     * @param mixed|null $readFilterOptions
     * @param mixed|null $writeFilterOptions deprecated since 1.5, will be removed in 2.0
     */
    public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null)
    {
        if (null !== $readFilterOptions) {
            $this->readFilterCallback = Filter\fun($this->readFilter(), $readFilterOptions);
        } else {
            $this->readFilterCallback = Filter\fun($this->readFilter());
        }
        if (null !== $writeFilterOptions) {
            $this->writeFilterCallback = Filter\fun($this->writeFilter(), $writeFilterOptions);
            @\trigger_error('The $writeFilterOptions argument is deprecated since version 1.5 and will be removed in 2.0.', \E_USER_DEPRECATED);
        } else {
            $this->writeFilterCallback = Filter\fun($this->writeFilter());
        }
        $this->stream = $stream;
    }
    /**
     * {@inheritdoc}
     */
    public function read($length)
    {
        if (\strlen($this->buffer) >= $length) {
            $read = \substr($this->buffer, 0, $length);
            $this->buffer = \substr($this->buffer, $length);
            return $read;
        }
        if ($this->stream->eof()) {
            $buffer = $this->buffer;
            $this->buffer = '';
            return $buffer;
        }
        $read = $this->buffer;
        $this->buffer = '';
        $this->fill();
        return $read . $this->read($length - \strlen($read));
    }
    /**
     * {@inheritdoc}
     */
    public function eof()
    {
        return $this->stream->eof() && '' === $this->buffer;
    }
    /**
     * Buffer is filled by reading underlying stream.
     *
     * Callback is reading once more even if the stream is ended.
     * This allow to get last data in the PHP buffer otherwise this
     * bug is present : https://bugs.php.net/bug.php?id=48725
     */
    protected function fill()
    {
        $readFilterCallback = $this->readFilterCallback;
        $this->buffer .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE));
        if ($this->stream->eof()) {
            $this->buffer .= $readFilterCallback();
        }
    }
    /**
     * {@inheritdoc}
     */
    public function getContents()
    {
        $buffer = '';
        while (!$this->eof()) {
            $buf = $this->read(self::BUFFER_SIZE);
            // Using a loose equality here to match on '' and false.
            if (null == $buf) {
                break;
            }
            $buffer .= $buf;
        }
        return $buffer;
    }
    /**
     * Always returns null because we can't tell the size of a stream when we filter.
     */
    public function getSize()
    {
        return null;
    }
    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->getContents();
    }
    /**
     * Filtered streams are not seekable.
     *
     * We would need to buffer and process everything to allow seeking.
     */
    public function isSeekable()
    {
        return \false;
    }
    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        @\trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', \E_USER_DEPRECATED);
        $this->doRewind();
    }
    /**
     * {@inheritdoc}
     */
    public function seek($offset, $whence = \SEEK_SET)
    {
        @\trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', \E_USER_DEPRECATED);
        $this->doSeek($offset, $whence);
    }
    /**
     * Returns the read filter name.
     *
     * @return string
     *
     * @deprecated since version 1.5, will be removed in 2.0
     */
    public function getReadFilter()
    {
        @\trigger_error('The ' . __CLASS__ . '::' . __METHOD__ . ' method is deprecated since version 1.5 and will be removed in 2.0.', \E_USER_DEPRECATED);
        return $this->readFilter();
    }
    /**
     * Returns the write filter name.
     *
     * @return string
     */
    protected abstract function readFilter();
    /**
     * Returns the write filter name.
     *
     * @return string
     *
     * @deprecated since version 1.5, will be removed in 2.0
     */
    public function getWriteFilter()
    {
        @\trigger_error('The ' . __CLASS__ . '::' . __METHOD__ . ' method is deprecated since version 1.5 and will be removed in 2.0.', \E_USER_DEPRECATED);
        return $this->writeFilter();
    }
    /**
     * Returns the write filter name.
     *
     * @return string
     */
    protected abstract function writeFilter();
}
