"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TeradataConnection = void 0;
const ffi = __importStar(require("ffi-napi"));
const ref = __importStar(require("ref-napi"));
const arrayType = require("ref-array-di")(ref);
const fs = require("fs");
const path = require("path");
const os = require("os");
const teradata_cursor_1 = require("./teradata-cursor");
const teradata_logging_1 = require("./teradata-logging");
const teradata_exceptions_1 = require("./teradata-exceptions");
class TeradataConnection {
    constructor() {
        this.sVersionNumber = "";
        if (!["arm64", "x64"].includes(process.arch)) {
            throw new Error("This package requires 64-bit Node.js. 32-bit Node.js is not supported.");
        }
        const sPackageJsonPathName = path.resolve(__dirname, "..", "package.json");
        try {
            const packageJson = fs.readFileSync(sPackageJsonPathName);
            JSON.parse(packageJson, (key, value) => {
                if (key === "version") {
                    this.sVersionNumber = value;
                }
            });
        }
        catch (error) {
            console.log(`sPackageJsonPathName=${sPackageJsonPathName}`);
            fs.stat(sPackageJsonPathName, (err, stat) => {
                if (err) {
                    console.log(err);
                }
                else {
                    if (stat.isFile()) {
                        console.log(stat);
                    }
                    else {
                        console.log(`${sPackageJsonPathName} is not a file.`);
                    }
                }
            });
            throw new teradata_exceptions_1.OperationalError(error.message);
        }
        this.poolHandle = null;
        this.ref = ref;
        this.byteArray = new arrayType(this.ref.types.byte);
        this.logLevel = 0;
        this.logger = new teradata_logging_1.TeradataLogging(this.logLevel);
        const osType = process.platform;
        const bARM = process.arch.startsWith("arm") || process.arch.startsWith("aarch");
        let sExtension = "";
        if (osType === "win32") {
            sExtension = "dll";
        }
        else if (osType === "darwin") {
            sExtension = "dylib";
        }
        else if (bARM) {
            sExtension = "arm.so";
        }
        else {
            sExtension = "so";
        }
        const sLibPathName = path.resolve(__dirname, "teradatasql." + sExtension);
        try {
            this.lib = ffi.Library(`${sLibPathName}`, {
                jsgoCreateConnection: ["void", ["ulong", "char*", "char*", "char**", this.ref.refType(this.ref.types.ulong)]],
                jsgoCloseConnection: ["void", ["ulong", "ulong", "char**"]],
                jsgoCancelRequest: ["void", ["ulong", "ulong", "char**"]],
                jsgoCreateRows: ["void", ["ulong", "ulong", "char*", "ulong", "void*", "char**", this.ref.refType(this.ref.types.ulong)]],
                jsgoFetchRow: ["void", ["ulong", "ulong", "char**", this.ref.refType(this.ref.types.long), "void**", "long"]],
                jsgoResultMetaData: [
                    "void",
                    ["ulong", "ulong", "char**", this.ref.refType(this.ref.types.ulong), this.ref.refType(this.ref.types.int), "void**"],
                ],
                jsgoNextResult: ["void", ["ulong", "ulong", "char**", "char*"]],
                jsgoCloseRows: ["void", ["ulong", "ulong", "char**"]],
                jsgoFreePointer: ["void", ["ulong", "void*"]],
            });
        }
        catch (error) {
            console.log(`sLibPathName=${sLibPathName}`);
            fs.stat(sLibPathName, (err, stat) => {
                if (err) {
                    console.log(err);
                }
                else {
                    if (stat.isFile()) {
                        console.log(stat);
                    }
                    else {
                        console.log(`${sLibPathName} is not a file.`);
                    }
                }
            });
            throw new teradata_exceptions_1.OperationalError(error.message);
        }
    }
    get uLog() {
        return this.logLevel;
    }
    get uPoolHandle() {
        return this.poolHandle;
    }
    get autocommit() {
        this.logger.traceLog("> enter autocommit getter");
        let result;
        try {
            const cur = this.cursor();
            cur.execute("{fn teradata_nativesql}{fn teradata_autocommit}");
            const row = cur.fetchone();
            if (row && row[0] === "true") {
                result = true;
            }
            else {
                result = false;
            }
            cur.close();
            return result;
        }
        finally {
            this.logger.traceLog("< leave autocommit getter");
        }
    }
    set autocommit(value) {
        this.logger.traceLog("> enter autocommit setter");
        if (typeof value !== "boolean") {
            throw new TypeError(`value unexpected type ${typeof value}`);
        }
        try {
            const s = value ? "on" : "off";
            const cur = this.cursor();
            cur.execute("{fn teradata_nativesql}{fn teradata_autocommit_" + s + "}");
            cur.close();
        }
        finally {
            this.logger.traceLog("< leave autocommit setter");
        }
    }
    cursor() {
        return new teradata_cursor_1.TeradataCursor(this, this.lib, this.ref, this.byteArray);
    }
    connect(connectParams = {}, sJSON = "{}") {
        const kwargs = { ...JSON.parse(sJSON) };
        Object.keys(kwargs).forEach((key) => {
            if (kwargs[key] === true) {
                kwargs[key] = "true";
            }
            else if (kwargs[key] === false) {
                kwargs[key] = "false";
            }
        });
        const traceback = Error().stack;
        let sFrame;
        let sFrames = [];
        let listFrames = [];
        if (traceback) {
            traceback.replace(path.sep, "/");
            sFrames = traceback.split("\n    at").slice(1);
            for (const fr of sFrames) {
                sFrame = fr.split("/").pop();
                if (sFrame) {
                    sFrame = sFrame.split(":")[0];
                    if (!listFrames.includes(sFrame)) {
                        listFrames.push(sFrame);
                    }
                }
            }
        }
        if (process.platform === "win32") {
            listFrames = listFrames.map((frame) => frame.split(" (C")[0].trim());
        }
        kwargs.client_kind = "S";
        kwargs.client_vmname = "Node.js " + process.version;
        kwargs.client_osname = os.version().split(":")[0] + " " + os.arch();
        kwargs.client_stack = listFrames.join(" ");
        kwargs.client_extra = "NODEJS=" + process.version + ";";
        const combinedConnectParams = { ...connectParams, ...kwargs };
        this.logLevel = combinedConnectParams.log ? Number(combinedConnectParams.log) : 0;
        this.logger = new teradata_logging_1.TeradataLogging(this.logLevel);
        this.logger.traceLog("> enter connect TeradataConnection");
        try {
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const inputString = this.ref.allocCString(JSON.stringify(combinedConnectParams));
            const sVersionNumberPtr = this.ref.allocCString(this.sVersionNumber);
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const jsgoCreateConnection = this.lib.jsgoCreateConnection;
            const poolHandlePtr = this.ref.alloc(this.ref.types.ulong);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            jsgoCreateConnection(this.uLog, sVersionNumberPtr, inputString, outputPtrPtr, poolHandlePtr);
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const msg = this.ref.readCString(outputString);
                jsgoFreePointer(this.uLog, outputString);
                throw new teradata_exceptions_1.OperationalError(msg);
            }
            this.poolHandle = this.ref.readUInt64LE(poolHandlePtr);
        }
        finally {
            this.logger.traceLog("< leave connect TeradataConnection");
        }
    }
    close() {
        this.logger.traceLog("> enter close TeradataConnection");
        try {
            const jsgoCloseConnection = this.lib.jsgoCloseConnection;
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            jsgoCloseConnection(this.uLog, this.poolHandle, outputPtrPtr);
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const msg = this.ref.readCString(outputString);
                jsgoFreePointer(this.uLog, outputString);
                throw new teradata_exceptions_1.OperationalError(msg);
            }
        }
        finally {
            this.logger.traceLog("< leave close TeradataConnection");
        }
    }
    cancel() {
        this.logger.traceLog("> enter cancel TeradataConnection");
        try {
            const jsgoCancelRequest = this.lib.jsgoCancelRequest;
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            jsgoCancelRequest(this.uLog, this.poolHandle, outputPtrPtr);
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const msg = this.ref.readCString(outputString);
                jsgoFreePointer(this.uLog, outputString);
                throw new teradata_exceptions_1.OperationalError(msg);
            }
        }
        finally {
            this.logger.traceLog("< leave cancel TeradataConnection");
        }
    }
    commit() {
        this.logger.traceLog("> enter commit TeradataConnection");
        try {
            this.cursor().execute("{fn teradata_commit}");
        }
        finally {
            this.logger.traceLog("< leave commit TeradataConnection");
        }
    }
    rollback() {
        this.logger.traceLog("> enter rollback TeradataConnection");
        try {
            this.cursor().execute("{fn teradata_rollback}");
        }
        finally {
            this.logger.traceLog("< leave rollback TeradataConnection");
        }
    }
}
exports.TeradataConnection = TeradataConnection;
//# sourceMappingURL=teradata-connection.js.map