const
	util = require( '../util' ),
	placeholderClass = 'lazy-image-placeholder';

/**
 * @ignore
 * @param {HTMLElement} root
 * @return {HTMLElement[]}
 */
function queryPlaceholders( root ) {
	return Array.prototype.slice.call(
		root.getElementsByClassName( placeholderClass )
	);
}

/**
 * Load an image on demand
 *
 * @ignore
 * @param {HTMLElement[]} placeholders a list of images that have not been loaded.
 * @return {jQuery.Deferred}
 */
function loadImages( placeholders ) {
	return util.Promise.all(
		placeholders.map( ( placeholder ) => module.exports.loadImage( placeholder ).promise )
	);
}

/**
 * Load an image on demand
 *
 * @ignore
 * @param {HTMLElement} placeholder
 * @return {{promise: jQuery.Deferred<'load'|'error'>, image: HTMLImageElement}}
 */
function loadImage( placeholder ) {
	const
		deferred = util.Deferred(),
		// data-width and height are attributes and do not specify dimension.
		width = placeholder.dataset.width,
		height = placeholder.dataset.height,
		image = new Image();

	if ( width ) {
		image.setAttribute( 'width', parseInt( width, 10 ) );
	}
	if ( height ) {
		image.setAttribute( 'height', parseInt( height, 10 ) );
	}

	// eslint-disable-next-line mediawiki/class-doc
	image.className = placeholder.dataset.class || '';
	image.alt = placeholder.dataset.alt || '';
	image.useMap = placeholder.dataset.usemap;
	image.style.cssText = placeholder.style.cssText || '';

	// When the image has loaded
	image.addEventListener( 'load', () => {
		// Swap the HTML inside the placeholder (to keep the layout and
		// dimensions the same and not trigger layouts
		image.classList.add( 'image-lazy-loaded' );
		if ( placeholder.parentNode ) {
			placeholder.parentNode.replaceChild( image, placeholder );
		}
		deferred.resolve( 'load' );
	}, { once: true } );
	image.addEventListener( 'error', () => {
		// Swap the HTML and let the browser decide what to do with the broken image.
		if ( placeholder.parentNode ) {
			placeholder.parentNode.replaceChild( image, placeholder );
		}
		// Never reject. Quietly resolve so that Promise.all() awaits for all Deferreds to complete.
		// Reevaluate using Deferred.reject in T136693.
		deferred.resolve( 'error' );
	}, { once: true } );

	// Trigger image download after binding the load handler
	const src = placeholder.dataset.mwSrc || '';
	if ( src ) {
		image.src = src;
		image.srcset = placeholder.dataset.mwSrcset || '';
	}

	return {
		promise: deferred,
		image
	};
}

module.exports = {
	placeholderClass,
	queryPlaceholders,
	loadImages,
	loadImage,
	test: {
		placeholderClass
	}
};
