/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const { runtimeToString, RuntimeSpecMap } = require("./util/runtime");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */

class CodeGenerationResults {
	constructor() {
		/** @type {Map<Module, RuntimeSpecMap<CodeGenerationResult>>} */
		this.map = new Map();
	}

	/**
	 * @param {Module} module the module
	 * @param {RuntimeSpec} runtime runtime(s)
	 * @returns {CodeGenerationResult} the CodeGenerationResult
	 */
	get(module, runtime) {
		const entry = this.map.get(module);
		if (entry === undefined) {
			throw new Error(
				`No code generation entry for ${module.identifier()} (existing entries: ${Array.from(
					this.map.keys(),
					m => m.identifier()
				).join(", ")})`
			);
		}
		if (runtime === undefined) {
			const results = new Set(entry.values());
			if (results.size !== 1) {
				throw new Error(
					`No unique code generation entry for unspecified runtime for ${module.identifier()} (existing runtimes: ${Array.from(
						entry.keys(),
						r => runtimeToString(r)
					).join(", ")}).
Caller might not support runtime-dependent code generation (opt-out via optimization.usedExports: "global").`
				);
			}
			return results.values().next().value;
		} else {
			const result = entry.get(runtime);
			if (result === undefined) {
				throw new Error(
					`No code generation entry for runtime ${runtimeToString(
						runtime
					)} for ${module.identifier()} (existing runtimes: ${Array.from(
						entry.keys(),
						r => runtimeToString(r)
					).join(", ")})`
				);
			}
			return result;
		}
	}

	/**
	 * @param {Module} module the module
	 * @param {RuntimeSpec} runtime runtime(s)
	 * @param {string} sourceType the source type
	 * @returns {Source} a source
	 */
	getSource(module, runtime, sourceType) {
		return this.get(module, runtime).sources.get(sourceType);
	}

	/**
	 * @param {Module} module the module
	 * @param {RuntimeSpec} runtime runtime(s)
	 * @returns {ReadonlySet<string>} runtime requirements
	 */
	getRuntimeRequirements(module, runtime) {
		return this.get(module, runtime).runtimeRequirements;
	}

	/**
	 * @param {Module} module the module
	 * @param {RuntimeSpec} runtime runtime(s)
	 * @param {string} key data key
	 * @returns {any} data generated by code generation
	 */
	getData(module, runtime, key) {
		const data = this.get(module, runtime).data;
		return data === undefined ? undefined : data.get(key);
	}

	/**
	 * @param {Module} module the module
	 * @param {RuntimeSpec} runtime runtime(s)
	 * @param {CodeGenerationResult} result result from module
	 * @returns {void}
	 */
	add(module, runtime, result) {
		const map = this.map.get(module);
		if (map !== undefined) {
			map.set(runtime, result);
		} else {
			const newMap = new RuntimeSpecMap();
			newMap.set(runtime, result);
			this.map.set(module, newMap);
		}
	}
}

module.exports = CodeGenerationResults;
