const {HttpError} = require('./HttpError');
const {Readable} = require('stream');
const zlib = require('zlib');

const MATCH_ACCEPT = new RegExp('^ *([^;]+)(?:;q=([0-9]+(?:\\.[0-9]+)?))? *$');

function parseAcceptEncoding(accept) {
	// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3

	const opts = (accept || 'identity;q=1, *;q=0.5').split(',');
	const types = new Map();
	opts.forEach((opt) => {
		const match = MATCH_ACCEPT.exec(opt);
		if(match) {
			let q = Number.parseFloat(match[2] || '1');
			if(q === 0) {
				q = -1;
			}
			types.set(match[1], q);
		}
	});
	if(!types.has('*')) {
		if(!types.has('identity')) {
			types.set('identity', -0.5);
		}
		types.set('*', -1);
	}
	return types;
}

function pickAcceptEncoding(types, preferred) {
	let best = null;
	let bestQ = -1;
	const wildcard = types.get('*');

	preferred.forEach((opt) => {
		const q = types.get(opt) || wildcard;
		if(q > bestQ) {
			best = opt;
			bestQ = q;
		}
	});
	if(best === null) {
		throw new HttpError(406, 'Not Acceptable');
	}
	return best;
}

function writeEncoded(res, encoding, compressionOpts, data) {
	if(encoding === 'identity') {
		res.end(data);
		return;
	}

	res.setHeader('Content-Encoding', encoding);

	const raw = new Readable();
	raw.push(data, 'utf8');
	raw.push(null);

	switch(encoding) {
	case 'gzip':
		raw.pipe(zlib.createGzip(compressionOpts)).pipe(res);
		break;
	case 'deflate':
		raw.pipe(zlib.createDeflate(compressionOpts)).pipe(res);
		break;
	default:
		throw new HttpError(500, 'Failed to encode');
	}
}

module.exports = {
	parseAcceptEncoding,
	pickAcceptEncoding,
	writeEncoded,
};
