"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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HotDBMySQL = void 0;
const mysql = __importStar(require("mysql2"));
const HotDB_1 = require("../HotDB");
const HotIO_1 = require("../HotIO");
/**
 * The MySQL database connection.
 */
class HotDBMySQL extends HotDB_1.HotDB {
    constructor(db = null, type = HotDB_1.HotDBType.MySQL, schema = null) {
        super(db, type, schema);
    }
    /**
     * Connect to the database.
     */
    connect(connectionInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            return (new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
                if (process.env["DATABASE_CONNECTIONS_LIMIT"] != null)
                    this.connectionLimit = parseInt(process.env["DATABASE_CONNECTIONS_LIMIT"]);
                let multipleStatements = false;
                if (connectionInfo.multipleStatements != null)
                    multipleStatements = connectionInfo.multipleStatements;
                let connectionObj = {
                    host: connectionInfo.server,
                    user: connectionInfo.username,
                    password: connectionInfo.password,
                    port: connectionInfo.port,
                    database: connectionInfo.database,
                    waitForConnections: true,
                    connectionLimit: this.connectionLimit,
                    multipleStatements: multipleStatements
                };
                if (connectionInfo.ssl != null) {
                    connectionObj.ssl = {
                        "rejectUnauthorized": true
                    };
                    if (connectionInfo.ssl.rejectUnauthorized != null)
                        connectionObj.ssl.rejectUnauthorized = connectionInfo.ssl.rejectUnauthorized;
                    if (connectionInfo.ssl.ca != null)
                        connectionObj.ssl.ca = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.ca);
                    if (connectionInfo.ssl.cert != null)
                        connectionObj.ssl.cert = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.cert);
                    if (connectionInfo.ssl.key != null)
                        connectionObj.ssl.key = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.key);
                }
                if (connectionInfo.connectionObjectOverride != null)
                    connectionObj = connectionInfo.connectionObjectOverride;
                this.connectionStatus = HotDB_1.ConnectionStatus.Connecting;
                this.db = mysql.createPool(connectionObj);
                this.connectionStatus = HotDB_1.ConnectionStatus.Connected;
                resolve([true]);
            })));
        });
    }
    /**
     * Checks to see if this has a database connection.
     */
    dbCheck() {
        if (this.db == null)
            throw new Error("Not connected to a database!");
    }
    /**
     * Synchronize all tables.
     */
    /*async syncAllTables (migrationsDirectory: string, throwErrors: boolean = true): Promise<boolean>
    {
        this.dbCheck ();

        const migrationsPath: string = ppath.normalize (migrationsDirectory);

        if (await HotIO.exists (migrationsPath) === false)
            throw new Error (`Migrations directory ${migrationsDirectory} does not exist.`);

        let files: string[] = await HotIO.listFiles (migrationsPath);
        let versions: {
                version: number;
                fileContent: string;
            }[] = [];

        for (let iIdx = 0; iIdx < files.length; iIdx++)
        {
            let file: string = ppath.normalize (files[iIdx]);
            let fileContent: string = await HotIO.readTextFile (file);

            let context = { HotDBMigration: HotDBMigration, db: this, migrationPath: file };
            vm.createContext (context);
            let migrationVersion: any = vm.runInContext (
`${fileContent}
let migration = new Migration ();
return (migration.version);
`, context);

            versions.push ({ version: migrationVersion, fileContent: fileContent });
        }

        versions.sort ();

        await this.syncMigrationsTableTracker ();

        let madeModifications: boolean = false;

        for (let tableName in this.schema.tables)
        {
            let tempResult: boolean = await this.syncTable (tableName, throwErrors);

            if (tempResult === true)
                madeModifications = true;
        }

        return (madeModifications);
    }*/
    /**
     * Synchronize a table. This will create/modify the table based on whether it
     * exists, and if there's been any changes to any fields.
     */
    /*async syncTable (tableName: string, throwErrors: boolean = true): Promise<boolean>
    {
        this.dbCheck ();

        let tableExists: boolean =  await this.tableCheck (tableName);
        let madeModifications: boolean = false;

        if (tableExists === false)
        {
            let structure: string[] = await this.schema.generateTableStructure (tableName);
            // This should always be structure[0]. There should only be 1 string to process
            // for a newly created table.
            let tempResults = await this.query (structure[0], []);

            if (tempResults.error == null)
                madeModifications = true;
            else
            {
                if (throwErrors === true)
                    throw new Error (`Error while creating table ${tableName}: ${tempResults.error.message}`);
            }
        }
        else
        {*/
    /*let structure: string[] = await this.schema.generateTableStructure (
                                tableName, HotDBGenerationType.Modify, this);
    let tempResults = await this.multiQuery (structure);

    for (let iIdx = 0; iIdx < tempResults.length; iIdx++)
    {
        let results = tempResults[iIdx];

        if (results.error != null)
        {
            madeModifications = false;

            if (throwErrors === true)
                throw new Error (`Error while creating table ${tableName}: ${results.error.message}`);
        }
        else
            madeModifications = true;
    }*/
    /*}

    return (madeModifications);
}*/
    /**
     * Sync the migrations table tracker. This keeps track of all database table migrations.
     * If the "migrations" table is missing, it will be created.
     */
    /*async syncMigrationsTableTracker (): Promise<void>
    {
        if (await this.tableCheck ("migrations") === false)
        {
            await new Promise<void> ((resolve, reject) =>
                {
                    this.db.getConnection (async (err: NodeJS.ErrnoException, connection: mysql.PoolConnection) =>
                        {
                            if (err)
                            {
                                reject (err);

                                return;
                            }
    
                            /// @todo Verify that this actually created.
                            await this.db.query (`create table if not exists migrations (
                                    version datetime not null
                                );`).on ("end", () =>
                                    {
                                        connection.release ();
                                    });

                            resolve ();
                        });
                });
        }
    }*/
    /**
     * Checks if the table exists.
     */
    tableCheck(tableName) {
        return __awaiter(this, void 0, void 0, function* () {
            this.dbCheck();
            let tableExists = yield new Promise((resolve, reject) => {
                this.db.getConnection((err, connection) => __awaiter(this, void 0, void 0, function* () {
                    if (err) {
                        reject(err);
                        return;
                    }
                    this.db.query("SELECT table_name FROM information_schema.tables where table_name = ?;", [tableName], (err, results, fields) => {
                        let result = false;
                        if (results != null) {
                            if (results.length > 0)
                                result = true;
                        }
                        resolve(result);
                    }).on("end", () => {
                        connection.release();
                    });
                }));
            });
            return (tableExists);
        });
    }
    /**
     * The query to make.
     */
    query(queryString_1) {
        return __awaiter(this, arguments, void 0, function* (queryString, values = []) {
            this.dbCheck();
            let dbresults = yield new Promise((resolve, reject) => {
                this.db.getConnection((err, connection) => {
                    if (err) {
                        reject(err);
                        return;
                    }
                    this.db.query(queryString, values, (err, results, fields) => {
                        resolve({ error: err, results: results, fields: fields });
                    }).on("end", () => {
                        connection.release();
                    });
                });
            });
            return (dbresults);
        });
    }
    /**
     * Make a single query. If there are no results, null will be in MySQLResults.results
     */
    queryOne(queryString_1) {
        return __awaiter(this, arguments, void 0, function* (queryString, values = []) {
            this.dbCheck();
            let dbresults = yield new Promise((resolve, reject) => {
                this.db.getConnection((err, connection) => {
                    if (err) {
                        reject(err);
                        return;
                    }
                    this.db.query(queryString, values, (err, results, fields) => {
                        let tempResults = null;
                        if (results != null) {
                            if (results.length > 0)
                                tempResults = results[0];
                        }
                        resolve({ error: err, results: tempResults, fields: fields });
                    }).on("end", () => {
                        connection.release();
                    });
                });
            });
            return (dbresults);
        });
    }
    /**
     * Make multiple queries. **Warning! This can be a security vulnerability
     * if misused! Ideally this should only be used when making changes to tables!
     * Additionally, this could overwhelm the server and each command sent is not
     * guaranteed to be done in order.**
     *
     * @deprecated
     */
    multiQuery(queryStrings) {
        return __awaiter(this, void 0, void 0, function* () {
            this.dbCheck();
            let alldbresults = [];
            yield new Promise((resolve, reject) => {
                this.db.getConnection((err, connection) => __awaiter(this, void 0, void 0, function* () {
                    let promises = [];
                    if (err) {
                        reject(err);
                        return;
                    }
                    for (let iIdx = 0; iIdx < queryStrings.length; iIdx++) {
                        /// @fixme This could overwhelm the server, and each query most likely will 
                        /// not be done in a deterministic order. Consider adding a 5-10ms delay between
                        /// each query.
                        promises.push(new Promise((resolve2, reject2) => {
                            let queryString = queryStrings[iIdx];
                            let queryValues = [];
                            if (typeof (queryString) !== "string") {
                                queryValues = queryString.values;
                                queryString = queryString.query;
                            }
                            this.db.query(queryString, queryValues, (err, results, fields) => {
                                resolve2({ error: err, results: results, fields: fields });
                            }).on("end", () => {
                            });
                        }));
                    }
                    alldbresults = yield Promise.all(promises);
                    connection.release();
                    resolve();
                }));
            });
            return (alldbresults);
        });
    }
    /**
     * Disconnect from the server.
     */
    disconnect() {
        return __awaiter(this, void 0, void 0, function* () {
            return (new Promise((resolve, reject) => {
                this.dbCheck();
                this.db.end((err) => {
                    if (err) {
                        reject(err);
                        return;
                    }
                    this.connectionStatus = HotDB_1.ConnectionStatus.Disconnected;
                    resolve();
                });
            }));
        });
    }
}
exports.HotDBMySQL = HotDBMySQL;
//# sourceMappingURL=HotDBMySQL.js.map