/**
 * SecureSql is the javascript interface to Sencha Web Application Client's encrypted
 * SQL database. SecureSql uses an encrypted form of SQLite 3. Please see
 * http://sqlite.org/ for more details on the subset of SQL that sqlite supports.
 *
 *
 * Basic Usage
 * ----
 *
 * Open a connection to the database. An application can have as many named databases as
 * needed. Each database is independent of the other databases in the application.
 *
 *     var db = Ext.space.SecureSql.get("test");
 *
 * Schema
 * ----
 *
 * Next you will need to define a schema in the database. Each version of the database
 * should have a version number. When the database is ready to create tables and
 * indexes, the callback function will be called with a
 * `Ext.space.securesql.Transaction`. Add queries to create the tables and indexes
 * needed for this database.
 *
 *         db.createSechema(1, function(tx){
 *             tx.query("CREATE TABLE IF NOT EXISTS test (id int, name text,  PRIMARY KEY (id))");
 *             tx.query("CREATE TABLE IF NOT EXISTS person (id int, firstName text,lastName text, email text, PRIMARY KEY (id))");
 *         });
 *
 * Do not run the transaction from within the callback. It will be executed
 * automatically after the callback function returns.
 *
 * The callback on createSchema is only called if a database of that version does not
 * already exist.  If the schema of the application's database changes overtime then the
 * data will need to be migrated.  See the Migration section below.
 *
 * See `Ext.space.securesql.Database.createSchema` for more details.
 *
 * Inserting Data
 * ----
 *
 * SecureSql provides two convenience methods for inserting data into the database. The
 * application can pass either an array of data or a javascript object.
 *
 * `Ext.space.securesql.Database.insert` will insert record into a table using a single
 * transaction.
 * `Ext.space.securesql.Database.insertMany` will insert multiple records into a table
 * single transaction.
 *
 *
 *
 * Queries
 * ----
 *
 * `Ext.space.securesql.Database.query` will execute a query against the database in a
 * single transaction.
 *
 *
 * Transactions
 * ----
 *
 * SecureSql supports executing multiple inserts and queries in a single logical
 * transaction
 *
 *         var tx = Ext.space.securesql.Database.createTransaction()
 *
 *         tx.query()
 *         tx.insert()
 *         tx.query()
 *         tx.query()
 *         tx.execute()
 *
 * see `Ext.space.securesql.Database.createTransaction`
 *
 *
 * Data Migration
 * ----
 * SecureSql provides methods to migrate a schema from one version to the next. Data
 * migrations can become complex so we recommend modification the database as little as
 * possible after the initial creation.
 *
 * If the application loads and createSechema attempts to create version 3 of the schema
 *
 *         db.createSchema(3, function(tx){
 *             //....
 *         });
 *
 * 1) if this client does not have a database then createSchema executes and the schema
 *    version is set to 3
 * 2) if the client already has schema version 3 then nothing happens, and queries will
 *    be executed.
 * 3) if the schema version is less that 3 then each of the registered migration
 *    callbacks are executed until the version is equal to the version defined in
 *    createSchema
 *
 * It is the responsibility of the developer to ensure that the table mutation
 * operations in the migration callbacks will correctly update the database to the
 * current schema.
 *
 * IF the developer has defined version 3 of createSchema then they should define two
 * version migrations.
 *
 * This migration will upgrade version 1 of the database to version 2.
 *
 *         db.migrate(2, function(tx){
 *             //....
 *         });
 *
 * This migration will upgrade version 2 to version 3:
 *
 *         db.migrate(3, function(tx){
 *             //....
 *         });
 *
 * Database List
 * ----
 *
 * If you need to programmatically determine what databases are available, call the
 * `listDatabases` API:
 *
 *     Ext.space.SecureSql.listDatabases().then(function(databaseList) {
 *         databaseList.forEach(function(db) {
 *             // db.name, db.version, etc...
 *         });
 *     });
 */
Ext.define("Ext.space.SecureSql", {
    singleton: true,
 
    /*
     * @private
     */
    _openDBs: null,
 
    /*
     * @private
     */
    constructor: function() {
        this._openDBs = {};
    },
 
    /**
     * List the available databases.
     *
     * @return {Ext.space.Promise} Promise which receives an array of database info objects
     *                       like {name, displayName}
     */
    listDatabases: function() {
        var result = new Ext.space.Promise();
        Ext.space.Communicator.send({
            command: "Sqlite#listDatabases",
            callbacks: {
                onSuccess: function(databaseList) {
                    result.fulfill(databaseList.map(function(db) {
                        return {
                            name: db.name,
                            displayName: db.displayName
                        };
                    }));
                },
                onError: function(error) {
                    result.reject(error);
                }
            }
        });
        return result;
    },
 
    /**
     *
     * Open a connection to a database. A database will automatically be created if it does not already exist.
     *
     * @param {String} name The name of the database to open.
     * @return {Ext.space.securesql.Database} the secure collection.
     */
    get: function(name) {
        function release(database) {
            Ext.space.Logger.debug("releasing DB: " + name);
            if (this._openDBs[database.name]) {
                delete this._openDBs[database.name];
            }
        }
 
        var db = this._openDBs[name];
        Ext.space.Logger.info("Get database", name, db);
        if (!db) {
            db = new Ext.space.securesql.Database({name: name, cleanup: release.bind(this)});
            this._openDBs[name] = db;
        }
        return db;
    },
 
    /**
     * Permanently delete this database. This operation cannot be undone. All data in
     * this database will be lost.
     *
     * @param {String} name The name of the database to delete.
     * @return {Ext.space.Promise} a promise that will resolve when the database has been removed.
     */
    drop: function(name) {
        var db = this._openDBs[name];
        return db ? db.drop() : this.get(name).drop();
    },
 
    /**
     * Opens the database with the given name; if it does not exist, it will be created.
     *
     * @private
     * @param {Object} options Database options
     * @param {String} options.name Database name
     * @param {String} options.displayName (optional) Database name for display purposes
     * @return {Ext.space.Promise} The promise that will resolve when the database is opened and returned.
     */
    _open: function(options) {
        var promise = new Ext.space.Promise();
 
        if (options.name) {
            Ext.space.Communicator.send({
                command: "Sqlite#openDatabase",
                name: options.name,
                displayName: options.displayName || options.name,
                callbacks: {
                    success: function(dbSpec) {
                        promise.fulfill(dbSpec);
                    },
                    failure: function(e) {
                        promise.reject(e);
                    }
                }
            });
        } else {
            promise.reject("Cannot open database; no name specified.");
        }
 
        return promise;
    }
});