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

const RequireEnsureDependenciesBlock = require("./RequireEnsureDependenciesBlock");
const RequireEnsureItemDependency = require("./RequireEnsureItemDependency");
const getFunctionExpression = require("./getFunctionExpression");

module.exports = class RequireEnsureDependenciesBlockParserPlugin {
	apply(parser) {
		parser.plugin("call require.ensure", expr => {
			let chunkName = null;
			let chunkNameRange = null;
			let errorExpressionArg = null;
			let errorExpression = null;
			switch(expr.arguments.length) {
				case 4:
					{
						const chunkNameExpr = parser.evaluateExpression(expr.arguments[3]);
						if(!chunkNameExpr.isString()) return;
						chunkNameRange = chunkNameExpr.range;
						chunkName = chunkNameExpr.string;
					}
					// falls through
				case 3:
					{
						errorExpressionArg = expr.arguments[2];
						errorExpression = getFunctionExpression(errorExpressionArg);

						if(!errorExpression && !chunkName) {
							const chunkNameExpr = parser.evaluateExpression(expr.arguments[2]);
							if(!chunkNameExpr.isString()) return;
							chunkNameRange = chunkNameExpr.range;
							chunkName = chunkNameExpr.string;
						}
					}
					// falls through
				case 2:
					{
						const dependenciesExpr = parser.evaluateExpression(expr.arguments[0]);
						const dependenciesItems = dependenciesExpr.isArray() ? dependenciesExpr.items : [dependenciesExpr];
						const successExpressionArg = expr.arguments[1];
						const successExpression = getFunctionExpression(successExpressionArg);

						if(successExpression) {
							parser.walkExpressions(successExpression.expressions);
						}
						if(errorExpression) {
							parser.walkExpressions(errorExpression.expressions);
						}

						const dep = new RequireEnsureDependenciesBlock(expr,
							successExpression ? successExpression.fn : successExpressionArg,
							errorExpression ? errorExpression.fn : errorExpressionArg,
							chunkName, chunkNameRange, parser.state.module, expr.loc);
						const old = parser.state.current;
						parser.state.current = dep;
						try {
							let failed = false;
							parser.inScope([], () => {
								dependenciesItems.forEach(ee => {
									if(ee.isString()) {
										const edep = new RequireEnsureItemDependency(ee.string, ee.range);
										edep.loc = dep.loc;
										dep.addDependency(edep);
									} else {
										failed = true;
									}
								});
							});
							if(failed) {
								return;
							}
							if(successExpression) {
								if(successExpression.fn.body.type === "BlockStatement")
									parser.walkStatement(successExpression.fn.body);
								else
									parser.walkExpression(successExpression.fn.body);
							}
							old.addBlock(dep);
						} finally {
							parser.state.current = old;
						}
						if(!successExpression) {
							parser.walkExpression(successExpressionArg);
						}
						if(errorExpression) {
							if(errorExpression.fn.body.type === "BlockStatement")
								parser.walkStatement(errorExpression.fn.body);
							else
								parser.walkExpression(errorExpression.fn.body);
						} else if(errorExpressionArg) {
							parser.walkExpression(errorExpressionArg);
						}
						return true;
					}
			}
		});
	}
};
