<?php

namespace Velis\Filesystem;

use Exception;
use RuntimeException;
use Velis\Filesystem\Stream\LocalStream;
use Velis\Lang;

/**
 * Implementacja lokalnego systemu plików, używana w przypadku braku biblioteki Gaufrette.
 *
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class Filesystem implements FilesystemInterface
{
    use CopyTrait;
    use FilesystemTrait;
    use ImageSizeTrait;
    use ListKeysTrait;
    use SymlinkTrait;

    /**
     * @var string
     */
    private $basePath;


    /**
     * @param string $basePath
     */
    public function __construct($basePath)
    {
        $this->basePath = rtrim($basePath, '/') . '/';
    }


    /**
     * {@inheritDoc}
     */
    public function has($key)
    {
        return file_exists($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     */
    public function rename($sourceKey, $targetKey)
    {
        $this->prepareDirectory($targetKey);

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


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function write($key, $content, $overwrite = true)
    {
        if (!$this->has($key) || $overwrite) {
            $this->prepareDirectory($key);
            return file_put_contents($this->basePath . $key, $content);
        }

        throw new RuntimeException('File cannot be overwritten.');
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function read($key)
    {
        if (!file_exists($this->basePath . $key)) {
            throw new RuntimeException(sprintf('File not found: %s', $key));
        }

        return file_get_contents($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function delete($key)
    {
        if (!$this->has($key)) {
            throw new RuntimeException(sprintf('File not found: %s', $key));
        }

        if (!$this->isDirectory($key)) {
            return unlink($this->basePath . $key);
        }

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


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


    /**
     * {@inheritDoc}
     */
    public function listKeys($prefix = '')
    {
        return $this->listKeysInDirectory('', $prefix);
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function mtime($key)
    {
        return filectime($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function checksum($key)
    {
        return md5($this->read($key));
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function size($key)
    {
        return filesize($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     */
    public function createStream($key)
    {
        return new LocalStream($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function mimeType($key)
    {
        return mime_content_type($this->basePath . $key);
    }


    /**
     * {@inheritDoc}
     *
     * @throws Exception
     */
    public function isDirectory($key)
    {
        return is_dir($this->basePath . $key);
    }


    /**
     * Creates target directory
     * @param string $key
     * @throws RuntimeException
     */
    private function prepareDirectory($key)
    {
        $directory = dirname($this->basePath . $key);
        if (!file_exists($directory)) {
            if (!mkdir($directory, 0777, true)) {
                throw new RuntimeException(Lang::get('GENERAL_CREATING_DIRECTORY_ERROR'));
            }
        }
    }
}
