"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ast_1 = require("../ast");
const traverse_1 = require("@glas/traverse");
const common_1 = require("../common");
function createConditionalDeclarations(root) {
    // first we need to find any IfStatements with consequent returns or throws that don't have a
    root = traverse_1.traverse(root, {
        leave(node, ancestors) {
            if (ast_1.BlockStatement.is(node)) {
                let { body } = node;
                for (let i = body.length - 1; i >= 0; i--) {
                    let statement = body[i];
                    if (ast_1.IfStatement.is(statement) && statement.alternate == null) {
                        let last = statement.consequent.body[statement.consequent.body.length - 1];
                        if (ast_1.ReturnStatement.is(last) || ast_1.ThrowStatement.is(last)) {
                            // we need to add everything that follows as implied
                            let remainder = body.slice(i + 1);
                            if (remainder.length > 0) {
                                body = [
                                    ...body.slice(0, i),
                                    statement.patch({
                                        alternate: new ast_1.BlockStatement({
                                            location: statement.location,
                                            body: remainder
                                        })
                                    })
                                ];
                            }
                        }
                    }
                }
                if (body !== node.body) {
                    return node.patch({ body });
                }
            }
        }
    });
    return traverse_1.traverse(root, {
        enter(node) {
        },
        leave(node, ancestors) {
            if (ast_1.IfStatement.is(node) && node.consequent.body.length > 0) {
                //  find all unique named references
                //  create a new ConditionalDeclaration for each, replacing named refs with DotExpression
                // find all variable declaration references
                let refs = new Set(common_1.getNodesOfType(node.test, ast_1.Reference.is).map(n => n.name) /*.filter(isLowerCase) */);
                // now we have to redeclare more strongly typed stuff.
                let newConsequents = new Array();
                let newAlternates = node.alternate != null ? new Array() : null;
                for (let name of refs) {
                    let { location } = node.test;
                    newConsequents.push(new ast_1.ConditionalDeclaration({
                        kind: "conditional",
                        location,
                        id: new ast_1.Declarator({ name, location })
                    }));
                    if (newAlternates) {
                        newAlternates.push(new ast_1.ConditionalDeclaration({
                            kind: "conditional",
                            negate: true,
                            location,
                            id: new ast_1.Declarator({ name, location })
                        }));
                    }
                }
                let { location } = node;
                let consequent = new ast_1.BlockStatement({ location, body: [...newConsequents, ...node.consequent.body] });
                let alternate = node.alternate;
                if (ast_1.IfStatement.is(alternate)) {
                    //  we must convert ifs to block statements so our scoped variable
                    //  won't interfere with it scoping the same named variable.
                    alternate = new ast_1.BlockStatement({
                        location,
                        body: [alternate]
                    });
                }
                if (ast_1.BlockStatement.is(alternate)) {
                    alternate = alternate.patch({
                        location,
                        body: [...newAlternates, ...alternate.body]
                    });
                }
                return node.patch({
                    consequent,
                    alternate,
                });
            }
        }
    });
}
exports.default = createConditionalDeclarations;
