<?php

if ($_SERVER['REQUEST_METHOD'] == "POST") {

	$id = isset($_POST["id"]) ? $_POST["id"] : -1;
	$filename = "/servers/www/files/uploads/";
	$filesize = 0;

	$uploadedFiles = [];
	for ($i = 0, $directoryListing = scandir($filename), $im = sizeof($directoryListing); $i < $im; $i++) {
		$testTarget = $directoryListing[$i];
		if (is_dir($filename . $testTarget)) {
			continue;
		}
		if ($testTarget == "." || $testTarget == "..") {
			continue;
		}
		$temp = preg_match("/(\d*)\-(.*)\.temp/", $testTarget, $matches) == 1;
		if (!$temp)
			preg_match("/(\d*)\-(.*)/", $testTarget, $matches);
		$uploadedFiles[] = [$testTarget,intval($matches[1]),$matches[2],$temp];//full filename, id, user filename, temp file?
	}

	$userFilename = NULL;
	$filesize = 0;
	$temp = true;
	if ($id < 0) {
		$userFilename = isset($_POST["filename"]) ? $_POST["filename"] : NULL;
		$filesize = isset($_POST["filesize"]) ? intval($_POST["filesize"]) : 0;
		if ($userFilename == NULL || $filesize < 1) {
			$id = -1;
			goto returnAndExit;
		}

		for ($i = 0, $im = sizeof($uploadedFiles); $i < $im; $i++) {
			if ($uploadedFiles[$i][1] > $id) {
				$id = $uploadedFiles[$i][1];
			}
		}
		$id++;
		
		$filename .= $id . "-" . $userFilename . ".temp";
		$fileHook = fopen($filename,"c");
		
		if ( $fileHook==FALSE ) {
			$id = -6; //FAILED TO CREATE FILE
			goto returnAndExit;
		}
	 
	 
		$currentFilesize = 0;
		while ($currentFilesize < $filesize) {
			$addSize = min(1024*16,$filesize-$currentFilesize);
			if ( fwrite($fileHook,str_repeat("\0",$addSize))==FALSE ) {
				$id = -5; //NOT ENOUGH SPACE
				goto returnAndExit;
			}
			$currentFilesize += $addSize;
		}

		fclose($fileHook);
		goto returnAndExit;
		
	} else {
		$chunkSize = isset($_POST["chunksize"]) ? intval($_POST["chunksize"]) : 0;
		$chunk = isset($_POST["chunk"]) ? intval($_POST["chunk"]) : -1;
		if ($chunk < 0 || $chunkSize < 1 || !isset($_FILES["data"])) {
			$id = -3; //INVALID ARGUMENTS
			goto returnAndExit;
		}
		$file = NULL;
		
		for ($i = 0, $im = sizeof($uploadedFiles); $i < $im; $i++) {
			if ($uploadedFiles[$i][1] == $id) {
				$file = $uploadedFiles[$i];
				$userFilename = $uploadedFiles[$i][2];
				break;
			}
		}
		if ($userFilename == NULL) {
			$id = -2;
			goto returnAndExit;
		}
		$nonTempFilename = $filename . $id . "-" . $userFilename;
		$filename .= $id . "-" . $userFilename . ".temp";
		if (!file_exists($filename)) {
			$id = -4;
			goto returnAndExit;
		}
		$filesize = filesize($filename);
		
		if ($_FILES["data"]["size"]+$chunkSize*$chunk > $filesize) {
			$id = -9; //INVALID CHUNK SIZE
			goto returnAndExit;
		}
		$rawData = file_get_contents($_FILES["data"]["tmp_name"]);
		if (!isset($rawData)) {
			$id = -8; //FAILED TO READ FILE
			goto returnAndExit;
		}
		
		
		$fileHook = fopen($filename, "r+");
		fseek($fileHook, $chunkSize*$chunk);
		
		if ( fwrite($fileHook,$rawData)==FALSE ) {
			$id = -7; //IO ERROR
			goto returnAndExit;
		}
		
		if ($_FILES["data"]["size"]+$chunkSize*$chunk == $filesize) {
			rename($filename,$nonTempFilename);
			echo json_encode([
				"id" => $id,
				"filename" => $id . "-" . $userFilename
			]);
			exit;
		}
		
	}

	returnAndExit:

	echo json_encode([
		"id" => $id
	]);
	exit;
} elseif ($_SERVER['REQUEST_METHOD'] == "GET") {
?>

<!DOCTYPE html>
<html>
	<head>
		<title>File uploader</title>
		<script>
			window.retryTimes = 2;
			window.chunkSize = 1000*100;
		
			window.performance = window.performance || {};
			performance.now = (function() {
					return performance.now       ||
						  performance.mozNow    ||
						  performance.msNow     ||
						  performance.oNow      ||
						  performance.webkitNow ||            
						  Date.now
			})();
			var uploadErrors = [
				"Invalid filename/filesize",
				"Couldent find temp file on server",
				"Invalid arguments",
				"Couldent write to file",
				"Not enough space on server",
				"Failed to create file on server",
				"Server IO error",
				"Failed to read file from server",
				"Invalid chunk size"];
			function makeRequest(method, url, data) {
				return new Promise(function (resolve, reject) {
				  let xhr = new XMLHttpRequest();
				  xhr.open(method, url);
				  xhr.onload = function () {
			      if (this.status >= 200 && this.status < 300) {
			          resolve(xhr.response);
			      } else {
			          reject({
			              status: this.status,
			              message: xhr.statusText
			          });
			      }
				  };
				  xhr.onerror = function () {
			      reject({
		          status: this.status,
		          message: xhr.statusText
			      });
				  };
				  xhr.send(data);
				});
			}
			async function* initChunkUploadFile(file, filename, onFail, onProgress, onFinish) {
				try {
					var maxMethodTime = window.performance.now() + 1000 / 10,
							currentIndex = 0, currentChunk = 0, attempt = 0;
							
					var data = new FormData();
					data.append("filename", filename);
					data.append("filesize", file.size);
					result = await makeRequest("POST","/upload",data);
					result = /.*?({.*})$/.exec(result)[1];
					result = JSON.parse(result);
					if (result.id < 0) {
						onFail(filename, uploadErrors[-1-result.id]);
						return;
					}
					var id = result.id;
					if (window.performance.now()>maxMethodTime) yield;
					
					while (currentIndex<file.size) {
						if (window.performance.now()>maxMethodTime) yield;
						data = new FormData();
						data.append("id",id);
						data.append("chunk", currentChunk++);
						data.append("chunksize", window.chunkSize);
						var nextIndex = Math.min(file.size,currentIndex+window.chunkSize);
						data.append("data", file.slice(currentIndex, nextIndex));
						currentIndex = nextIndex;
						
						var i = 0;
						while (true) {
							if (window.performance.now()>maxMethodTime) yield;
							try {
								var result = await makeRequest("POST","/upload",data);
								result = /.*?({.*})$/.exec(result)[1];
								result = JSON.parse(result);
								if (result.id < 0) {
									throw new Error(uploadErrors[-1-result.id]);
								} else {
								 break;
								}
							} catch (e) {
								i++;
								if (i >= window.retryTimes) {
									throw e;
								}
							}
						}
						onProgress(file.size,currentIndex,filename);
					}
					
					onFinish(result.filename);
				} catch (e) {
					console.error(e);
					onFail(filename, e.message);
				}
			}
			function humanReadableBytes(size, rounding) {
				var powerIndex = 0;
				while (size / Math.pow(1000,powerIndex) > 999)
					powerIndex++;
				rounding = Math.pow(10,rounding);
				size = size / Math.pow(1000,powerIndex);
				size = Math.round(size * rounding) / rounding;
				return size + ["B","kB","MB","GB","TB","PB"][powerIndex];
			}
			
			
			window.addEventListener("load", function() {
				var progressBar = window.progressBar,
						fileInput = window.fileInput,
						uploadButton = window.uploadButton,
						fileNameInput = window.fileName,
						progressBarInner = progressBar.children[0],
						statusElement = window.statusText;
				var workerProcess = null;
						
				var defaultName = true;
				function filenameChange() {
					if (fileNameInput.value.trim()=="") {
						defaultName = true;
					} else {
					  defaultName = false;
					}
				}
				function initUpload() {
					displayedProgress = 0;
					progressStatus = 0;
					if (fileInput.files.length == 0)
						return;
					uploadButton.disabled = true;
					fileInput.disabled = true;
					progressBarInner.setAttribute("progress","");
					fileName = fileInput.files[0].name;
					fileNameInput.value = fileNameInput.value.trim();
					if (defaultName)
						fileName = fileNameInput.value;
					workerProcess = initChunkUploadFile(fileInput.files[0], fileName, uploadFail, uploadProgress, uploadFinish);
					lastTime = window.performance.now();
				}
				function uploadFail(fileName, reason) {
					uploadButton.disabled = false;
					fileInput.disabled = false;
					progressBarInner.setAttribute("progress","fail");
					if (reason)
						statusElement.innerHTML = "Failed to upload: " + reason;
					else
						statusElement.innerHTML = "Failed to upload";
				}
				var lastUploaded = 0, lastTime = 0;
				function uploadProgress(fileSize, uploaded, fileName) {
					var speed = (uploaded - lastUploaded) / (window.performance.now() - lastTime) * 1000;
					lastTime = window.performance.now();
					lastUploaded = uploaded;
					
					progressStatus = Math.round(uploaded / fileSize * 1000) / 10;
					statusElement.innerHTML = humanReadableBytes(speed, 2) + "/s\n" + humanReadableBytes(uploaded, 2) + " of " + humanReadableBytes(fileSize, 2) + " for: \n" + fileName;
				}
				function uploadFinish(fileName) {
					fileInput.disabled = false;
					uploadButton.disabled = false;
					progressStatus = 100;
					progressBarInner.setAttribute("progress","finish");
					var url = "http://files.arcanes-spoteh.nz/uploads/"+fileName;
					statusElement.innerHTML = "<a target=\"_blank\" href=\""+url+"\">"+url+"</a>";
				}
				
				function fileSelected() {
					if (fileInput.files.length == 1) {
						fileName.placeholder = fileInput.files[0].name;
						fileName.disabled = false;
						if (fileName.value == "")
							fileName.value = fileInput.files[0].name;
					} else if(fileInput.files.length > 1) {
						fileName.disabled = true;
						fileName.placeholder = "";
					} else {
						fileName.disabled = false;
						fileName.placeholder = "";
					}
				}
				
				var progressStatus = 0, displayedProgress = 0;
				function render() {
					requestAnimationFrame(render);
					
					if (workerProcess) {
						if (workerProcess.next().done)
							workerProcess = null;
					}
					
					if (displayedProgress * 4 + window.tmp != 0)
						displayedProgress = (displayedProgress * 10 + progressStatus) / 11 || 0;
					progressBarInner.style.width = displayedProgress+"%";
					
				}
				
				fileSelected();
				uploadButton.addEventListener("click", initUpload);
				fileInput.addEventListener("change", fileSelected);
				fileNameInput.addEventListener("change",filenameChange);
				
				render();
			});
		</script>
		<style>
			div, body {
				box-sizing: border-box;
				position: relative;
				font-family: Consolas,monaco,monospace; 
			}
			html {
				overflow: hidden;
				height: 100%;
				width: 100%;
			}
			body {
				margin:0;
				padding: 10px;
				height: 100%;
				width: 100%;
				background-color: #272727;
			}
			#container {
				margin: auto;
				padding: 10px;
				background-color: #eeeeee;
				display: block;
				width: min-content;
			}
			#container > * {
				margin: 3px;
			}
			#progressBar {
				height: 25px;
				overflow:hidden;
				background-color: #7f7f7f;
			}
			#progressBar > div {
				height: 25px;
				overflow:hidden;
				background-color: #28e9ff;
			}
			#progressBar > div[progress="fail"] {
				background-color: #fd4242;
			}
			#progressBar > div[progress="finish"] {
				background-color: #63f042;
			}
			#uploadButton {
				margin:auto;
				display:block;
			}
			#status {
				color: #7f7f7f;
				padding: 4px;
			}
			input {
				font-family: Consolas,monaco,monospace; 
			}
			#fileName {
				width: 99%;
				box-sizing: border-box;
			}
			a[target="_blank"]::after {
				content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
				margin: 0 3px 0 5px;
			}
		</style>
	</head><body>
		<div id="container">
			<input type="file" id="fileInput"/><br/>
			Upload as: <input type="text" id="fileName"/><br />
			<hr>
			<button id="uploadButton">Upload</button><br />
			<div id="progressBar"><div></div></div>
			<span id="statusText">dsa</span>
		</div>
	</body>
</html>

<?php
}




