<?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\Classes;

/**
 * A stream writer for arbitrary files.
 *
 * Calling close() on an instance of this class at some point is mandatory, if the
 * output stream has been opened, which in turn is the case when at least one chunk of data
 * has been written to the output stream.
 */
class FileResponseStream
{
    /**
     * @var string
     */
    private $contentDispositionType;

    /**
     * @var
     */
    private $contentType;

    /**
     * @var mixed
     */
    protected $handle;

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

    /**
     * @var \Enlight_Controller_Response_ResponseHttp
     */
    private $response;

    /**
     * @param \Enlight_Controller_Response_ResponseHttp $response the response to write to
     * @param string $fileName the name of the download attachment as presented to the user
     * @param string $contentType the mime type of the file to be send
     * @param bool $forceDownload if true the file should be sent as attachment, that is downloaded and
     *        saved locally by the browser; if false the file is expected to be displayed inline
     *        by the browser
     */
    public function __construct(
        \Enlight_Controller_Response_ResponseHttp $response,
        $fileName,
        $contentType,
        $forceDownload = false
    ) {
        $this->contentDispositionType = $forceDownload ? 'attachment' : 'inline';
        $this->contentType = $contentType;
        $this->fileName = $fileName;
        $this->response = $response;
    }

    /**
     * @return bool true if the stream is open, false otherwise
     */
    public function isOpen()
    {
        return $this->handle !== null;
    }

    /**
     * Writes a given chunk of data to the stream.
     *
     * @param string $chunkOfData the chunk of data to be written to the stream
     * @throws \Exception
     */
    public function write($chunkOfData)
    {
        $this->ensureStreamIsWritable();
        fwrite($this->handle, $chunkOfData);
    }

    /**
     * Closes the output stream. Calling this method once you're finished writing data is mandatory.
     */
    public function close()
    {
        if ($this->isOpen()) {
            fclose($this->handle);
            $this->handle = null;
        }
    }

    /**
     * Ensures that the output stream is open and all HTTP headers are sent.
     *
     * @throws \Exception if the output stream could not be opened or the headers could not be sent
     */
    protected function ensureStreamIsWritable()
    {
        if ($this->isOpen()) {
            return;
        }

        $this->sendHeaders();

        $this->handle = fopen('php://output', 'w');
        if ($this->handle === false) {
            $this->handle = null;
            throw new \Exception('Output stream could not be opened.');
        }
    }

    /**
     * Sends all necessary HTTP headers.
     *
     * @throws \Exception if headers could not be sent
     * @throws \Zend_Controller_Response_Exception
     */
    protected function sendHeaders()
    {
        if (!$this->response->canSendHeaders()) {
            throw new \Exception('Headers could not be sent.');
        }

        $this->response->setHeader('Content-Type', $this->contentType);
        $this->response->setHeader('Content-disposition', $this->contentDispositionType . '; filename=' . $this->fileName, true);
        $this->response->sendHeaders();
    }
}
