<?php

namespace Velis\Filesystem\Adapter;

use Gaufrette\Exception\UnexpectedFile;
use Gaufrette\File;
use Gaufrette\FilesystemInterface as GaufretteFilesystemInterface;
use InvalidArgumentException;
use Velis\Filesystem\FilesystemInterface;
use Velis\Filesystem\FilesystemTrait;
use Velis\Filesystem\Stream\GaufretteStreamWrapper;

/**
 * Adapter zamieniający interfejs Gaufrette\FilesystemInterface na Velis\Filesystem\FilesystemInterface
 *
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
abstract class GaufretteFilesystemWrapper implements FilesystemInterface
{
    use FilesystemTrait;

    /**
     * @var GaufretteFilesystemInterface
     */
    protected $wrapped;


    /**
     * @param GaufretteFilesystemInterface $filesystem
     */
    public function __construct(GaufretteFilesystemInterface $filesystem)
    {
        $this->wrapped = $filesystem;
    }


    /**
     * {@inheritDoc}
     */
    public function has($key)
    {
        try {
            return $this->wrapped->has($key);
        } catch (InvalidArgumentException $e) {
            if (empty($key)) {
                $message = sprintf('File path cannot be null. Requested at %s', $_SERVER['REQUEST_URI']);
                throw new InvalidArgumentException($message, $e->getCode(), $e);
            }

            throw $e;
        }
    }


    /**
     * Overrides filesystem method. Some file exists on the server, but not exists in database.
     *
     * Typical case:
     * The stage database is reproduced directly from the production database.
     * When copying data from the database files are not copied,
     * which caused a problem with the file added by user,
     * because the application system has already found a file with such an identification number.
     *
     * {@inheritdoc}
     */
    public function rename($sourceKey, $targetKey)
    {
        if ($this->has($targetKey)) {
            $this->delete($targetKey);
        }

        return $this->wrapped->rename($sourceKey, $targetKey);
    }


    /**
     * @param string $key
     * @param bool $create
     * @return File
     */
    protected function get($key, $create = false)
    {
        return $this->wrapped->get($key, $create);
    }


    /**
     * {@inheritDoc}
     */
    public function write($key, $content, $overwrite = true)
    {
        return $this->wrapped->write($key, $content, $overwrite);
    }


    /**
     * {@inheritDoc}
     */
    public function read($key)
    {
        return $this->wrapped->read($key);
    }


    /**
     * {@inheritDoc}
     *
     * At this moment, Gaufrette does not support deleting directories:
     * https://github.com/KnpLabs/Gaufrette/issues/524
     */
    public function delete($key)
    {
        if (!$this->has($key)) {
            return false;
        }

        if (!$this->isDirectory($key)) {
            return $this->wrapped->delete($key);
        }

        return $this->rmdir($key);
    }


    /**
     * @param $key
     * @return bool
     */
    abstract protected function rmdir($key);


    /**
     * {@inheritDoc}
     */
    public function keys()
    {
        return $this->wrapped->keys();
    }


    /**
     * {@inheritDoc}
     */
    public function listKeys($prefix = '')
    {
        $keys = $this->wrapped->listKeys($prefix);

        if (isset($keys['keys'])) {
            return array_merge($keys['keys'], $keys['dirs']);
        }

        return $keys;
    }


    /**
     * {@inheritDoc}
     */
    public function mtime($key)
    {
        return $this->wrapped->mtime($key);
    }


    /**
     * {@inheritDoc}
     */
    public function checksum($key)
    {
        return $this->wrapped->checksum($key);
    }


    /**
     * {@inheritDoc}
     */
    public function size($key)
    {
        return $this->wrapped->size($key);
    }


    /**
     * {@inheritDoc}
     */
    public function createStream($key)
    {
        $stream = $this->wrapped->createStream($key);

        return new GaufretteStreamWrapper($stream);
    }


    /**
     * {@inheritDoc}
     */
    public function mimeType($key)
    {
        return $this->wrapped->mimeType($key);
    }


    /**
     * {@inheritDoc}
     */
    public function isDirectory($key)
    {
        return $this->wrapped->isDirectory($key);
    }
}
