from django import db as django_db
from django.conf import settings
from django.core import signals


class ReadOnlyConnection(object):

    """
    This class constructs a new connection to the DB using the read-only
    credentials from settings.  It reaches into some internals of
    django.db.connection which are undocumented as far as I know, but I believe
    it works across many, if not all, of the backends.
    """
    _the_instance = None

    # support singleton
    @classmethod
    def get_connection(cls):
        if cls._the_instance is None:
            cls._the_instance = ReadOnlyConnection()
        return cls._the_instance

    @classmethod
    def set_globally_disabled(cls, disabled):
        """
        When globally disabled, the ReadOnlyConnection will simply pass through
        to the global Django connection.
        """
        if disabled:
            cls._the_instance = DummyReadOnlyConnection()
        else:
            cls._the_instance = None

    def __init__(self):
        self._connection = None

    def _open_connection(self):
        if self._connection is not None:
            return
        self._save_django_state()
        self._connection = self._get_readonly_connection()
        self._restore_django_state()

    def _save_django_state(self):
        self._old_connection = django_db.connection.connection
        _default_db = settings.DATABASES['default']
        self._old_host = _default_db['HOST']
        self._old_username = _default_db['USER']
        self._old_password = _default_db['PASSWORD']

    def _restore_django_state(self):
        django_db.connection.connection = self._old_connection
        _default_db = settings.DATABASES['default']
        _default_db['HOST'] = self._old_host
        _default_db['USER'] = self._old_username
        _default_db['PASSWORD'] = self._old_password

    def _get_readonly_connection(self):
        _default_db = settings.DATABASES['default']
        _default_db['HOST'] = _default_db['READONLY_HOST']
        _default_db['USER'] = _default_db['READONLY_USER']
        _default_db['PASSWORD'] = _default_db['READONLY_PASSWORD']
        reload(django_db)
        # cursor() causes a new connection to be created
        cursor = django_db.connection.cursor()
        assert django_db.connection.connection is not None
        return django_db.connection.connection

    def set_django_connection(self):
        assert (django_db.connection.connection != self._connection or
                self._connection is None)
        self._open_connection()
        self._old_connection = django_db.connection.connection
        django_db.connection.connection = self._connection

    def unset_django_connection(self):
        assert self._connection is not None
        assert django_db.connection.connection == self._connection
        django_db.connection.connection = self._old_connection

    def cursor(self):
        self._open_connection()
        return self._connection.cursor()

    def close(self):
        if self._connection is not None:
            assert django_db.connection.connection != self._connection
            self._connection.close()
            self._connection = None


class DummyReadOnlyConnection(object):

    """
    A dummy version which passes queries straight to the global Django
    connection.
    """

    def __init__(self):
        self._is_set = False

    def set_django_connection(self):
        assert not self._is_set
        self._is_set = True

    def unset_django_connection(self):
        assert self._is_set
        self._is_set = False

    def cursor(self):
        return django_db.connection.cursor()

    def close(self):
        pass


# convenience
def connection():
    return ReadOnlyConnection.get_connection()


# close any open connection when request finishes
def _close_connection(**unused_kwargs):
    connection().close()


signals.request_finished.connect(_close_connection)
