# This file was generated by mettle.genes.db.GenPy3 [ver 2.1] on Mon Feb 15 23:21:02 2021
#  Target Database = postgresql
#
import asyncio
import copy
import datetime
import uuid
import time
import mettle.lib
import mettle.io
import mettle.db

from bs_loco.db.tables.config import tConfig
from bs_loco.db.tables.config_key import tConfigKey

from .config_lock_one import dConfigLockOne
from .config_select_one import dConfigSelectOne
from .config_update import dConfigUpdate
from .config_insert import dConfigInsert

class dConfig:

    def __init__(self, dbcon: mettle.db.IAConnect):
        """
        Constructor.

        :param dbcon: Mettle database connection object.
        """
        self.rec    = tConfig()
        self._dbcon = dbcon

    def __enter__(self):
        """
        DAO enter.
        """
        return self

    def __exit__(self, type, value, traceback):
        """
        DAO exit.
        """
        pass

    async def lock_one(self, _lock4update: mettle.db.DBLock, _expected: bool = True) -> bool:
        """
        Lock and select the record.

        :param _lock4update: The db lock object to use.
        :param _expected: If set to false, will not raise if record is not found.
        :return: True if selected, False if not found and _expected is set to False
        """
        _key = tConfigKey()

        self.rec._write_key(_key)

        return await self.lock_one_by_key(_key, _lock4update, _expected)

    async def lock_one_with(self, _rec: tConfig, _lock4update: mettle.db.DBLock, _expected: bool = True) -> bool:
        """
        Lock and select the record using a record.

        :param _rec: The table rec to select with and into.
        :param _lock4update: The db lock object to use.
        :param _expected: If set to false, will not raise if record is not found.
        :return: True if selected, False if not found and _expected is set to False
        """
        _key = tConfigKey()
        _rec._write_key(_key)

        await self.lock_one_by_key(_key, _lock4update, _expected)
        _rec._copy_from(self.rec)
        return True

    async def lock_one_deft(self,
                            id: str,
                            _lock4update: mettle.db.DBLock,
                            _expected: bool = True) -> bool:
        """
        Attempt to lock and select the record by it's primary key columns.

        :param id: str
        :param _lock4update:  The db lock object to use. 
        :param _expected: bool
        """
        _key = tConfigKey()

        _key.id = id

        return await self.lock_one_by_key(_key, _lock4update, _expected)

    async def lock_one_by_key(self, _key: tConfigKey, _lock4update: mettle.db.DBLock, _expected: bool = True) -> bool:
        """
        Attempt to lock and select the record with it's primary key.

        :param _key: Primary key of the table record.
        :param _lock4update: The db lock object to use.
        :param _expected: If set to false, will not raise if record is not found.
        :return: True if selected, False if not found and _expected is set to False
        """
        with dConfigLockOne(self._dbcon) as _qry:
            _dex = _lock4update.retrys - 1 if _lock4update.retrys > 0 else 0

            while _dex >= 0:
                try:
                    await _qry.exec(_key)
                    if not await _qry.fetch():
                        if not _expected:
                            return False

                        raise mettle.lib.xMettle("LockOne failed to return a record (dConfig)")

                    break
                except mettle.lib.xMettle as _x:
                    if _x.get_error_code() != mettle.lib.xMettle.eCode.DBLockNoWaitFailed:
                        raise _x

                    if _dex == 0:
                        raise mettle.lib.xMettle("Could not lock Config record after %d attempt(s) at %d mili second intervals!" % (
_lock4update.retrys, _lock4update.mili_seconds), errCode = mettle.lib.xMettle.eCode.DBLockNoWaitFailed)

                    if _lock4update.mili_seconds > 0:
                        await asyncio.sleep(_lock4update.mili_seconds * 0.001)
                _dex -= 1

            self.rec = _qry.orec

            return True

    async def try_select_one(self) -> bool:
        """
        Attempt to select the record.

        :return: True if the record was found.
        """
        _key = tConfigKey()

        self.rec._write_key(_key)

        return await self.try_select_one_by_key(_key)

    async def try_select_one_with(self, _rec: tConfig) -> bool:
        """
        Attempt to select the record using a table rec.

        :param _rec: The table rec to select with and into.
        :return: True if the row as selectd.
        """
        _key = tConfigKey()
        _rec._write_key(_key)

        if not await self.try_select_one_by_key(_key):
            return False

        _rec._copy_from(self.rec)

        return True

    async def try_select_one_deft(self,
                                  id: str) -> bool:
        """
        Attempt to select the record by it's primary key columns.

        :param id: str
        :return: True if the row as selectd.
        """
        _key = tConfigKey()

        _key.id = id

        return await self.try_select_one_by_key(_key)

    async def try_select_one_by_key(self, _key: tConfigKey) -> bool:
        """
        Attempt to select the record with it's primary key.

        :param _key: Primary key of the table record.
        :returns: True if row was selected.
        """
        with dConfigSelectOne(self._dbcon) as _qry:
            await _qry.exec(_key)
            if not await _qry.fetch():
                return False

            self.rec = _qry.orec

        return True

    async def select_one(self) -> "dConfig":
        """
        Selects the record, raises and error if it does not exist.

        :return: Self for convenience.
        """
        if not await self.try_select_one():
            raise mettle.lib.xMettle("SelectOne failed to return a record (dConfig)")

        return self

    async def select_one_with(self, _rec: tConfig) -> "dConfig":
        """
        Attempt to select the record with the table record.

        :param _rec: Table record.
        :returns: Self for convenience.
        """
        if not await self.try_select_one_with(_rec):
            raise mettle.lib.xMettle("SelectOne failed to return a record (dConfig)")

        return self

    async def select_one_deft(self,
                              id: str) -> "dConfig":
        """
        Selects the record by its primary key columns, raises and error if it does not exist.

        :param id: str
        """
        _key = tConfigKey()

        _key.id = id

        if not await self.try_select_one_by_key(_key):
            raise mettle.lib.xMettle("SelectOne failed to return a record (dConfig)")

        return self

    async def select_one_by_key(self, _key: tConfigKey) -> "dConfig":
        """
        Attempt to select the record with it's primary key.

        :param _key: Primary key of the table record.
        :return: Self for convenience.
        """
        if not await self.try_select_one_by_key(_key):
            raise mettle.lib.xMettle("SelectOne failed to return a record (dConfig)")

        return self

    async def update_deft(self,
                          id: str,
                          descr: str,
                          value: str,
                          grp: str,
                          modified_by: str):
        """
        Update the record by setting all column values.

        :param id: str
        :param descr: str
        :param value: str
        :param grp: str
        :param modified_by: str
        :param tm_stamp: datetime.datetime
        """
        self.rec.id = id
        self.rec.descr = descr
        self.rec.value = value
        self.rec.grp = grp
        self.rec.modified_by = modified_by

        await self.update()

    async def update(self, irec: tConfig = None):
        """
        Update the record in the database with the record or irec if it is not None.

        :param irec: If not None use this record to update the database.
        """
        if irec:
            self.rec._copy_from(irec)

        with dConfigUpdate(self._dbcon) as _qry:
            await _qry.exec(self.rec)

        if irec:
            irec._copy_from(self.rec)

    async def insert_deft(self,
                          id: str,
                          descr: str,
                          value: str,
                          grp: str,
                          modified_by: str):
        """
        Insert the record setting all column values.

        :param id: str
        :param descr: str
        :param value: str
        :param grp: str
        :param modified_by: str
        :param tm_stamp: datetime.datetime
        """
        self.rec.id = id
        self.rec.descr = descr
        self.rec.value = value
        self.rec.grp = grp
        self.rec.modified_by = modified_by

        await self.insert()

    async def insert(self, irec: tConfig = None):
        """
        Insert the record in the database with the record or irec if it is not None.

        :param irec: If not None use this record to insert into the database.
        """
        if irec:
            self.rec._copy_from(irec)

        with dConfigInsert(self._dbcon) as _qry:
            await _qry.exec(self.rec)

        if irec:
            irec._copy_from(self.rec)

