class UploadQueue {
	constructor(options) {
		this.queue = options.queue_element;
		this.preview_area = options.preview_area;
		this.upload_progress = [];
		this.upload_url = options.upload_url;
		this.submit = options.submit_button;
		this.addEvents();
	}

	addEvents() {
		this.queue.addEventListener('change', event => {
			this.showSpinner(this.queue, "Generating previews (not uploading yet!)");
			this.clearPreviews();
			for (let i = 0; i < this.queue.files.length; i++) {
				const callback = (i !== this.queue.files.length - 1) ? null : () => {
					this.hideSpinner();
					this.submit.disabled = false;
				};

				if (this.queue.files[0].name.toUpperCase().endsWith(".HEIC")) {
					alert('Sorry, the HEIC image format is not supported.\nPlease convert your photos to JPEG before uploading.');
					this.hideSpinner();
					this.submit.disabled = false;
					break;
				}

				this.addPreviewBoxForQueueSlot(i);
				this.addPreviewForFile(this.queue.files[i], i, callback);
			};
		});
		this.submit.addEventListener('click', event => {
			event.preventDefault();
			this.process();
		});
		this.submit.disabled = true;
	}

	clearPreviews() {
		this.preview_area.innerHTML = '';
		this.submit.disabled = true;
		this.current_upload_index = -1;
	}

	addPreviewBoxForQueueSlot(index) {
		const preview_box = document.createElement('div');
		preview_box.id = 'upload_preview_' + index;
		this.preview_area.appendChild(preview_box);
	}

	addPreviewForFile(file, index, callback) {
		if (!file) {
			return false;
		}

		const preview = document.createElement('canvas');
		preview.title = file.name;

		const preview_box = document.getElementById('upload_preview_' + index);
		preview_box.appendChild(preview);

		const reader = new FileReader();
		reader.addEventListener('load', event => {
			const original = document.createElement('img');
			original.src = reader.result;

			original.addEventListener('load', function() {
				// Preparation: make canvas size proportional to the original image.
				preview.height = 150;
				preview.width = preview.height * (original.width / original.height);

				// First pass: resize to 50% on temp canvas.
				const temp = document.createElement('canvas'),
					tempCtx = temp.getContext('2d');

				temp.width = original.width * 0.5;
				temp.height = original.height * 0.5;
				tempCtx.drawImage(original, 0, 0, temp.width, temp.height);

				// Second pass: resize again on temp canvas.
				tempCtx.drawImage(temp, 0, 0, temp.width * 0.5, temp.height * 0.5);

				// Final pass: resize to desired size on preview canvas.
				const context = preview.getContext('2d');
				context.drawImage(temp, 0, 0, temp.width * 0.5, temp.height * 0.5,
					0, 0, preview.width, preview.height);

				if (callback) {
					callback();
				}
			});
		}, false);
		reader.readAsDataURL(file);
	}

	process() {
		this.showSpinner(this.submit, "Preparing to upload files...");
		if (this.queue.files.length > 0) {
			this.submit.disabled = true;
			this.nextFile();
		}
	}

	nextFile() {
		const files = this.queue.files;
		const i = ++this.current_upload_index;
		if (i === files.length) {
			this.hideSpinner();
		} else {
			this.setSpinnerLabel("Uploading file " + (i + 1) + " out of " + files.length);
			this.sendFile(files[i], i, this.nextFile);
		}
	}

	sendFile(file, index, callback) {
		const request = new XMLHttpRequest();
		request.addEventListener('error', event => {
			this.updateProgress(index, -1);
		});
		request.addEventListener('progress', event => {
			this.updateProgress(index, event.loaded / event.total);
		});
		request.addEventListener('load', event => {
			this.updateProgress(index, 1);
			if (request.responseText !== null && request.status === 200) {
				const obj = JSON.parse(request.responseText);
				if (obj.error) {
					alert(obj.error);
					return;
				}
				else if (callback) {
					callback.call(this, obj);
				}
			}
		});

		const data = new FormData();
		data.append('uploads', file, file.name);

		request.open('POST', this.upload_url, true);
		request.send(data);
	}

	addProgressBar(index) {
		if (index in this.upload_progress) {
			return;
		}

		const progress_container = document.createElement('div');
		progress_container.className = 'progress';

		const progress = document.createElement('div');
		progress_container.appendChild(progress);

		const preview_box = document.getElementById('upload_preview_' + index);
		preview_box.appendChild(progress_container);

		this.upload_progress[index]	= progress;
	}

	updateProgress(index, progress) {
		if (!(index in this.upload_progress)) {
			this.addProgressBar(index);
		}

		const bar = this.upload_progress[index];

		if (progress >= 0) {
			bar.style.width = Math.ceil(progress * 100) + '%';
		} else {
			bar.style.width = "";
			if (progress === -1) {
				bar.className = "error";
			}
		}
	}

	showSpinner(sibling, label) {
		if (this.spinner) {
			return;
		}

		this.spinner = document.createElement('div');
		this.spinner.className = 'spinner';
		sibling.parentNode.appendChild(this.spinner);

		if (label) {
			this.spinner_label = document.createElement('span');
			this.spinner_label.className = 'spinner_label';
			this.spinner_label.innerHTML = label;
			sibling.parentNode.appendChild(this.spinner_label);
		}
	}

	setSpinnerLabel(label) {
		if (this.spinner_label) {
			this.spinner_label.innerHTML = label;
		}
	}

	hideSpinner() {
		if (this.spinner) {
			this.spinner.parentNode.removeChild(this.spinner);
			this.spinner = null;
		}
		if (this.spinner_label) {
			this.spinner_label.parentNode.removeChild(this.spinner_label);
			this.spinner_label = null;
		}
	}
}