<?php
namespace App\Utils\FileTransfer;

/**
 * Class that handles ftp file-uploads & listings
 * This class uses ftp_* functions from PHP
 * @see https://php.net/manual/en/book.ftp.php
 * @author Lucian Sirbu
 */
class FtpStandardTransfer extends \App\Utils\FileTransfer\TransferAbstract
{
	private $stream = null;

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::connect()
	 */
	public function connect($host, $port, $username, $password)
	{
		$this->stream = ftp_connect($host, $port);

		// display debug info
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_connect({%host})", ['host'=>$host]);
		}

		if (!$this->stream) {
			throw new \App\Utils\FileTransfer\TransferException("Cannot connect to {$host}:{$port}");
		}

		if ($username!==null && $password!==null) {
			if(!ftp_login($this->stream, $username, $password) ) {
				throw new \App\Utils\FileTransfer\TransferException("Cannot login into {$host}:{$port} as {$username}");
			}
		}
		else {
			if(!ftp_login($this->stream, 'anonymous', '') ) {
				throw new \App\Utils\FileTransfer\TransferException("Cannot login into {$host}:{$port} as `anonymous`");
			}
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::disconnect()
	 */
	public function disconnect()
	{
		if ($this->stream) {
			if (in_array('command', $this->debugOptions)) {
				\Engine\Debug\Dline::instance()->vprintf("ftp_close", []);
			}

			ftp_close($this->stream);
			$this->stream = null;
		}
	}

	/**
	 * Turns passive mode on or off
	 * @param string $pasv
	 * @throws \App\Utils\FileTransfer\TransferException
	 */
	public function setPasv($pasv=true)
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_pasv", []);
		}

		if (!ftp_pasv($this->stream, true)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_pasv() failed");
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::getFolderContents()
	 */
	public function getFolderContents($remotePath='.')
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_rawlist({%remotePath})", ['remotePath'=>$remotePath]);
		}
		$rawFilesList = ftp_rawlist($this->stream, '-lna '.addcslashes($remotePath, " "));

		if ($rawFilesList===false) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_rawlist() failed");
		}

		$files = [];
		foreach($rawFilesList as $rawFile) {
			$matches = [];
			preg_match("/^(?P<perm>(?P<is_dir>d|-)[rwx-]+)(?P<stuff>.{45}) (?P<name>.+)$/", $rawFile, $matches);

			$file = [];
			$file['is_dir'] = ($matches['is_dir']=='d');
			$file['name'] = $matches['name'];
			$files[] = $file;

		}
		return $files;
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::mkdir()
	 */
	public function mkdir($remotePath)
	{
		$remotePathComponents = array_filter(explode('/', $remotePath));

		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_pwd", []);
		}
		$curdir = ftp_pwd($this->stream);

		foreach ($remotePathComponents as $rp) {
			if (in_array('command', $this->debugOptions)) {
				\Engine\Debug\Dline::instance()->vprintf("ftp_chdir({%rp})", ['rp'=>$rp]);
			}
			if (!@ftp_chdir($this->stream, $rp)) {
				if (in_array('command', $this->debugOptions)) {
					\Engine\Debug\Dline::instance()->vprintf("ftp_mkdir({%rp})", ['rp'=>$rp]);
				}

				if (!ftp_mkdir($this->stream, $rp)) {
					throw new \App\Utils\FileTransfer\TransferException("ftp_mkdir failed for {$rp} (while creating {$remotePath})");
				}

				if (in_array('command', $this->debugOptions)) {
					\Engine\Debug\Dline::instance()->vprintf("ftp_chdir({%rp})", ['rp'=>$rp]);
				}
				if (!ftp_chdir($this->stream, $rp)) {
					throw new \App\Utils\FileTransfer\TransferException("ftp_chdir to child folder failed");
				}
			}
		}

		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_chdir({%rp})", ['rp'=>$curdir]);
		}
		if (!ftp_chdir($this->stream, $curdir)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_chdir to initial folder failed");
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::rmdir()
	 */
	public function rmdir($remotePath=".")
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_rmdir({%rp})", ['rp'=>$remotePath]);
		}
		if (!ftp_rmdir($this->stream, $remotePath)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_rmdir({$remotePath}) failed");
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::unlink()
	 */
	public function unlink($remotePath)
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_delete({%rp})", ['rp'=>$remotePath]);
		}
		if (!ftp_delete($this->stream, $remotePath)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_delete({$remotePath}) failed");
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::uploadFile()
	 */
	public function uploadFile($localPath, $remotePath, $mode= FTP_BINARY)
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_put('{%lp}', '{%rp}')", ['rp'=>$remotePath, 'lp'=>$localPath]);
		}
		if (!ftp_put($this->stream, $remotePath, $localPath, $mode)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_put({$remotePath}, {$localPath}) failed");
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Utils\FileTransfer\TransferAbstract::downloadFile()
	 */
	public function downloadFile($remotePath, $localPath)
	{
		if (in_array('command', $this->debugOptions)) {
			\Engine\Debug\Dline::instance()->vprintf("ftp_get('{%rp}', '{%lp}')", ['rp'=>$remotePath, 'lp'=>$localPath]);
		}
		if (!ftp_get($this->stream, $localPath, $remotePath, FTP_BINARY)) {
			throw new \App\Utils\FileTransfer\TransferException("ftp_get({$remotePath}, {$localPath}) failed");
		}
	}
}
