"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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
    var g = generator.apply(thisArg, _arguments || []), i, q = [];
    return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
    function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
    function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
    function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
    function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
    function fulfill(value) { resume("next", value); }
    function reject(value) { resume("throw", value); }
    function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readableFromFileOrURL = readableFromFileOrURL;
exports.readFromFileOrURL = readFromFileOrURL;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const ts_necessities_1 = require("@glideapps/ts-necessities");
const browser_or_node_1 = require("browser-or-node");
const is_url_1 = __importDefault(require("is-url"));
const readable_stream_1 = require("readable-stream");
const Messages_js_1 = require("../../Messages.js");
const Support_js_1 = require("../../support/Support.js");
const WindowsPaths_js_1 = require("../../support/WindowsPaths.js");
const index_js_1 = require("./get-stream/index.js");
function parseHeaders(httpHeaders) {
    if (!Array.isArray(httpHeaders)) {
        return {};
    }
    return httpHeaders.reduce((result, httpHeader) => {
        if (httpHeader !== undefined && httpHeader.length > 0) {
            const split = httpHeader.indexOf(":");
            if (split < 0) {
                return (0, Support_js_1.panic)(`Could not parse HTTP header "${httpHeader}".`);
            }
            const key = httpHeader.slice(0, split).trim();
            const value = httpHeader.slice(split + 1).trim();
            result[key] = value;
        }
        return result;
    }, {});
}
function webStreamChunks(stream) {
    return __asyncGenerator(this, arguments, function* webStreamChunks_1() {
        const reader = stream.getReader();
        try {
            for (;;) {
                const { done, value } = yield __await(reader.read());
                if (done)
                    return yield __await(void 0);
                yield yield __await((0, ts_necessities_1.defined)(value));
            }
        }
        finally {
            reader.releaseLock();
        }
    });
}
// readable-stream implements Readable.from (it can't do Readable.fromWeb),
// but @types/readable-stream doesn't declare it, hence this cast.
const ReadableWithFrom = readable_stream_1.Readable;
function readableFromResponseBody(body) {
    // Native fetch returns a WHATWG ReadableStream, which lacks the Node
    // stream API that our consumers rely on, so we have to wrap it.
    if (typeof body.getReader === "function") {
        return ReadableWithFrom.from(webStreamChunks(body), { objectMode: false });
    }
    return body;
}
function resolveSymbolicLink(filePath) {
    if (!fs.lstatSync(filePath).isSymbolicLink()) {
        return filePath;
    }
    const linkPath = fs.readlinkSync(filePath);
    if (path.isAbsolute(linkPath)) {
        return linkPath;
    }
    return path.join(path.dirname(filePath), linkPath);
}
function readableFromFileOrURL(fileOrURL, httpHeaders) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            if (fileOrURL.startsWith("file://")) {
                fileOrURL = (0, WindowsPaths_js_1.filePathFromFileURI)(fileOrURL);
            }
            else if ((0, is_url_1.default)(fileOrURL)) {
                const response = yield globalThis.fetch(fileOrURL, {
                    headers: parseHeaders(httpHeaders),
                });
                return readableFromResponseBody((0, ts_necessities_1.defined)(response.body));
            }
            if (browser_or_node_1.isNode) {
                if (fileOrURL === "-") {
                    // Cast node readable to isomorphic readable from readable-stream
                    return process.stdin;
                }
                const filePath = resolveSymbolicLink(fileOrURL);
                if (fs.existsSync(filePath)) {
                    // Cast node readable to isomorphic readable from readable-stream
                    return fs.createReadStream(filePath, "utf8");
                }
            }
        }
        catch (e) {
            return (0, Messages_js_1.messageError)("MiscReadError", {
                fileOrURL,
                message: (0, ts_necessities_1.exceptionToString)(e),
            });
        }
        return (0, Messages_js_1.messageError)("DriverInputFileDoesNotExist", { filename: fileOrURL });
    });
}
function readFromFileOrURL(fileOrURL, httpHeaders) {
    return __awaiter(this, void 0, void 0, function* () {
        const readable = yield readableFromFileOrURL(fileOrURL, httpHeaders);
        try {
            return yield (0, index_js_1.getStream)(readable);
        }
        catch (e) {
            return (0, Messages_js_1.messageError)("MiscReadError", {
                fileOrURL,
                message: (0, ts_necessities_1.exceptionToString)(e),
            });
        }
    });
}
