<?php

namespace NPS\Factory;

use NPS\Model\User;
use NPS\Model\File;
use NPS\Etc\Files;
use NPS\Entity\IEntity;
use Doctrine\ORM\EntityManagerInterface;
use NPS\Etc\Strings;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\File\UploadedFile as RequestFile;
use NPS\Entity\FileEntity;
use NPS\Exception\UnsupportedFileException;


/**
 * File factory and file uploader in one place
 * @author Lukas Korinek
 */
class FileFactory implements IObjectFactory
{
    use TMemorizedFactory;

    /** @var EntityManagerInterface $em */
    protected $em;

    /** @var UserFactory $userFactory */
    protected $userFactory;

    /** @var string $mediaPath */
    protected $mediaPath;

    /** @var string $mediaHiddenPath */
    protected $mediaHiddenPath;

    /** @var string $mediaBasePath */
    protected $mediaBasePath;

    public function __construct(EntityManagerInterface $em, UserFactory $userFactory, $mediaPath, $mediaHiddenUploadsPath, $mediaBasePath)
    {
        $this->em = $em;
        $this->userFactory = $userFactory;

        $this->mediaBasePath = $mediaBasePath;
        $this->mediaPath = Files::normalizeDirectory($mediaPath, Files::WRITEABLE);
        $this->mediaHiddenPath = Files::normalizeDirectory($mediaHiddenUploadsPath, Files::WRITEABLE);
    }

    /**
     * @inheritdoc
     */
    public function make(IEntity $entity)
    {
        return $this->load($entity, function($entity)
        {
            if(!$entity->getId())
            {
                $this->em->persist($entity);
            }

            $uploader = $entity->uploader ? $this->userFactory->make($entity->uploader) : null;
            return new File($entity, $uploader, $this->mediaBasePath);
        });
    }

    /**
     * Creates (uploads) the file by a path
     * @param int $id
     * @param string $path 
     * @param User $user
     * @param array $tags
     * @param bool $public
     * @param string $note
     */
    public function createFromPath($id, $path, User $user = null, array $tags, $public, $note)
    {
        $file = new RequestFile($path, basename($path));
        return $this->createFromRequest($id, $file, $user, $tags, $public, $note);
    }


    /** 
    * Creates (uploads) the file using symfony request
    * @param int $id 
    * @param RequestFile $file
    * @param User $user 
    * @param array $tags
    * @param bool $public
    * @param string $note
    */
    public function createFromRequest($id, RequestFile $file, User $user = null, array $tags, $public, $note = NULL)
    {
        $directory = ($public ? $this->mediaPath : $this->mediaHiddenPath) . '/' . date('Ym') . '/';
        $directory = Files::normalizeDirectory($directory, Files::WRITEABLE, true);

        $filename = $directory . '/' . $id . '_' . $file->getClientOriginalName();
        $filename = Files::normalizeFile($filename, Files::WRITEABLE);
        if(!copy($file->getRealPath(), $filename))
        {
            throw new \LogicException('File cannot be copied');
        }

        $entity = FileEntity::create([
            'realpath' => $filename,
            'filename' => $file->getClientOriginalName(),
            'path' => '/' . date('Ym') . '/' . basename($filename),
            'type' => $this->guessFileType($file),
            'note' => $note,
            'tags' => $tags,
            'uploader' => $user ? $user->getEntity() : null,
        ]);

        return $this->make($entity);
    }

    /**
     * Guesses file type based on its MIME
     * @param RequestFile $file
     * @return int
     */
    private function guessFileType(RequestFile $file)
    {
        $mime = $file->getMimeType();

        if(Strings::contains('image', $mime))
        {
            return File::FILE_IMAGE;
        }

        if(Strings::contains('video', $mime))
        {
            return File::FILE_VIDEO;
        }

        if(Strings::contains('audio', $mime))
        {
            return File::FILE_AUDIO;
        }

        if(Strings::contains('application/zip', $mime))
        {
            return File::FILE_ZIP;
        }

        throw new UnsupportedFileException($mime);
    }

}

