<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Storage
 * @subpackage File
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_Storage
 * @subpackage File
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @author     Zwer
 */
class Core_Storage_Strategy_Hash extends Core_Storage_Strategy_Abstract
{
    /**
     * Hash directory structure depth
     * @var int
     */
    protected $_depth   = 0;

    protected function _has(Core_Storage_File $file)
    {
        $path = $this->createFilePath($file->getStorageName());
        return file_exists($path);
    }

    protected function _fetch(Core_Storage_File $file)
    {
        $path = $this->createFilePath($file->getStorageName());
        if (!$this->_has($file)) {
            throw new InvalidArgumentException(
                sprintf('Failed fetching file "%s", no such file in storage', $file->getPath())
            );
        } else if (!is_readable($path)) {
            throw new RuntimeException(
                sprintf('Failed fetching file "%s", it is non-readable', $file->getPath())
            );
        }

        return $this->_file($path);
    }

    protected function _store(Core_Storage_File $file)
    {
        $path   = $this->createFilePath($file->getStorageName());
        $from   = $file->getPath();
        $parent = dirname($path);

        if (!file_exists($parent)) {
            if (!mkdir($parent, 0777, true)) {
                throw new RuntimeException(
                    sprintf('Failed storing file "%s", unable to create target directory "%s"!', $from, $parent)
                );
            }
        }
        if (!file_exists($from)) {
            throw new RuntimeException(
                sprintf('Failed storing file "%s", it does not exist', $from)
            );
        } else if (!is_readable($from)) {
            throw new RuntimeException(
                sprintf('Failed storing file "%s", it is non-readable', $from)
            );
        } else if (!is_writable($parent) || (file_exists($path) && !is_writable($path))) {
            throw new RuntimeException(
                sprintf('Failed storing file "%s", parent dir or target file is non-writable', $path)
            );
        } else if (!copy($from, $path)) {
            // how to get here?
            // - no space left on device

            // Ignore coverage, need newer vfsStream with quota support to test this
            // @codeCoverageIgnoreStart
            throw new RuntimeException(
                sprintf('Failed storing file "%s", copying the file failed', $path)
            );
            // @codeCoverageIgnoreEnd
        }
        // TODO:
        // chmod($path, $this->getFileMode());

        return $this->_file($path);
    }

    protected function _remove(Core_Storage_File $file)
    {
        $path = $this->createFilePath($file->getStorageName());
        if (!$this->_has($file)) {
            throw new InvalidArgumentException(
                sprintf('Failed removing file "%s", no such file in storage', $file->getPath())
            );
        } else if (!is_writable(dirname($path))) {
            throw new RuntimeException(
                sprintf('Failed removing file "%s", parent dir is non-writable', $file->getPath())
            );
        }

        return unlink($path);
    }

    protected function _getDepthPath($path)
    {
        if ($this->_depth <= 0) {
            return '';
        }

        $filename   = pathinfo($path, PATHINFO_FILENAME);
        $filenameArray = preg_split('/([^\S]*)/u', $filename, null, PREG_SPLIT_NO_EMPTY);
        $length     = count($filenameArray);
        $depth      = $length > $this->_depth ? $this->_depth : $length;
        // Split multibyte string on everything except whitespace
        $depthArray = array_slice($filenameArray, 0, $depth);
        $depthPath  = implode('/', $depthArray) . '/';

        return $depthPath;
    }

    public function setDepth($depth)
    {
        $this->_depth = (int) $depth;
    }

    public function getDepth()
    {
        return $this->_depth;
    }


    public function createFilePath($path)
    {
        return $this->getRoot() .'/'. $this->_getDepthPath($path) . basename($path);
    }

    public function createFileUrl($path)
    {
        return $this->getDomain() . $this->getUrlRoot() .'/'. $this->_getDepthPath($path) . basename($path);
    }

    public function createFileUrlPath($path)
    {
        return $this->getUrlRoot() .'/'. $this->_getDepthPath($path) . basename($path);
    }

    protected function _file($path)
    {
        $file = new Core_Storage_File($path);
        $file->setStorage($this);
        return $file;
    }
}