<?php declare(strict_types=1);

/*
 * This file is part of the Rapsys PackBundle package.
 *
 * (c) Raphaël Gertz <symfony@rapsys.eu>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Rapsys\PackBundle\Util;

use Psr\Container\ContainerInterface;

use Rapsys\PackBundle\RapsysPackBundle;
use Rapsys\PackBundle\Util\ImageUtil;
use Rapsys\PackBundle\Util\IntlUtil;
use Rapsys\PackBundle\Util\SluggerUtil;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * Manages file informations
 */
class FileUtil {
	/**
	 * Alias string
	 */
	protected string $alias;

	/**
	 * Config array
	 */
	protected array $config;

	/**
	 * Creates a new file util
	 *
	 * @param ContainerInterface $container The container instance
	 * @param ImageUtil $image The ImageUtil instance
	 * @param IntlUtil $intl The IntlUtil instance
	 * @param RouterInterface $router The router instance
	 * @param SluggerUtil $slugger The SluggerUtil instance
	 * @param TranslatorInterface $translator The translator instance
	 */
	public function __construct(protected ContainerInterface $container, protected ImageUtil $image, protected IntlUtil $intl, protected RouterInterface $router, protected SluggerUtil $slugger, protected TranslatorInterface $translator) {
		//Retrieve config
		$this->config = $container->getParameter($this->alias = RapsysPackBundle::getAlias());
	}

	/**
	 * Get file infos
	 *
	 * @param string $path The base path
	 * @param string $realpath The real path
	 * @param string $name The route name
	 * @param array $parameters The route parameters
	 * @param bool $si Use si unit
	 */
	public function getFile(string $path, string $realpath, string $name, array $parameters = [], bool $si = true): array {
		//Set file
		$file = new \SplFileObject($realpath);

		//Set size
		$size = $file->getSize();

		//Set unit
		$unit = $si ? 1000 : 1024;

		//Set index
		$index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ];

		//Get exp
		$exp = intval((log($size) / log($unit)));

		//Rebase number
		$number = round($size / pow($unit, $exp), 2);

		//Set ext, height, image, mime, preview, thumb and width
		$ext = $height = $image = $mime = $preview = $thumb = $width = false;

		//With supporter format extension
		if (($pos = strrpos($realpath, '.')) && ($ext = substr($realpath, $pos + 1)) && in_array($ext, $this->config['formats'])) {
			//With mime type
			//XXX: getimagesize is too slow to run against all files
			if (($image = getimagesize($realpath)) !== false) {
				//Set ext
				if (($ext = image_type_to_extension($image[2], false)) === false) {
					//Throw error
					throw new \Exception(sprintf('Unable to get "%s" file image extension', $realpath));
				}

				//Set height
				$height = $image[1];

				//Set mime
				$mime = image_type_to_mime_type($image[2]);

				//Set width
				$width = $image[0];

				//Set preview
				$preview = $this->image->getThumb($realpath, $height, $width, $ext);

				//Set preview
				$thumb = $this->image->getThumb($realpath, $this->config['thumb']['height'], $this->config['thumb']['weight'], $ext);
			}
		}

