/**
 * @fileoverview Virtual file
 * @author Nicholas C. Zakas
 */

"use strict";

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------

/** @typedef {import("@eslint/core").File} File */

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Determines if a given value has a byte order mark (BOM).
 * @param {string|Uint8Array} value The value to check.
 * @returns {boolean} `true` if the value has a BOM, `false` otherwise.
 */
function hasUnicodeBOM(value) {
    return typeof value === "string"
        ? value.charCodeAt(0) === 0xFEFF
        : value[0] === 0xEF && value[1] === 0xBB && value[2] === 0xBF;
}

/**
 * Strips Unicode BOM from the given value.
 * @param {string|Uint8Array} value The value to remove the BOM from.
 * @returns {string|Uint8Array} The stripped value.
 */
function stripUnicodeBOM(value) {

    if (!hasUnicodeBOM(value)) {
        return value;
    }

    if (typeof value === "string") {

        /*
         * Check Unicode BOM.
         * In JavaScript, string data is stored as UTF-16, so BOM is 0xFEFF.
         * http://www.ecma-international.org/ecma-262/6.0/#sec-unicode-format-control-characters
         */
        return value.slice(1);
    }

    /*
     * In a Uint8Array, the BOM is represented by three bytes: 0xEF, 0xBB, and 0xBF,
     * so we can just remove the first three bytes.
     */
    return value.slice(3);
}

//------------------------------------------------------------------------------
// Exports
//------------------------------------------------------------------------------

/**
 * Represents a virtual file inside of ESLint.
 * @implements {File}
 */
class VFile {

    /**
     * The file path including any processor-created virtual path.
     * @type {string}
     * @readonly
     */
    path;

    /**
     * The file path on disk.
     * @type {string}
     * @readonly
     */
    physicalPath;

    /**
     * The file contents.
     * @type {string|Uint8Array}
     * @readonly
     */
    body;

    /**
     * The raw body of the file, including a BOM if present.
     * @type {string|Uint8Array}
     * @readonly
     */
    rawBody;

    /**
     * Indicates whether the file has a byte order mark (BOM).
     * @type {boolean}
     * @readonly
     */
    bom;

    /**
     * Creates a new instance.
     * @param {string} path The file path.
     * @param {string|Uint8Array} body The file contents.
     * @param {Object} [options] Additional options.
     * @param {string} [options.physicalPath] The file path on disk.
     */
    constructor(path, body, { physicalPath } = {}) {
        this.path = path;
        this.physicalPath = physicalPath ?? path;
        this.bom = hasUnicodeBOM(body);
        this.body = stripUnicodeBOM(body);
        this.rawBody = body;
    }
}

module.exports = { VFile };
