export interface ParsedDataURI {
	type: string;
	typeFull: string;
	charset: string;
	buffer: ArrayBuffer;
}

export interface IBufferConversions {
	base64ToArrayBuffer(base64: string): ArrayBuffer;
	stringToBuffer(str: string): ArrayBuffer;
}

/**
 * Returns a `Buffer` instance from the given data URI `uri`.
 *
 * @param {String} uri Data URI to turn into a Buffer instance
 */
export const makeDataUriToBuffer = (convert: IBufferConversions) => (uri: string | URL): ParsedDataURI => {
	uri = String(uri);

	if (!/^data:/i.test(uri)) {
		throw new TypeError(
			'`uri` does not appear to be a Data URI (must begin with "data:")'
		);
	}

	// strip newlines
	uri = uri.replace(/\r?\n/g, '');

	// split the URI up into the "metadata" and the "data" portions
	const firstComma = uri.indexOf(',');
	if (firstComma === -1 || firstComma <= 4) {
		throw new TypeError('malformed data: URI');
	}

	// remove the "data:" scheme and parse the metadata
	const meta = uri.substring(5, firstComma).split(';');

	let charset = '';
	let base64 = false;
	const type = meta[0] || 'text/plain';
	let typeFull = type;
	for (let i = 1; i < meta.length; i++) {
		if (meta[i] === 'base64') {
			base64 = true;
		} else if (meta[i]) {
			typeFull += `;${meta[i]}`;
			if (meta[i].indexOf('charset=') === 0) {
				charset = meta[i].substring(8);
			}
		}
	}
	// defaults to US-ASCII only if type is not provided
	if (!meta[0] && !charset.length) {
		typeFull += ';charset=US-ASCII';
		charset = 'US-ASCII';
	}

	// get the encoded data portion and decode URI-encoded chars
	const data = unescape(uri.substring(firstComma + 1));
	const buffer = base64 ? convert.base64ToArrayBuffer(data) : convert.stringToBuffer(data);

	return {
		type,
		typeFull,
		charset,
		buffer,
	};
}
