from threading import RLock

from sqlite3 import Connection
from dbapitemplate import DbApiTemplate

from typing import (
    Any,
    Callable,
    Optional,
)


class SynchronizedDbApiTemplate(DbApiTemplate):

    def __init__(
        self,   # type: SynchronizedDbApiTemplate
        conn,   # type: Connection
    ):
        # type: (...) -> None

        super(SynchronizedDbApiTemplate, self).__init__(conn)

        self._lock = RLock()

    def __enter__(
        self,   # type: SynchronizedDbApiTemplate
    ):
        # type: (...) -> SynchronizedDbApiTemplate

        self._lock.acquire()
        return self

    def execute(
        self,           # type: SynchronizedDbApiTemplate
        sql,            # type: str
        params=(),      # type: Optional[tuple]
        mapper=None,    # type: Optional[Callable[[tuple], Any]]
    ):
        # type: (...) -> Optional[Any]

        with self._lock:
            return super(SynchronizedDbApiTemplate, self).execute(
                sql=sql,
                params=params,
                mapper=mapper,
            )

    def first(
        self,           # type: SynchronizedDbApiTemplate
        sql,            # type: str
        params=(),      # type: tuple
        mapper=None,    # type: Optional[Callable[[tuple], Any]]
    ):
        # type: (...) -> Optional[Any]

        with self._lock:
            return super(SynchronizedDbApiTemplate, self).first(
                sql=sql,
                params=params,
                mapper=mapper,
            )

    def one(
        self,           # type: SynchronizedDbApiTemplate
        sql,            # type: str
        params=(),      # type: tuple
        mapper=None,    # type: Optional[Callable[[tuple], Any]]
    ):
        # type: (...) -> Optional[Any]

        with self._lock:
            return super(SynchronizedDbApiTemplate, self).one(
                sql=sql,
                params=params,
                mapper=mapper,
            )

    def get_scalar(
        self,   # type: SynchronizedDbApiTemplate
        sql,    # type: str
    ):
        # type: (...) -> Optional[Any]

        with self._lock:
            return super(SynchronizedDbApiTemplate, self).get_scalar(
                sql=sql,
            )

    def __exit__(
        self,       # type: SynchronizedDbApiTemplate
        exc_type,   # type: Optional[Exception]
        exc_val,    # type: Optional[str]
        exc_tb,     # type: Optional[str]
    ):
        # type: (...) -> None

        self._lock.release()
