"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSrcCodePosFromEntry = exports.getSrcCodePosFromPath = exports.getRemainderAst = exports.getCurrentSrcLineNum = exports.getAncestorByName = exports.isPathMatch = exports.getEntryName = exports.constructFromCode = exports.construct = void 0;
const util_1 = __importDefault(require("util"));
const fs_1 = __importDefault(require("fs"));
const strip_bom_1 = __importDefault(require("strip-bom"));
const uglify_js_1 = require("../tools/uglify-js/uglify-js");
const ErrCodes = __importStar(require("./err_codes"));
const promisify_1 = __importDefault(require("../../utils/promisify"));
var readFile = (0, promisify_1.default)(fs_1.default.readFile);
function construct(fileName, ownerFilename, callback) {
    readFile(fileName)
        .then(data => {
        data = (0, strip_bom_1.default)(data);
        var constructed = null;
        try {
            constructed = constructFromCode(data, fileName);
        }
        catch (err) {
            callback(err);
            return;
        }
        callback(null, constructed.ast, constructed.preprocessedCode);
    })
        .catch(() => {
        callback({
            type: ownerFilename ? ErrCodes.FAILED_LOAD_REQUIRE : ErrCodes.READ_FILE_FAILED,
            filename: fileName,
            ownerFilename: ownerFilename
        });
    });
}
exports.construct = construct;
;
function constructFromCode(code, fileName) {
    var ast = null;
    code = code.toString().trim();
    //NOTE: perform srcCode preprocessing the same way it's done in the uglify tokenizer, so
    //we'll get correct entities positions for code generator
    code = code.replace(/\r\n?|[\n\u2028\u2029]/g, "\n");
    try {
        ast = uglify_js_1.parser.parse(code, false, true);
    }
    catch (parseErr) {
        throw {
            type: ErrCodes.JAVASCRIPT_PARSING_FAILED,
            filename: fileName,
            parserErr: parseErr
        };
    }
    return {
        ast: ast,
        preprocessedCode: code
    };
}
exports.constructFromCode = constructFromCode;
;
function getEntryName(entry) {
    return typeof entry[0] === 'object' ? entry[0].name : entry[0];
}
exports.getEntryName = getEntryName;
;
function isPathMatch(expectedPath, actualPath, ensureLast) {
    if (expectedPath.length !== actualPath.length - 1)
        return false;
    //NOTE: Check if it is a last entry in the path root
    if (ensureLast && !actualPath[1][0].last)
        return false;
    for (var i = 0; i < expectedPath.length; i++) {
        var hasFlag = util_1.default.isArray(expectedPath[i]), name = hasFlag ? expectedPath[i][0] : expectedPath[i];
        if (getEntryName(actualPath[i]) !== name || (hasFlag && actualPath[i][1] !== expectedPath[i][1]))
            return false;
    }
    return true;
}
exports.isPathMatch = isPathMatch;
;
function getAncestorByName(name, currentAstPath) {
    if (currentAstPath.length > 1) {
        for (var i = currentAstPath.length - 2; i >= 0; i--) {
            if (getEntryName(currentAstPath[i]) === name)
                return currentAstPath[i];
        }
    }
    return null;
}
exports.getAncestorByName = getAncestorByName;
;
function getCurrentSrcLineNum(currentAst) {
    //NOTE: Try to obtain the current line number. currentAst may not contain additional info for unknown reason.
    for (var i = currentAst.length - 1; i >= 0; i--) {
        if (currentAst[i]) {
            if (currentAst[i].start)
                return currentAst[i].start.line;
            if (currentAst[i][0] && currentAst[i][0].start)
                return currentAst[i][0].start.line;
        }
    }
    return 0;
}
exports.getCurrentSrcLineNum = getCurrentSrcLineNum;
;
function getRemainderAst(ast) {
    //NOTE: We traverse through statements of the 'toplevel' and
    //just remove those that are marked with 'remove' flag.
    //Everything that's left is a shared code.
    var astBranches = [];
    ast[1].forEach(function (statement) {
        if (!statement[0].remove)
            astBranches.push(statement);
    });
    return astBranches.length ? ['toplevel', astBranches] : null;
}
exports.getRemainderAst = getRemainderAst;
;
function getSrcCodePosFromPath(astPath) {
    return getSrcCodePosFromEntry(astPath[astPath.length - 1]);
}
exports.getSrcCodePosFromPath = getSrcCodePosFromPath;
;
function getSrcCodePosFromEntry(astEntry) {
    var pos = {
        start: 0,
        end: 0
    };
    if (astEntry && astEntry[0] && astEntry[0].start) {
        pos.start = astEntry[0].start.pos;
        pos.end = astEntry[0].end.endpos;
    }
    return pos;
}
exports.getSrcCodePosFromEntry = getSrcCodePosFromEntry;
;