<?php

declare (strict_types=1);
namespace SlimStat\Dependencies\League\Flysystem;

class Filesystem implements FilesystemOperator
{
    /**
     * @var FilesystemAdapter
     */
    private $adapter;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var PathNormalizer
     */
    private $pathNormalizer;
    public function __construct(FilesystemAdapter $adapter, array $config = [], PathNormalizer $pathNormalizer = null)
    {
        $this->adapter = $adapter;
        $this->config = new Config($config);
        $this->pathNormalizer = $pathNormalizer ?: new WhitespacePathNormalizer();
    }
    public function fileExists(string $location): bool
    {
        return $this->adapter->fileExists($this->pathNormalizer->normalizePath($location));
    }
    public function write(string $location, string $contents, array $config = []): void
    {
        $this->adapter->write($this->pathNormalizer->normalizePath($location), $contents, $this->config->extend($config));
    }
    public function writeStream(string $location, $contents, array $config = []): void
    {
        /* @var resource $contents */
        $this->assertIsResource($contents);
        $this->rewindStream($contents);
        $this->adapter->writeStream($this->pathNormalizer->normalizePath($location), $contents, $this->config->extend($config));
    }
    public function read(string $location): string
    {
        return $this->adapter->read($this->pathNormalizer->normalizePath($location));
    }
    public function readStream(string $location)
    {
        return $this->adapter->readStream($this->pathNormalizer->normalizePath($location));
    }
    public function delete(string $location): void
    {
        $this->adapter->delete($this->pathNormalizer->normalizePath($location));
    }
    public function deleteDirectory(string $location): void
    {
        $this->adapter->deleteDirectory($this->pathNormalizer->normalizePath($location));
    }
    public function createDirectory(string $location, array $config = []): void
    {
        $this->adapter->createDirectory($this->pathNormalizer->normalizePath($location), $this->config->extend($config));
    }
    public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing
    {
        $path = $this->pathNormalizer->normalizePath($location);
        return new DirectoryListing($this->adapter->listContents($path, $deep));
    }
    public function move(string $source, string $destination, array $config = []): void
    {
        $this->adapter->move($this->pathNormalizer->normalizePath($source), $this->pathNormalizer->normalizePath($destination), $this->config->extend($config));
    }
    public function copy(string $source, string $destination, array $config = []): void
    {
        $this->adapter->copy($this->pathNormalizer->normalizePath($source), $this->pathNormalizer->normalizePath($destination), $this->config->extend($config));
    }
    public function lastModified(string $path): int
    {
        return $this->adapter->lastModified($this->pathNormalizer->normalizePath($path))->lastModified();
    }
    public function fileSize(string $path): int
    {
        return $this->adapter->fileSize($this->pathNormalizer->normalizePath($path))->fileSize();
    }
    public function mimeType(string $path): string
    {
        return $this->adapter->mimeType($this->pathNormalizer->normalizePath($path))->mimeType();
    }
    public function setVisibility(string $path, string $visibility): void
    {
        $this->adapter->setVisibility($this->pathNormalizer->normalizePath($path), $visibility);
    }
    public function visibility(string $path): string
    {
        return $this->adapter->visibility($this->pathNormalizer->normalizePath($path))->visibility();
    }
    /**
     * @param mixed $contents
     */
    private function assertIsResource($contents): void
    {
        if (is_resource($contents) === false) {
            throw new InvalidStreamProvided("Invalid stream provided, expected stream resource, received " . gettype($contents));
        } elseif ($type = get_resource_type($contents) !== 'stream') {
            throw new InvalidStreamProvided("Invalid stream provided, expected stream resource, received resource of type " . $type);
        }
    }
    /**
     * @param resource $resource
     */
    private function rewindStream($resource): void
    {
        if (ftell($resource) !== 0 && stream_get_meta_data($resource)['seekable']) {
            rewind($resource);
        }
    }
}