<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Components\FileStorage;

use Shopware\Plugins\ViisonCommon\Classes\Exceptions\FileSystemExceptions\FileNotReadableException;
use Shopware\Plugins\ViisonCommon\Classes\Exceptions\FileSystemExceptions\FileNotStreamableException;
use Shopware\Plugins\ViisonCommon\Classes\Exceptions\FileSystemExceptions\FileNotWritableException;
use Shopware\Plugins\ViisonCommon\Classes\FileResponseStream;

class LocalDirectoryFileStorage implements FileStorage
{
    /**
     * @var string full path to the local directory were the files are read from and written to
     */
    private $localDirectory;

    /**
     * @param string $localDirectory full path to the local directory were the files are read from and written to
     */
    public function __construct($localDirectory)
    {
        $this->localDirectory = $localDirectory;
    }

    /**
     * @inheritdoc
     */
    public function readFileContents($fileName)
    {
        $localFilePath = $this->getLocalFilePath($fileName);
        if (!is_readable($localFilePath)) {
            throw new FileNotReadableException($localFilePath);
        }

        $contents = file_get_contents($localFilePath);

        if ($contents === false) {
            throw new FileNotReadableException($localFilePath);
        }

        return $contents;
    }

    /**
     * @inheritdoc
     */
    public function writeFileContents($fileName, $contents)
    {
        $localFilePath = $this->getLocalFilePath($fileName);
        if ((file_exists($localFilePath) && !is_writable($localFilePath)) || !is_writable(dirname($localFilePath))) {
            throw new FileNotWritableException($localFilePath);
        }

        $success = file_put_contents($localFilePath, $contents);

        if ($success === false) {
            throw new FileNotWritableException($localFilePath);
        }
    }

    /**
     * @inheritdoc
     */
    public function streamFile($fileName, FileResponseStream $fileResponseStream)
    {
        $localFilePath = $this->getLocalFilePath($fileName);
        if (!is_readable($localFilePath)) {
            throw new FileNotReadableException($localFilePath);
        }
        $fileHandle = fopen($localFilePath, 'rb');
        if ($fileHandle === false) {
            throw new FileNotReadableException($localFilePath);
        }

        $chunkSizeInBytes = 1024 * 1024;
        try {
            while (!feof($fileHandle)) {
                $nextChunkOfData = fgets($fileHandle, $chunkSizeInBytes);
                $fileResponseStream->write($nextChunkOfData);
            }
        } catch (\Exception $e) {
            throw new FileNotStreamableException($localFilePath, $e);
        } finally {
            $fileResponseStream->close();
            fclose($fileHandle);
        }
    }

    /**
     * @inheritDoc
     */
    public function deleteFile($fileName)
    {
        $localFilePath = $this->getLocalFilePath($fileName);

        if (!file_exists($localFilePath)) {
            throw new FileNotReadableException(dirname($localFilePath));
        }

        $success = unlink($localFilePath);
        if (!$success) {
            throw new FileNotWritableException(dirname($localFilePath));
        }
    }

    /**
     * @inheritDoc
     */
    public function doesFileExist($fileName)
    {
        $localFilePath = $this->getLocalFilePath($fileName);

        return file_exists($localFilePath);
    }

    /**
     * @param string $fileName
     * @return string
     */
    private function getLocalFilePath($fileName)
    {
        $localDir = ($this->localDirectory === '') ? '' : (rtrim($this->localDirectory, '/\\') . '/');

        return $localDir . $fileName;
    }
}
