"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
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 __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
    var m = o[Symbol.asyncIterator], i;
    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ixIxxStream = exports.ixIxx = void 0;
var util_1 = require("util");
var stream_1 = require("stream");
var events_1 = require("events");
var split2_1 = __importDefault(require("split2"));
var fs_1 = __importDefault(require("fs"));
var readline_1 = __importDefault(require("readline"));
var tmp_1 = __importDefault(require("tmp"));
var external_sorting_1 = __importDefault(require("external-sorting"));
tmp_1.default.setGracefulCleanup();
var streamFinished = util_1.promisify(stream_1.finished); // (A)
// this file (ixixx.ts) is a translation of ixIxx.c from ucscGenomeBrowser/kent
// the license of that file is reproduced below
/*
 * MIT License
 *
 * Copyright (C) 2001 UC Regents
 *
 * Permission is hereby granted, free of charge, to any person or non-commercial
 * entity obtaining a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
var binSize = 64 * 1024;
// Characters that may be part of a word
var wordMiddleChars = [];
var wordBeginChars = [];
function isalpha(c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
function isdigit(c) {
    return c >= '0' && c <= '9';
}
function isalnum(c) {
    return isalpha(c) || isdigit(c);
}
function initCharTables() {
    for (var c = 0; c < 256; ++c) {
        if (isalnum(String.fromCharCode(c))) {
            wordBeginChars[c] = wordMiddleChars[c] = true;
        }
    }
    wordBeginChars['_'.charCodeAt(0)] = wordMiddleChars['_'.charCodeAt(0)] = true;
    wordMiddleChars['.'.charCodeAt(0)] = true;
    wordMiddleChars['-'.charCodeAt(0)] = true;
}
var TrixInputTransform = /** @class */ (function (_super) {
    __extends(TrixInputTransform, _super);
    function TrixInputTransform() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TrixInputTransform.prototype._transform = function (chunk, encoding, done) {
        var _a = chunk.toString().split(/\s+/), id = _a[0], words = _a.slice(1);
        this.push(words.map(function (word) { return word.toLowerCase() + " " + id + "\n"; }).join(''));
        done();
    };
    return TrixInputTransform;
}(stream_1.Transform));
function elt(buff, current) {
    return current + " " + buff.map(function (elt, idx) { return elt + "," + (idx + 1); }).join(' ') + "\n";
}
var TrixOutputTransform = /** @class */ (function (_super) {
    __extends(TrixOutputTransform, _super);
    function TrixOutputTransform() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.buff = [];
        _this.current = '';
        return _this;
    }
    TrixOutputTransform.prototype._transform = function (chunk, encoding, done) {
        // weird: need to strip nulls from string, xref
        // https://github.com/GMOD/jbrowse-components/pull/2451
        var _a = chunk.toString().replace(/\0/g, '').split(' '), id = _a[0], data = _a[1];
        if (this.current !== id) {
            if (this.buff.length) {
                this.push(elt(this.buff, this.current));
                this.buff = [];
            }
            this.current = id;
        }
        this.buff.push(data);
        done();
    };
    TrixOutputTransform.prototype._flush = function (done) {
        if (this.buff.length) {
            this.push(elt(this.buff, this.current));
        }
        done();
    };
    return TrixOutputTransform;
}(stream_1.Transform));
function makeIxStream(fileStream, outIxFilename) {
    return __awaiter(this, void 0, void 0, function () {
        var _this = this;
        return __generator(this, function (_a) {
            return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
                    var dir, tempDir, out, input, output;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                initCharTables();
                                dir = tmp_1.default.dirSync({
                                    prefix: 'jbrowse-trix-sort',
                                });
                                tempDir = dir.name;
                                out = fs_1.default.createWriteStream(outIxFilename);
                                input = stream_1.pipeline(fileStream, split2_1.default(), new TrixInputTransform(), function (err) {
                                    if (err) {
                                        reject(err);
                                    }
                                });
                                output = split2_1.default();
                                stream_1.pipeline(output, new TrixOutputTransform(), out, function (err) {
                                    if (err) {
                                        reject(err);
                                    }
                                });
                                return [4 /*yield*/, external_sorting_1.default({
                                        input: input,
                                        output: output,
                                        tempDir: tempDir,
                                    }).asc()];
                            case 1:
                                _a.sent();
                                resolve(true);
                                return [2 /*return*/];
                        }
                    });
                }); })];
        });
    });
}
function makeIx(inFile, outIndex) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            return [2 /*return*/, makeIxStream(fs_1.default.createReadStream(inFile), outIndex)];
        });
    });
}
function getPrefix(word, prefixSize) {
    return word.slice(0, prefixSize).padEnd(prefixSize, ' ');
}
function makeIxx(inIx, outIxx, prefixSize) {
    var e_1, _a;
    if (prefixSize === void 0) { prefixSize = 5; }
    return __awaiter(this, void 0, void 0, function () {
        var out, fileStream, rl, lastPrefix, writtenPrefix, writtenPos, startPrefixPos, bytes, rl_1, rl_1_1, line, word, curPrefix, res, e_1_1;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    out = fs_1.default.createWriteStream(outIxx);
                    _b.label = 1;
                case 1:
                    _b.trys.push([1, , 17, 19]);
                    fileStream = fs_1.default.createReadStream(inIx);
                    rl = readline_1.default.createInterface({
                        input: fileStream,
                    });
                    lastPrefix = void 0;
                    writtenPrefix = void 0;
                    writtenPos = -binSize;
                    startPrefixPos = 0;
                    bytes = 0;
                    _b.label = 2;
                case 2:
                    _b.trys.push([2, 10, 11, 16]);
                    rl_1 = __asyncValues(rl);
                    _b.label = 3;
                case 3: return [4 /*yield*/, rl_1.next()];
                case 4:
                    if (!(rl_1_1 = _b.sent(), !rl_1_1.done)) return [3 /*break*/, 9];
                    line = rl_1_1.value;
                    word = line.split(/\s/)[0];
                    curPrefix = getPrefix(word, prefixSize);
                    if (curPrefix !== lastPrefix) {
                        startPrefixPos = bytes;
                    }
                    if (!(bytes - writtenPos >= binSize && curPrefix !== writtenPrefix)) return [3 /*break*/, 7];
                    res = out.write("" + curPrefix + startPrefixPos
                        .toString(16)
                        .toUpperCase()
                        .padStart(10, '0') + "\n");
                    if (!!res) return [3 /*break*/, 6];
                    return [4 /*yield*/, events_1.once(out, 'drain')];
                case 5:
                    _b.sent();
                    _b.label = 6;
                case 6:
                    writtenPos = bytes;
                    writtenPrefix = curPrefix;
                    _b.label = 7;
                case 7:
                    lastPrefix = curPrefix;
                    bytes += line.length + 1;
                    _b.label = 8;
                case 8: return [3 /*break*/, 3];
                case 9: return [3 /*break*/, 16];
                case 10:
                    e_1_1 = _b.sent();
                    e_1 = { error: e_1_1 };
                    return [3 /*break*/, 16];
                case 11:
                    _b.trys.push([11, , 14, 15]);
                    if (!(rl_1_1 && !rl_1_1.done && (_a = rl_1.return))) return [3 /*break*/, 13];
                    return [4 /*yield*/, _a.call(rl_1)];
                case 12:
                    _b.sent();
                    _b.label = 13;
                case 13: return [3 /*break*/, 15];
                case 14:
                    if (e_1) throw e_1.error;
                    return [7 /*endfinally*/];
                case 15: return [7 /*endfinally*/];
                case 16: return [3 /*break*/, 19];
                case 17:
                    out.end();
                    return [4 /*yield*/, streamFinished(out)];
                case 18:
                    _b.sent();
                    return [7 /*endfinally*/];
                case 19: return [2 /*return*/];
            }
        });
    });
}
function ixIxx(inText, outIx, outIxx, prefixSize) {
    if (prefixSize === void 0) { prefixSize = 5; }
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, makeIx(inText, outIx)];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, makeIxx(outIx, outIxx, prefixSize)];
                case 2:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    });
}
exports.ixIxx = ixIxx;
function ixIxxStream(stream, outIx, outIxx, prefixSize) {
    if (prefixSize === void 0) { prefixSize = 5; }
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, makeIxStream(stream, outIx)];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, makeIxx(outIx, outIxx, prefixSize)];
                case 2:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    });
}
exports.ixIxxStream = ixIxxStream;
