<?php

namespace App\Classes;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\File\File as FileBase;

use App\Classes\FTN\Packet;

class File extends FileBase implements \Iterator
{
	private const LOGKEY = 'F--';
	private int $counter = 0;
	private bool $isArchive = FALSE;
	private bool $canHandle = FALSE;
	private \ZipArchive $z;
	private array $zipfile = [];

	public function __construct(mixed $path,bool $checkPath=true)
	{
		parent::__construct($path,$checkPath);

		switch($x=$this->guessExtension()) {
			case 'zip':
				$this->canHandle = TRUE;
				$this->isArchive = TRUE;
				$this->z = new \ZipArchive;
				$this->z->open($this->getRealPath());
				break;

			case NULL:
			case 'bin':
				if ($this->isPacket() || ($path instanceof UploadedFile && (strcasecmp($path->getClientOriginalExtension(),'pkt') === 0))) {
					$this->canHandle = TRUE;
					break;
				}

			default:
				Log::alert(sprintf('%s:? Unknown file received: %s (%s) [%s]',self::LOGKEY,$x,$this->getExtension(),$path instanceof UploadedFile ? $path->getClientOriginalExtension() : '-'));
		}
	}

	/* ITERATOR */

	public function current()
	{
		if ($this->isArchive) {
			$this->zipfile = $this->z->statIndex($this->counter,\ZipArchive::FL_UNCHANGED);

			$f = $this->z->getStream($this->zipfile['name']);
			if (! $f)
				throw new \Exception(sprintf('%s:Failed getting ZipArchive::stream (%s)',self::LOGKEY,$this->z->getStatusString()));

			return $f;

		} else {
			return fopen($this->getRealPath(),'r+');
		}
	}

	public function next()
	{
		$this->counter++;
	}

	public function key()
	{
		return $this->counter;
	}

	public function valid()
	{
		// If we have a pkt file, then counter can only be 1.
		return $this->canHandle && (($this->isArchive && ($this->counter < $this->z->numFiles)) || $this->counter === 0);
	}

	public function rewind()
	{
		$this->counter = 0;
	}

	/* METHODS */

	/**
	 * Determine if the file is a mail packet
	 *
	 * @return bool
	 */
	private function isPacket(): bool
	{
		return (strcasecmp($this->getExtension(),'pkt') === 0);
	}

	public function itemName(): string
	{
		return ($this->isArchive && $this->valid()) ? Arr::get(stream_get_meta_data($this->current()),'uri') : $this->getFilename();
	}

	public function itemSize(): int
	{
		return $this->isArchive ? Arr::get($this->zipfile,'size') : $this->getSize();
	}

	/**
	 * Return the name of the file, without a node ID, mtime prefix
	 *
	 * @return string
	 */
	public function rawName(): string
	{
		return preg_replace(sprintf('/^%s\.pkt$/i',Packet::regex),'\3\4',$this->getFilename());
	}

	/**
	 * Return the packet name
	 *
	 * @return string|null
	 * @throws \Exception
	 */
	public function pktName(): ?string
	{
		if ($this->isArchive) {
			$this->zipfile = $this->z->statIndex($this->counter,\ZipArchive::FL_UNCHANGED);

			$f = $this->z->getStream($this->zipfile['name']);
			if (! $f)
				throw new \Exception(sprintf('%s:Failed getting ZipArchive::stream (%s)',self::LOGKEY,$this->z->getStatusString()));

			return preg_replace('/.pkt$/i','',Arr::get(stream_get_meta_data($f),'uri'));

		} else {
			return $this->isPacket() ? $this->rawName() : NULL;
		}
	}
}