<?php
namespace ModulesAbstract\FluxRest;

/**
 * Download files from TINA
 * @author Lucian Sirbu
 */
abstract class ControllerDownloader extends ControllerAbstract
{
	/**
	 * Get the path to the first file in queue. The same path will be requested later by the client app
	 * @return bool
	 */
	abstract protected function getFirstFileInQueue(\Entity\FluxInstance $fluxInstance);

	/**
	 * Get the file content for the specified filename
	 * @param string $filename
	 * @return string
	 */
	abstract protected function getFileContent($filename, \Entity\FluxInstance $fluxInstance);

	/**
	 * Confirm the file (move it on the server in the confirmed folder)
	 * @param mixed $request
	 * @param Response\Main $mainResponse
	 */
	abstract protected function actionRestConfirmWriteFile($request, \ModulesAbstract\FluxRest\Response\Main $mainResponse);

	/**
	 * {@inheritDoc}
	 * @see \ModulesAbstract\FluxRest\ControllerAbstract::onAuthenticate_localInitialization()
	 */
	protected function onAuthenticate_localInitialization($request, Response\Main $mainResponse)
	{
		$fluxInstance = $this->getAppTokenDetails($request->appToken);
		if (!$fluxInstance->basePath) {
			// instance not correctly configured
			$op = new Response\Operation\NotifyUserLog();
			$op->text = sprintf("<span class='warning'>%s</span>", "Instance not configured. base path is not set");
			$mainResponse->appendOperation($op);

			$op = new Response\Operation\Wait();
			$op->setTimeoutLimits(0, 60*60)->setTimeout(\Engine\RandomGenerator::randomFloat(15, 2*60));
			$mainResponse->appendOperation($op);

			$op = new Response\Operation\Authenticate();
			$mainResponse->appendOperation($op);

			\Engine\Os::sleep(\Engine\RandomGenerator::randomFloat(2.5, 10));
			return false;
		}

		// send the client settings
		$op = new Response\Operation\SetBasePath();
		$op->path = $fluxInstance->basePath;
		$mainResponse->appendOperation($op);

		return true;
	}

	/**
	 * (non-PHPdoc)
	 * @see \ModulesAbstract\FluxRest\ControllerAbstract::actionRestAuthenticate()
	 */
	protected function actionRestAuthenticate($request, Response\Main $mainResponse)
	{
		parent::actionRestAuthenticate($request, $mainResponse);

		$op = new Response\Operation\GetCommands();
		$op->responseHandling = 'send:overwrite';	// before FluX 1.10, TODO: remove this after 2021-01-01
		$mainResponse->appendOperation($op);
		$mainResponse->responseHandling = "overwrite"; // after FluX 1.11
	}

	/**
	 * Handle ExceptionInvalidFile errors, force re-auth when errors are encountered
	 * Treating errors like this can be dangerous, because we do not want to ignore all errors without control, and without confirming each errors & ignored file
	 */
// 	public function execute($action, array $R=null)
// 	{
// 		try {
// 			return parent::execute($action, $R);
// 		}
// 		catch (\ModulesAbstract\FluxRest\ExceptionInvalidFile $ex) {
// 			$mainResponse = new Response\Main();
// 			$op = new Response\Operation\NotifyUserLog();
// 			$op->text = sprintf("Cannot download file from TINA. <span class='warning'>%s</span>", $ex->getMessage());
// 			$mainResponse->appendOperation($op);
// 			$op = new Response\Operation\NotifyUserTray();
// 			$op->text = sprintf("%s", $ex->getMessage());
// 			$mainResponse->appendOperation($op);
// 			$op = new Response\Operation\Wait();
// 			$op->setTimeoutLimits(1, 2*60);
// 			$op->setTimeout(15);
// 			$op->display = 'none';
// 			$mainResponse->appendOperation($op);
// 			$op = new Response\Operation\NotifyUserLog();
// 			$op->text = sprintf("Reauthenticating");
// 			$mainResponse->appendOperation($op);
// 			$op = new Response\Operation\Authenticate();
// 			$mainResponse->appendOperation($op);
// 			return new \Engine\Mvc\Response\Json($mainResponse);
// 		}
// 	}

