const sqlite3 = require('sqlite3'),
    updateAccountTable = require('./models/accounts.js').updateTable,
    updateAccountConfigTable = require('./models/accountConfig.js').updateTable,
    config = require("../config.js");

const DB_PATH = config.db || (__dirname +"/../database.sqlite"),
    DB_VERSION = 3;

function updateMetaTable(dbObj, currentVersion, cb) {
    if (!currentVersion) {
        console.log("creating meta table");
        dbObj.run('CREATE TABLE IF NOT EXISTS `meta` ('
            +"`key` STRING PRIMARY KEY NOT NULL,"
            +"`value` STRING"
            +');', (err) => {
            if (err)
                cb(err);
            else
                dbObj.run("INSERT OR REPLACE INTO meta(key, value) VALUES('dbVersion', 1)", cb);
        });
    } else if (currentVersion < DB_VERSION) {
        dbObj.run("INSERT OR REPLACE INTO meta(key, value) VALUES('dbVersion', " +(currentVersion +1) +")", cb);
    } else {
        cb(null);
    }
};

function Database() {
    this.db = null;
};

Database.prototype.updateTables = function(cb, currentVersion) {
    if (currentVersion === undefined) {
        this.queryFirst("meta", { "key": "dbVersion" }, (err, res) => {
            if (res) {
                if (res["value"] < DB_VERSION) {
                    // db to be updated
                    this.updateAllTables([
                        updateMetaTable,
                        updateAccountTable,
                        updateAccountConfigTable
                    ], 0, res["value"], cb);
                } else {
                    // No update found
                    cb(null);
                }
            } else {
                // Something goes wrong
                cb(err);
            }
        });
    } else {
        this.updateAllTables([
            updateMetaTable,
            updateAccountTable,
            updateAccountConfigTable
        ], 0, currentVersion, cb);
    }
};

Database.prototype.initDb = function(cb) {
    var self = this;
    if (!self.db) {
	console.log("DB: " +DB_PATH);
        self.db = new sqlite3.Database(DB_PATH, sqlite3.OPEN_READWRITE, (err) => {
            if (err) {
                if (err.code === "SQLITE_CANTOPEN") {
                    self.db = new sqlite3.Database(DB_PATH, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
                        if (!err) {
                            self.updateTables(cb, 0);
                        } else {
                            cb(err);
                        }
                    });
                } else {
                    cb(err);
                }
            } else {
                self.updateTables(cb);
            }
        });
    } else {
        self.updateTables(cb);
    }
};

Database.prototype.updateAllTables = function(factories, i, currentVersion, cb) {
    var self = this;

    if (factories[i]) {
        factories[i](self.db, currentVersion, (err) => {
            if (!err)
                self.updateAllTables(factories, ++i, currentVersion, cb);
            else
                cb(err);
        });
    } else if (currentVersion +1 < DB_VERSION) {
        this.updateAllTables(factories, 0, currentVersion +1, cb);
    } else {
        cb(null);
    }
};

Database.prototype.query = function(table, fields, cb) {
    var args = [],
        query = "SELECT * FROM `" +table +"` WHERE ",
        first = true;

    for (var i in fields) {
        if (!first) {
            query += "AND ";
        }
        first = false;
        if (fields[i]) {
            query += '`' +i +'`';
            if (typeof fields[i] === "object") {
                for (var word in fields[i]) {
                    query += ' ' +word +' "' +fields[i][word] +'"';
                    break;
                }
            } else {
                query += '=?';
            }
            args.push(fields[i]);
        } else {
            query += '`' +i +'` IS NULL ';
        }
    }
    this.db.all(query, args, (err, rows) => {
        if (err) {
            console.warn(err);
            cb(err, null);
        } else {
            cb(null, rows);
        }
    });
};

Database.prototype.queryFirst = function(table, fields, cb) {
    var args = [],
        query = "SELECT * FROM `" +table +"` WHERE ",
        first = true;

    for (var i in fields) {
        if (!first) {
            query += "AND ";
        }
        first = false;
        if (fields[i]) {
            query += '`' +i +'`';
            if (typeof fields[i] === "object") {
                for (var word in fields[i]) {
                    query += ' ' +word +' "' +fields[i][word] +'"';
                    break;
                }
            } else {
                query += '=?';
            }
            args.push(fields[i]);
        } else {
            query += '`' +i +'` IS NULL ';
        }
    }
    query += " LIMIT 1";
    this.db.get(query, args, (err, row) => {
        if (err) {
            console.warn(err);
            cb(err, null);
        } else {
            cb(null, row || null);
        }
    });
};

Database.prototype.insert = function(table, data, cb) {
    var args = []
        ,keys = []
        ,templateArgs = [];
    for (var i in data) {
        keys.push('`' +i +'`');
        args.push(data[i]);
        templateArgs.push('?');
    }

    this.db.run("INSERT INTO " +table +" ("+ keys.join(",") +") VALUES(" +templateArgs.join(', ') +")", args, function(err) {
        if (err) {
            console.error(err);
            cb(null);
        }
        cb(this.lastID);
    });
};

Database.prototype.update = function(table, id, fields, cb) {
    var args = [];
    for (var i in fields)
        args.push(fields[i]);
    args.push(id);
    this.db.run("UPDATE " +table +" SET `" +Object.keys(fields).join("`=?,`") +"`=? WHERE id=?", args, (err) => {
        if (err) {
            console.error(err);
        }
        cb();
    });
};

const singleton = new Database();

module.exports.database = singleton;

