"use strict";
/*
 * edge-parser
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EdgeBuffer = void 0;
const os_1 = require("os");
const js_stringify_1 = __importDefault(require("js-stringify"));
/**
 * Buffer class to construct template
 */
class EdgeBuffer {
    constructor(filename, options) {
        this.filename = filename;
        this.outputFileAndLineNumber = true;
        this.outputOutVariable = true;
        this.outputReturnStatement = true;
        this.wrapInsideTryCatch = true;
        this.options = {
            outputVar: '',
            rethrowCallPath: '',
            fileNameVar: '$filename',
            lineVar: '$lineNumber',
        };
        /**
         * Prefixes and suffix to wrap the final output
         */
        this.prefix = [];
        this.suffix = [];
        /**
         * Collected lines
         */
        this.buffer = [];
        /**
         * Current runtime line number
         */
        this.currentLineNumber = 1;
        /**
         * Current runtime filename
         */
        this.currentFileName = this.filename;
        this.outputVariableName = options.outputVar;
        this.options.outputVar = options.outputVar;
        this.options.rethrowCallPath = Array.isArray(options.rethrowCallPath)
            ? options.rethrowCallPath.join('.')
            : options.rethrowCallPath;
    }
    /**
     * Creates a new buffer instance by merging the options from the existing one
     */
    create(filename, options) {
        return new EdgeBuffer(filename, Object.assign({}, this.options, options));
    }
    /**
     * Returns the size of buffer text
     */
    get size() {
        return this.buffer.length;
    }
    /**
     * Setup template with initial set of lines
     */
    setup(buffer) {
        /**
         * Define output variable
         */
        this.outputOutVariable && buffer.push(`let ${this.outputVariableName} = "";`);
        /**
         * Define line number variable
         */
        this.outputFileAndLineNumber && buffer.push(`let ${this.options.lineVar} = 1;`);
        /**
         * Define filename variable
         */
        this.outputFileAndLineNumber &&
            buffer.push(`let ${this.options.fileNameVar} = ${(0, js_stringify_1.default)(this.filename)};`);
        /**
         * Write try block
         */
        this.wrapInsideTryCatch && buffer.push('try {');
    }
    /**
     * Tear down template by writing final set of lines
     */
    teardown(buffer) {
        if (this.wrapInsideTryCatch) {
            /**
             * Close try and catch block
             */
            buffer.push('} catch (error) {');
            /**
             * Write catch block
             */
            buffer.push(`${this.options.rethrowCallPath}(error, ${this.options.fileNameVar}, ${this.options.lineVar});`);
            /**
             * End catch block
             */
            buffer.push('}');
        }
        /**
         * Return output variable
         */
        this.outputReturnStatement && buffer.push(`return ${this.outputVariableName};`);
    }
    /**
     * Update the filename at runtime
     */
    updateFileName(filename) {
        if (this.currentFileName !== filename) {
            this.currentFileName = filename;
            this.buffer.push(`${this.options.fileNameVar} = ${(0, js_stringify_1.default)(filename)};`);
        }
    }
    /**
     * Update the line number at runtime
     */
    updateLineNumber(lineNumber) {
        if (lineNumber > 0 && this.currentLineNumber !== lineNumber) {
            this.currentLineNumber = lineNumber;
            this.buffer.push(`${this.options.lineVar} = ${lineNumber};`);
        }
    }
    /**
     * Write raw text to the output variable
     */
    outputRaw(text) {
        this.buffer.push(`${this.outputVariableName} += ${(0, js_stringify_1.default)(text)};`);
        return this;
    }
    /**
     * Write JS expression to the output variable
     */
    outputExpression(text, filename, lineNumber, templateLiteral) {
        this.updateFileName(filename);
        this.updateLineNumber(lineNumber);
        text = templateLiteral ? `\`\${${text}}\`` : text;
        this.buffer.push(`${this.outputVariableName} += ${text};`);
        return this;
    }
    /**
     * Write JS expression
     */
    writeExpression(text, filename, lineNumber) {
        this.updateFileName(filename);
        this.updateLineNumber(lineNumber);
        this.buffer.push(`${text};`);
        return this;
    }
    /**
     * Write JS statement. Statements are not suffixed with a semi-colon. It
     * means, they can be used for writing `if/else` statements.
     */
    writeStatement(text, filename, lineNumber) {
        this.updateFileName(filename);
        this.updateLineNumber(lineNumber);
        this.buffer.push(`${text}`);
        return this;
    }
    /**
     * Wrap template with a custom prefix and suffix
     */
    wrap(prefix, suffix) {
        this.prefix.push(prefix);
        this.suffix.push(suffix);
        return this;
    }
    /**
     * Disable instantiation of the file and the line number variables.
     */
    disableFileAndLineVariables() {
        this.outputFileAndLineNumber = false;
        return this;
    }
    /**
     * Disable instantiation of the out variable.
     */
    disableOutVariable() {
        this.outputOutVariable = false;
        return this;
    }
    /**
     * Disable outputting the return statement
     */
    disableReturnStatement() {
        this.outputReturnStatement = false;
        return this;
    }
    /**
     * Disable wrapping buffer output inside try/catch.
     */
    disableTryCatchBlock() {
        this.wrapInsideTryCatch = false;
        return this;
    }
    /**
     * Return template as a string
     */
    flush() {
        if (this.compiledOutput !== undefined) {
            return this.compiledOutput;
        }
        let buffer = [];
        /**
         * Write prefixes
         */
        this.prefix.forEach((text) => text.split(os_1.EOL).forEach((line) => buffer.push(`${line}`)));
        /**
         * Write setup code
         */
        this.setup(buffer);
        /**
         * Copy template contents
         */
        buffer = buffer.concat(this.buffer);
        /**
         * Write teardown code
         */
        this.teardown(buffer);
        /**
         * Write prefixes
         */
        this.suffix.forEach((text) => text.split(os_1.EOL).forEach((line) => buffer.push(`${line}`)));
        this.compiledOutput = buffer.join(os_1.EOL);
        return this.compiledOutput;
    }
}
exports.EdgeBuffer = EdgeBuffer;
