<?php
	
	namespace Api\Libs\Rcon\Games;
	
	use Exception;
	use Api\Libs\Rcon\BaseRcon;
	use Api\Libs\Rcon\IRcon;
	
	class Fivem extends BaseRcon implements IRcon
	{
		protected string $checkCommand = "status";
		
		/**
		 * Connect to a server.
		 *
		 * @return boolean
		 */
		public function connect()
		{
			$this->socket = fsockopen("udp://" . $this->host, $this->port, $errno, $errstr, 5);
			
			if (!$this->socket) {
				throw new Exception("Could not connect to server: $errstr ($errno)");
			}
			
			if (!$this->isConnectionValid()) {
				throw new Exception("Invalid RCON password or connection failed.");
			}
		}
		
		/**
		 * Disconnect from server.
		 *
		 * @return boolean
		 */
		public function disconnect(): bool
		{
			if (is_resource($this->socket)) {
				fclose($this->socket);
				$this->socket = null;
				return true;
			}
			return false;
		}
		
		/**
		 * Check if the connection to the server is valid.
		 * @return boolean
		 */
		public function checkConnection(): bool
		{
			try {
				$this->connect();
				$this->sendCommand("gamename");
				$this->disconnect();
				return true;
			} catch (Exception $e) {
				return false;
			}
		}
		
		/**
		 * Send a command to the connected server.
		 *
		 * @param string $command
		 * @param bool $response Whether to return the response from the server
		 *
		 * @return string|null|void
		 */
		public function sendCommand(string $command, bool $response = false)
		{
			$this->send("rcon " . $this->password . " $command");
			
			if ($response) {
				return $this->getResponse();
			}
		}
		
		private function send($str): void
		{
			fwrite($this->socket, "\xFF\xFF\xFF\xFF$str\x00");
		}
		
		/**
		 * Get the response from the server.
		 *
		 * @return string
		 */
		private function getResponse(): string
		{
			stream_set_timeout($this->socket, 0, 7e5);
			$s = '';
			do {
				$read = fread($this->socket, 9999);
				$s .= substr($read, strpos($read, "\n") + 1);
				$info = stream_get_meta_data($this->socket);
			} while (!$info["timed_out"]);
			
			return $s;
		}
		
		/**
		 * Check if the connection is valid by sending a validation command.
		 *
		 * @return bool
		 */
		private function isConnectionValid(): bool
		{
			// Send a status command to check if the connection is valid
			$response = $this->sendCommand($this->checkCommand, true);
			return !($response === false || $response === '' || strpos($response, 'Invalid password') !== false);
		}
	}