		//Return file infos
		return [
			'created' => (new \DateTime())->setTimestamp($file->getCTime()),
			'height' => $height,
			'intlsize' => $intlsize = $this->intl->number($number),
			'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'),
			'link' => $this->router->generate($name, ['path' => substr($realpath, strlen($path) + 1)] + $parameters),
			'download' => $this->router->generate('rapsyspack_download', ['path' => $short = $this->slugger->short($realpath), 'hash' => $this->slugger->hash($short), 'u' => $mtime = $file->getMTime()]),
			'mime' => $mime,
			'modified' => (new \DateTime())->setTimestamp($mtime),
			'name' => basename($realpath).' ('.$intlsize.' '.$intlunit.')',
			'preview' => $preview,
			'size' => $size,
			'thumb' => $thumb,
			'width' => $width,
			//TODO: preview for images ?
			//TODO: extra fields ? (preview, miniature, etc ?)
			//TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?)
			//TODO: XXX: finish this !!!
		];
	}

	/**
	 * Get path infos
	 *
	 * @param string $path The base path
	 * @param string $realpath The real path
	 * @param string $name The route name
	 * @param array $parameters The route parameters
	 * @param bool $si Use si unit
	 */
	//Route object instead of shitty array ?
	public function getInfos(string $path, string $realpath, string $slug, string $name, array $parameters = []) {
		//Set result
		$result = [
			'breadcrumbs' => [/*$breadcrumb*/],
			'directories' => [],
			'file' => [],
			'files' => [],
		];

		//Set base
		$base = '';

		//Iterate on breadcrumbs
		foreach (explode('/', substr($realpath, strlen($path))) as $value) {
			$result['breadcrumbs'][] = [
				'name' => $value ? '/ '.$value : $slug,
				'link' => $this->router->generate($name, ['path' => ($base .= ($base == '' ? '' : '/').$value)] + $parameters)
			];
		}

		//With file
		if (is_file($realpath)) {
			//Set file
			$result['file'] = $this->getFile($path, $realpath, $name, $parameters);
		//With directory
		//TODO: for pagination, files and directories are to be placed in a single array
		//TODO: only get informations on files and directories inside the pagination window !
		} elseif (is_dir($realpath)) {
			//Set dir
			$dir = dir($realpath);

			//Iterate on directory
			while (($item = $dir->read()) !== false) {
				//Skip ., .., .git, .svn, .htpasswd, .htgroup and .*.un~ items
				//TODO: set this regexp in config ?
				if (preg_match('/^(\.|\.\.|\.git|\.svn|\.ht(access|passwd|group)|\..*\.un~)$/', $item)) {
					//Skip it
					continue;
				}

				//Check item
				if (
					//Without item realpath
					!($itempath = realpath($realpath.'/'.$item)) ||
					//Item realpath not matching album path
					//XXX: skip files outside of album/element path (symlink outside of tree)
					//TODO: make it configurable ? by album/element/admin ?
					$path !== substr($itempath, 0, strlen($path))
				) {
					//Skip it
					continue;
				}

				//With directory
				if (is_dir($itempath)) {
					//Append directory
					//TODO: use this structure or revert to old $item.'/' => $link form
					$result['directories'][] = [
						'name' => $item.'/',
						'link' => $this->router->generate($name, ['path' => substr($itempath, strlen($path) + 1)] + $parameters)
						//TODO: add stats here ? like number of files ?
					];
				//With file
				} elseif (is_file($itempath)) {
					//Set file
					$result['files'][] = $this->getFile($path, $itempath, $name, $parameters);
				//With unknown type
				} else {
					//Throw 404
					throw new \Exception(sprintf('Unknown file "%s" type', $itempath));
				}
			}
		//With unknown type
		} else {
			//Throw 404
			throw new \Exception(sprintf('Unknown file "%s" type', $realpath));
		}

		//Return result
		return $result;
	}

	/**
	 * Get file infos
	 *
	 * @param string $path The file path
	 * @param bool $si Use si units
	 * @return array The file infos
	 * /
	public function infos(string $path, bool $si = true): array {
		//Stat file
		$stat = stat($path);

		//Set unit
		$unit = $si ? 1000 : 1024;

		//Set index
		$index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ];

		//Get exp
		$exp = intval((log($stat['size']) / log($unit)));

		//Rebase number
		$number = round($stat['size'] / pow($unit, $exp), 2);

		//Set file infos
		$fileinfos = [
			'intlsize' => $intlsize = $this->intl->number($number),
			'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'),
			'name' => basename($path).' ('.$intlsize.' '.$intlunit.')',
			'size' => $stat['size'],
			'ctime' => $stat['ctime'],
			'mtime' => $stat['mtime'],
			//TODO: preview for images ?
			//TODO: extra fields ? (preview, miniature, etc ?)
			//TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?)
			//TODO: XXX: finish this !!!
		];

		//With mimetype
		if (($mimetype = mime_content_type($path)) !== false) {
			//Set file mimetype
			$fileinfos['mimetype'] = $mimetype;

			//TODO: with image preview, movie webm preview, others a imagemagick file with format initials ?

			//With image
			if (
				$mimetype == 'image/jpeg' ||
				$mimetype == 'image/png' ||
				$mimetype == 'image/bmp' ||
				$mimetype == 'image/tiff' ||
				$mimetype == 'image/svg+xml'
			) {
				//Get image width and height
				if (($fileinfos['image'] = getimagesize($path)) === false) {
					//Throw error
					throw new \Exception(sprintf('Unable to get "%s" file image size', $path));
				}

				//Set thumb
				$fileinfos['thumb'] = $this->image->getThumb($path, 64, 64);
			}
		}
		/*
			'src' => 
				//With location user source image
				if (($isFile = is_file($source = $this->config['path'].'/location/'.$location['id'].'/'.$id.'.png')) && ($mtime = stat($source)['mtime'])) {
					//Set location image
					$this->context['locations'][$locationId]['image'] = $this->image->getThumb($location['miniature'], $mtime, $source);

			//With image mimetype
			if (in_array($fileinfos['mimetype'], [ 'image/jpeg', 'image/png', 'image/webp' ])) {
				header('Content-Type: text/plain');
				var_dump($fileinfos);
				exit;
				$file['thumb'] = $this->router->generate('rapsyspack_thumb', [ 'hash' => $this->slugger->hash(''), 'path' => $path, 'slug' => $slug ]);
				#public function thumb(Request $request, string $hash, int $updated, string $path, int $width, int $height): Response {
			}
		 * /

		//Return file infos
		return $fileinfos;
	}*/
}
