# from spg import CONFIG_DIR, TIMEOUT

import os
import os.path
import random
import sqlite3 as sql

from ..simulation import ParameterEnsemble


class SPGMasterDB:
    """
    A class that abstract the interaction with a central database where all the registered simulations lay
    """

    def __init__(self, connection=None, EnsembleConstructor=ParameterEnsemble):
        if not connection:
            if not os.path.exists(CONFIG_DIR):
                os.makedirs(CONFIG_DIR)
            self.connection = sql.connect(
                "%s/spg-pool.sqlite" % CONFIG_DIR, timeout=TIMEOUT
            )
        else:
            self.connection = connection
        self.test_run = False

        self.EnsembleConstructor = EnsembleConstructor
        self.cursor = self.connection.cursor()
        self.__init_db()
        self.result_dbs = {}
        self.update_list_ensemble_dbs()

        self.normalising = 0.0
        self.active_dbs = []

    def __init_db(self):

        #:::~ status can be either
        #:::~    'S': stopped
        #:::~    'R': running
        #:::~    'F': finished
        self.cursor.execute(
            "CREATE TABLE IF NOT EXISTS dbs "
            "(id INTEGER PRIMARY KEY, full_name CHAR(256), "
            "path CHAR(256), base_name CHAR(256), status CHAR(1), total_values_set INTEGER, "
            " total_combinations INTEGER, done_combinations INTEGER, running_combinations INTEGER, error_combinations INTEGER, "
            " weight FLOAT, queue CHAR(64))"
        )

        #:::~ status can be either
        #:::~    'S': stopped
        #:::~    'R': running
        self.cursor.execute(
            "CREATE TABLE IF NOT EXISTS queues "
            "(id INTEGER PRIMARY KEY, name CHAR(64), max_jobs INTEGER, status CHAR(1))"
        )

        self.cursor.execute(
            "CREATE TABLE IF NOT EXISTS running "
            "(id INTEGER PRIMARY KEY, job_id CHAR(64), dbs_id INTEGER, params_id INTEGER)"
        )

        #        self.cursor.execute("CREATE TABLE IF NOT EXISTS infiles "
        #                          "(id INTEGER PRIMARY KEY, last INTEGER)")

        self.cursor.execute(
            "INSERT INTO queues (name, max_jobs, status) VALUES ('default',1,'R') "
        )

        self.connection.commit()

    def query_master_db(self, query, *args):
        ret = [i for i in self.cursor.execute(query, args)]
        self.connection.commit()
        return ret

    def query_master_fetchone(self, query, *args):
        ret = self.cursor.execute(query, args).fetchone()
        self.connection.commit()
        return ret

    def update_list_ensemble_dbs(
        self, status=None
    ):  # These are the dbs that are registered and running
        self.result_dbs = {}
        if status:
            res = self.cursor.execute(
                "SELECT id, full_name, weight, queue, status FROM dbs  WHERE status = ?",
                status,
            )
        else:
            res = self.cursor.execute(
                "SELECT id, full_name, weight, queue, status FROM dbs "
            )

        self.normalising = 0.0
        self.active_dbs = []
        for (id, full_name, weight, queue, status) in res:
            # print (id, full_name, weight, queue, status)
            self.result_dbs[full_name] = {}
            self.result_dbs[full_name]["id"] = id
            self.result_dbs[full_name]["queue"] = queue
            self.result_dbs[full_name]["status"] = status
            self.result_dbs[full_name]["weight"] = weight
            if status == "R":
                self.normalising += weight
                self.active_dbs.append(full_name)
        #     print full_name, self.result_dbs[full_name]

    def write_ensemble_to_master(self, param_db):

        db_status = param_db.get_updated_status()
        res = self.cursor.execute(
            "SELECT * FROM dbs WHERE full_name = ?", (param_db.full_name,)
        ).fetchone()

        if res is None:
            self.cursor.execute(
                "INSERT INTO dbs (full_name, path, base_name, total_values_set, total_combinations, done_combinations, running_combinations, error_combinations, status, weight , queue ) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
                (
                    param_db.full_name,
                    param_db.path,
                    param_db.base_name,
                    db_status["value_set_with_rep"],
                    db_status["value_set"],
                    db_status["process_done"],
                    db_status["process_running"],
                    db_status["process_error"],
                    param_db.status,
                    param_db.weight,
                    param_db.queue,
                ),
            )
            res = self.cursor.execute(
                "SELECT * FROM dbs WHERE full_name = ?", (param_db.full_name,)
            ).fetchone()
            param_db.id = res[0]
        else:
            param_db.id = res[0]
            self.cursor.execute(
                "UPDATE dbs SET total_values_set = ? , total_combinations = ?, done_combinations = ?, running_combinations = ?, error_combinations = ?, status = ? , weight = ?, queue = ? WHERE full_name = ? ",
                (
                    db_status["value_set_with_rep"],
                    db_status["value_set"],
                    db_status["process_done"],
                    db_status["process_running"],
                    db_status["process_error"],
                    param_db.status,
                    param_db.weight,
                    param_db.queue,
                    param_db.full_name,
                ),
            )

        if db_status["process_error"] == 1:
            self.cursor.execute(
                "UPDATE dbs SET status = ? WHERE full_name = ? ",
                ("D", param_db.full_name),
            )

        self.connection.commit()

        return db_status

    def synchronise_master_db(self):
        for i in self.result_dbs:
            try:
                self.write_ensemble_to_master(self.result_dbs[i])
            except:
                pass

    def pick_ensemble(self):
        rnd = self.normalising * random.random()
        curr_db = self.active_dbs[0]
        curr_id = 0
        ac = 0.0
        while rnd > ac:
            curr_db = self.active_dbs[curr_id]
            ac += self.result_dbs[curr_db]["weight"]
            curr_id += 1

        return self.EnsembleConstructor(curr_db, init_db=True)
