import logging
import socket
import errno
import sys
import os
import os.path
import importlib

from amqpservices import amqplib_ext


log = logging.getLogger('amqpservices')

DEFAULT_CONFIG_MODULE_NAME = 'amqpservices_config'


class ConfigData(object):
    """Configuration data processed from the configuration module.
    The original Python module is accessible as `module` attribute.

    Attribute `queues` is dictionary with collected queues configuration
    from all services.

    Sentry object created from specified SENTRY_DSN is accessible
    as `sentry` attribute.
    """

    def __init__(self):
        # Configuration module and queues collected from it
        self._module = None
        self._queues = None
        self._sentry = None

    def _load_config(self):
        if self._module is not None:
            return
        module_from_env = os.getenv('AMQPSERVICES_CONFIG_MODULE')
        if module_from_env:
            log.info('Importing config module from env. variable AMQPSERVICES_CONFIG_MODULE: %s',
                    module_from_env)
            module_name = module_from_env
        else:
            log.info('Importing default config module %s', DEFAULT_CONFIG_MODULE_NAME)
            module_name = DEFAULT_CONFIG_MODULE_NAME
        try:
            self._module = importlib.import_module(module_name)
            self._collect_queues()
        except ImportError:
            msg = 'Cannot import amqpservices config module'
            sys.stderr.write(msg + '\n')
            log.critical(msg)
            raise

    def _collect_queues(self):
        self._queues = {}
        for service in self._module.SERVICES:
            self._queues.update(self._module.SERVICES[service]['queues'])
            self._queues['broadcast.{service}'.format(service=service)] = \
                    {'durable': False, 'want_reply': False}

    @property
    def module(self):
        self._load_config()
        return self._module

    @property
    def queues(self):
        self._load_config()
        return self._queues

    def _create_sentry(self):
        if self._sentry is not None:
            return
        try:
            import raven
            self._sentry = raven.Client(dsn=getattr(config.module, 'SENTRY_DSN', ''))
            log.info('Using sentry for error notification')
        except ImportError:
            class _SentryStub(object):
                def captureMessage(self, msg): pass
                def captureException(self): pass
            self._sentry = _SentryStub()
            log.info('Not using sentry for error notification - no module named "raven"')

    @property
    def sentry(self):
        self._create_sentry()
        return self._sentry


config = ConfigData()


LOCALHOST_CONNECTION = dict(host='localhost', userid='guest', password='guest', ssl=False)

class AMQPBase(object):
    def init_connection_and_channel(self, connparams, connect_timeout):
        log.info('Configured SERVICES=%s', config.module.SERVICES)
        log.info('All queues=%s', config.queues)
        try:
            self.conn = amqplib_ext.Connection(host=connparams['host'], userid=connparams['userid'],
                    password=connparams['password'], ssl=connparams['ssl'],
                        use_threading=False, connect_timeout=connect_timeout)
            self.conn.transport.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            self.ch = self.conn.channel()
            self.ch.access_request('/data', active=True, write=True)
            self.ch.basic_qos(prefetch_size=0, prefetch_count=1, a_global=False)
        except:
            log.exception('While initializing broker connection')
            sentry.captureException()
            raise
    def _process_events(self, timeout=None):
        while True:
            try:
                self.conn.drain_events(timeout)
                return
            except socket.error, val:
                if val.errno == errno.EINTR:
                    print('Restarting due to interrupting by a signal')
                    pass
                else:
                    raise