	/**
	 * (non-PHPdoc)
	 * @see \ModulesAbstract\FluxRest\ControllerAbstract::actionRestGetCommands()
	 */
	protected function actionRestGetCommands($request, Response\Main $mainResponse)
	{
		$this->actionRestCommonCommands($request, $mainResponse);

		$fluxInstance = $this->getRequestTokenDetails($request->token);

		$file = $this->getFirstFileInQueue($fluxInstance);
		if ($file) {
			$op = new Response\Operation\GetFirstFile();
			$mainResponse->appendOperation($op);

			// reset the timeout
			$this->queueSessionData['actionRestGetCommands']['lastTimeout'] = 0;
		}
		else {
			$lastTimeout = $this->queueSessionData['actionRestGetCommands']['lastTimeout'];

			switch($fluxInstance->behaviourOnIdle) {
				case 'exponential:15m':	// was the default
					$op = new Response\Operation\Wait();
					$op->setTimeoutLimits(0.5, 15*60)					// 15m max timelimit
						->setTimeout((max($lastTimeout, 0.1))*1.25);	// increase the timeout with 25% on every idle call
					$this->queueSessionData['actionRestGetCommands']['lastTimeout'] = $op->getTimeout();
					$mainResponse->appendOperation($op);
				break;

				case 'exponential:5m':
					$op = new Response\Operation\Wait();
					$op->setTimeoutLimits(0.5, 5*60)					// 5m max timelimit
						->setTimeout((max($lastTimeout, 0.1))*1.15);	// increase the timeout with 15% on every idle call
					$this->queueSessionData['actionRestGetCommands']['lastTimeout'] = $op->getTimeout();
					$mainResponse->appendOperation($op);
				break;
				case 'exponential:2m':
					$op = new Response\Operation\Wait();
					$op->setTimeoutLimits(0.5, 2*60)					// 2m max timelimit
						->setTimeout((max($lastTimeout, 0.1))*1.10);	// increase the timeout with 10% on every idle call
					$this->queueSessionData['actionRestGetCommands']['lastTimeout'] = $op->getTimeout();
					$mainResponse->appendOperation($op);
				break;
				case 'exponential:1m':
					$op = new Response\Operation\Wait();
					$op->setTimeoutLimits(0.5, 1*60)					// 1m max timelimit
						->setTimeout((max($lastTimeout, 0.1))*1.10);	// increase the timeout with 10% on every idle call
					$this->queueSessionData['actionRestGetCommands']['lastTimeout'] = $op->getTimeout();
					$mainResponse->appendOperation($op);
				break;

				default:
					throw new \Exception(sprintf("Unknown idle behaviour: %s", $fluxInstance->behaviourOnIdle));
			}

			$op = new Response\Operation\GetCommands();
			$op->responseHandling = 'send:overwrite';	// before FluX 1.10, TODO: remove this after 2021-01-01
			$mainResponse->appendOperation($op);
			$mainResponse->responseHandling = "overwrite"; // after FluX 1.11
		}
	}

	/**
	 *Get the first file in the queue
	 * @param Response\Main $mainResponse
	 */
	protected function actionRestGetFirstFile($request, Response\Main $mainResponse)
	{
		$this->validateRequestAuthenticated($request);

		$fluxInstance = $this->getRequestTokenDetails($request->token);

		$filename = $this->getFirstFileInQueue($fluxInstance);

		$filecontent = $this->getFileContent($filename, $fluxInstance);
		$op = new Response\Operation\WriteFile();

		// base64 encoding, for binary transfer
		$remoteFilename = $this->determineRemoteFilePath($filename, $fluxInstance);
		$op->fileName = $remoteFilename;
		$op->fileContent = base64_encode($filecontent);
		$op->fileCompression = "none";
		$op->fileEncoding = "base64";
		$op->fileHash = sprintf("md5:%s", md5($filecontent));

		/*
		$op->fileName = $filename;
		$op->fileContent = $filecontent;
		$op->fileCompression = "none";
		$op->fileEncoding = "none";
		$op->fileHash = sprintf("md5:%s", md5($filecontent));
		*/

		$mainResponse->appendOperation($op);

		$op = new Response\Operation\ConfirmWriteFile();
		$op->fileName = $filename;
		$mainResponse->appendOperation($op);

		$op = new Response\Operation\NotifyUserLog();
		$op->text = sprintf("Downloaded %s", $remoteFilename);
		$mainResponse->appendOperation($op);

		$op = new Response\Operation\GetCommands();
		$op->responseHandling = 'send:overwrite';	// before FluX 1.10, TODO: remove this after 2021-01-01
		$mainResponse->appendOperation($op);
		$mainResponse->responseHandling = "overwrite";	// after FluX 1.10
	}

	/**
	 * Determine the remot file path
	 * @param String $filename
	 * @param \Entity\FluxInstance $fluxInstance
	 * @throws \Exception
	 * @return string
	 */
	protected function determineRemoteFilePath($filename, \Entity\FluxInstance $fluxInstance)
	{
		switch ($fluxInstance->createMonthBasedFolders) {
			case 'Ym':
				$ts = (int)$this->timestamp;

				return sprintf("%s/%s",
					date("Y-m", $ts),
					$filename
				);
			break;
			case 'Ym/d':
				$ts = $this->timestamp;

				return sprintf("%s/%s/%s",
					date("Y-m", $ts),
					date("d", $ts),
					$filename
				);
			break;

			case 'none':
				return $filename;
			break;

			default:
			throw new \Exception(sprintf("[%s] Dont know how to handle createMonthBasedFolders=%s", __METHOD__, $fluxInstance->createMonthBasedFolders));
		}
	}
}
