"use strict";
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 (g && (g = 0, op[0] && (_ = 0)), _) 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 __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqljsAdapter = void 0;
var rtree_sql_js_1 = __importDefault(require("rtree-sql.js"));
/**
 * This adapter uses sql.js to execute queries against the GeoPackage database
 * @module db/sqljsAdapter
 * @see {@link http://kripken.github.io/sql.js/documentation/|sqljs}
 */
var SqljsAdapter = /** @class */ (function () {
    /**
     * @param  {string|Buffer|Uint8Array} [filePath] string path to an existing file or a path to where a new file will be created or a url from which to download a GeoPackage or a Uint8Array containing the contents of the file, if undefined, an in memory database is created
     */
    function SqljsAdapter(filePath) {
        this.filePath = filePath;
    }
    SqljsAdapter.setSqljsWasmLocateFile = function (locateFile) {
        SqljsAdapter.sqljsWasmLocateFile = locateFile;
    };
    /**
     * Returns a Promise which, when resolved, returns a DBAdapter which has connected to the GeoPackage database file
     */
    SqljsAdapter.prototype.initialize = function () {
        var _this = this;
        return new Promise(function (resolve, reject) {
            new Promise(function (resolve) {
                if (SqljsAdapter.SQL == null) {
                    (0, rtree_sql_js_1.default)({
                        locateFile: SqljsAdapter.sqljsWasmLocateFile,
                    }).then(function (SQL) {
                        SqljsAdapter.SQL = SQL;
                        resolve(SQL);
                    }).catch(function (e) {
                        reject(e);
                    });
                }
                else {
                    resolve(SqljsAdapter.SQL);
                }
            }).then(function (SQL) {
                if (_this.filePath && typeof _this.filePath === 'string') {
                    if (typeof process !== 'undefined' && process.version) {
                        // eslint-disable-next-line @typescript-eslint/no-var-requires
                        var fs = require('fs');
                        if (_this.filePath.indexOf('http') === 0) {
                            // eslint-disable-next-line @typescript-eslint/no-var-requires
                            var http = require('http');
                            http
                                .get(_this.filePath, function (response) {
                                if (response.statusCode !== 200) {
                                    return reject(new Error('Unable to reach url: ' + _this.filePath));
                                }
                                var body = [];
                                response.on('data', function (chunk) { return body.push(chunk); });
                                response.on('end', function () {
                                    var t = new Uint8Array(Buffer.concat(body));
                                    _this.db = new SQL.Database(t);
                                    resolve(_this);
                                });
                            })
                                .on('error', function (e) {
                                return reject(e);
                            });
                        }
                        else {
                            try {
                                fs.statSync(_this.filePath);
                            }
                            catch (e) {
                                _this.db = new SQL.Database();
                                // var adapter = new SqljsAdapter(db);
                                return resolve(_this);
                            }
                            var filebuffer = fs.readFileSync(_this.filePath);
                            var t = new Uint8Array(filebuffer);
                            _this.db = new SQL.Database(t);
                            return resolve(_this);
                        }
                    }
                    else {
                        // eslint-disable-next-line no-undef
                        var xhr_1 = new XMLHttpRequest();
                        xhr_1.open('GET', _this.filePath, true);
                        xhr_1.responseType = 'arraybuffer';
                        xhr_1.onload = function () {
                            if (xhr_1.status !== 200) {
                                return reject(new Error('Unable to reach url: ' + _this.filePath));
                            }
                            var uInt8Array = new Uint8Array(xhr_1.response);
                            _this.db = new SQL.Database(uInt8Array);
                            return resolve(_this);
                        };
                        xhr_1.onerror = function () {
                            return reject(new Error('Error reaching url: ' + _this.filePath));
                        };
                        xhr_1.send();
                    }
                }
                else if (_this.filePath) {
                    var byteArray = _this.filePath;
                    _this.db = new SQL.Database(byteArray);
                    return resolve(_this);
                }
                else {
                    _this.db = new SQL.Database();
                    return resolve(_this);
                }
            }).catch(function (e) {
                reject(e);
            });
        });
    };
    /**
     * Closes the connection to the GeoPackage
     */
    SqljsAdapter.prototype.close = function () {
        this.db.close();
    };
    /**
     * Get the connection to the database file
     * @return {any}
     */
    SqljsAdapter.prototype.getDBConnection = function () {
        return this.db;
    };
    /**
     * Returns a Uint8Array containing the contents of the database as a file
     */
    SqljsAdapter.prototype.export = function () {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                return [2 /*return*/, this.db.export()];
            });
        });
    };
    /**
     * Registers the given function so that it can be used by SQL statements
     * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Database.html#create_function-dynamic|sqljs create_function}
     * @param  {string} name               name of function to register
     * @param  {Function} functionDefinition function to register
     * @return {module:db/sqljsAdapter~Adapter} this
     */
    SqljsAdapter.prototype.registerFunction = function (name, functionDefinition) {
        this.db.create_function(name, functionDefinition);
        return this;
    };
    /**
     * Gets one row of results from the statement
     * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#get-dynamic|sqljs get}
     * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#getAsObject-dynamic|sqljs getAsObject}
     * @param  {String} sql    statement to run
     * @param  {Array|Object} [params] substitution parameters
     * @return {Object}
     */
    SqljsAdapter.prototype.get = function (sql, params) {
        params = params || [];
        var statement = this.db.prepare(sql);
        statement.bind(params);
        var hasResult = statement.step();
        var row;
        if (hasResult) {
            row = statement.getAsObject();
        }
        statement.free();
        return row;
    };
    /**
     * Determines if a tableName exists in the database
     * @param {String} tableName
     * @returns {Boolean}
     */
    SqljsAdapter.prototype.isTableExists = function (tableName) {
        var statement = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name");
        statement.bind([tableName]);
        var hasResult = statement.step();
        var row;
        if (hasResult) {
            row = statement.getAsObject();
        }
        statement.free();
        return !!row;
    };
    /**
     * Gets all results from the statement in an array
     * @param  {String} sql    statement to run
     * @param  {Array|Object} [params] bind parameters
     * @return {Object[]}
     */
    SqljsAdapter.prototype.all = function (sql, params) {
        var e_1, _a;
        var rows = [];
        var iterator = this.each(sql, params);
        try {
            for (var iterator_1 = __values(iterator), iterator_1_1 = iterator_1.next(); !iterator_1_1.done; iterator_1_1 = iterator_1.next()) {
                var row = iterator_1_1.value;
                rows.push(row);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (iterator_1_1 && !iterator_1_1.done && (_a = iterator_1.return)) _a.call(iterator_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return rows;
    };
    /**
     * Returns an Iterable with results from the query
     * @param  {string} sql    statement to run
     * @param  {Object|Array} params bind parameters
     * @return {IterableIterator<Object>}
     */
    SqljsAdapter.prototype.each = function (sql, params) {
        var _a;
        var statement = this.db.prepare(sql);
        statement.bind(params);
        return _a = {},
            _a[Symbol.iterator] = function () {
                return this;
            },
            _a.next = function () {
                if (statement.step()) {
                    return {
                        value: statement.getAsObject(),
                        done: false,
                    };
                }
                else {
                    statement.free();
                    return {
                        value: undefined,
                        done: true,
                    };
                }
            },
            _a;
    };
    /**
     * Runs the statement specified, returning information about what changed
     * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#run-dynamic|sqljs run}
     * @param  {string} sql    statement to run
     * @param  {Object|Array} [params] bind parameters
     * @return {Object} object containing a changes property indicating the number of rows changed and a lastInsertROWID indicating the last inserted row
     */
    SqljsAdapter.prototype.run = function (sql, params) {
        if (params && !(params instanceof Array)) {
            for (var key in params) {
                params['$' + key] = params[key];
            }
        }
        this.db.run(sql, params);
        var lastId = this.db.exec('select last_insert_rowid();');
        var lastInsertedId;
        if (lastId) {
            lastInsertedId = lastId[0].values[0][0];
        }
        return {
            lastInsertRowid: lastInsertedId,
            changes: this.db.getRowsModified(),
        };
    };
    /**
     * Runs the specified insert statement and returns the last inserted id or undefined if no insert happened
     * @param  {String} sql    statement to run
     * @param  {Object|Array} [params] bind parameters
     * @return {Number} last inserted row id
     */
    SqljsAdapter.prototype.insert = function (sql, params) {
        if (params && !(params instanceof Array)) {
            for (var key in params) {
                params['$' + key] = params[key];
            }
        }
        var statement = this.db.prepare(sql, params);
        statement.step();
        statement.free();
        var lastId = this.db.exec('select last_insert_rowid();');
        if (lastId) {
            return lastId[0].values[0][0];
        }
        else {
            return;
        }
    };
    /**
     * Prepares a SQL statement
     * @param sql
     */
    SqljsAdapter.prototype.prepareStatement = function (sql) {
        return this.db.prepare(sql);
    };
    /**
     * Runs an insert statement with the parameters provided
     * @param  {any} statement  statement to run
     * @param  {Object|Array} [params] bind parameters
     * @return {Number} last inserted row id
     */
    SqljsAdapter.prototype.bindAndInsert = function (statement, params) {
        if (params && !(params instanceof Array)) {
            for (var key in params) {
                params['$' + key] = params[key];
            }
        }
        return statement.run(params).lastInsertRowid;
    };
    /**
     * Closes a prepared statement
     * @param statement
     */
    SqljsAdapter.prototype.closeStatement = function (statement) {
        statement.free();
    };
    /**
     * Runs the specified delete statement and returns the number of deleted rows
     * @param  {String} sql    statement to run
     * @param  {Object|Array} [params] bind parameters
     * @return {Number} deleted rows
     */
    SqljsAdapter.prototype.delete = function (sql, params) {
        var rowsModified;
        var statement = this.db.prepare(sql, params);
        statement.step();
        rowsModified = this.db.getRowsModified();
        statement.free();
        return rowsModified;
    };
    /**
     * Drops the table
     * @param  {String} table table name
     * @return {Boolean} indicates if the table was dropped
     */
    SqljsAdapter.prototype.dropTable = function (table) {
        var response = this.db.exec('DROP TABLE IF EXISTS "' + table + '"');
        this.db.exec('VACUUM');
        return !!response;
    };
    /**
     * Counts rows that match the query
     * @param  {String} tableName table name from which to count
     * @param  {String} [where]     where clause
     * @param  {Object|Array} [whereArgs] where args
     * @return {Number} count
     */
    SqljsAdapter.prototype.count = function (tableName, where, whereArgs) {
        var sql = 'SELECT COUNT(*) as count FROM "' + tableName + '"';
        if (where) {
            sql += ' where ' + where;
        }
        return this.get(sql, whereArgs).count;
    };
    SqljsAdapter.prototype.transaction = function (func) {
        this.db.exec('BEGIN TRANSACTION');
        try {
            func();
            this.db.exec('COMMIT TRANSACTION');
        }
        catch (e) {
            this.db.exec('ROLLBACK TRANSACTION');
            throw e;
        }
    };
    SqljsAdapter.sqljsWasmLocateFile = function (filename) { return filename; };
    return SqljsAdapter;
}());
exports.SqljsAdapter = SqljsAdapter;
//# sourceMappingURL=sqljsAdapter.js.map