(function () {
	'use strict';

	function extensionForMime(mime) {
		if (mime === 'image/jpeg') {
			return 'jpg';
		}

		return mime.replace('image/', '');
	}

	function replaceExtension(name, mime) {
		var extension = extensionForMime(mime);
		var baseName = name.replace(/\.[^.]+$/, '');

		return baseName + '.' + extension;
	}

	function dimensionsFor(bitmap, maxWidth) {
		var width = bitmap.width;
		var height = bitmap.height;

		if (maxWidth > 0 && width > maxWidth) {
			height = Math.round(height * (maxWidth / width));
			width = maxWidth;
		}

		return {
			width: Math.max(1, width),
			height: Math.max(1, height)
		};
	}

	async function canvasToBlob(canvas, mime, quality) {
		if (typeof canvas.convertToBlob === 'function') {
			return canvas.convertToBlob({
				type: mime,
				quality: quality
			});
		}

		return new Promise(function (resolve, reject) {
			canvas.toBlob(resolve, mime, quality);
			setTimeout(function () {
				reject(new Error('Canvas encoding timed out.'));
			}, 30000);
		});
	}

	async function encodeWithFallback(canvas, requestedMime, quality) {
		try {
			return {
				blob: await canvasToBlob(canvas, requestedMime, quality),
				mime: requestedMime,
				fallback: false
			};
		} catch (error) {
			if (requestedMime !== 'image/avif') {
				throw error;
			}
		}

		return {
			blob: await canvasToBlob(canvas, 'image/webp', quality),
			mime: 'image/webp',
			fallback: true
		};
	}

	async function processImage(message) {
		var settings = message.settings;
		var file = message.file;
		var requestedMime = settings.mime;
		var quality = Math.min(1, Math.max(0.01, settings.quality / 100));
		var maxWidth = parseInt(settings.maxWidth, 10) || 0;
		var bitmap;
		var canvas;

		self.postMessage({ id: message.id, type: 'progress', progress: 8 });

		if (typeof createImageBitmap !== 'function' || typeof OffscreenCanvas === 'undefined') {
			throw new Error('This browser does not support worker-based image conversion.');
		}

		bitmap = await createImageBitmap(file);
		self.postMessage({ id: message.id, type: 'progress', progress: 35 });

		var dimensions = dimensionsFor(bitmap, maxWidth);
		canvas = new OffscreenCanvas(dimensions.width, dimensions.height);
		var context = canvas.getContext('2d', { alpha: requestedMime === 'image/png' });

		context.drawImage(bitmap, 0, 0, dimensions.width, dimensions.height);
		self.postMessage({ id: message.id, type: 'progress', progress: 70 });

		var result = await encodeWithFallback(canvas, requestedMime, quality);
		if (!result.blob || result.blob.size < 1) {
			throw new Error('The browser returned an empty optimized image.');
		}

		self.postMessage({ id: message.id, type: 'progress', progress: 100 });
		self.postMessage({
			id: message.id,
			type: 'done',
			blob: result.blob,
			fileName: replaceExtension(file.name, result.mime),
			mime: result.mime,
			originalSize: file.size,
			optimizedSize: result.blob.size,
			width: dimensions.width,
			height: dimensions.height,
			fallback: result.fallback
		});

		if (bitmap && typeof bitmap.close === 'function') {
			bitmap.close();
		}

		if (canvas) {
			canvas.width = 1;
			canvas.height = 1;
		}
	}

	self.addEventListener('message', function (event) {
		processImage(event.data).catch(function (error) {
			self.postMessage({
				id: event.data.id,
				type: 'error',
				message: error && error.message ? error.message : 'Image optimization failed.'
			});
		});
	});
}());
