import grok
import logging
import os
from zope.authentication.interfaces import IAuthentication
from zope.component import createObject, getUtilitiesFor
from zope.component.interfaces import ObjectEvent
from zope.pluggableauth import PluggableAuthentication
from waeup.sirp.authentication import setup_authentication
from waeup.sirp.datacenter import DataCenter
from waeup.sirp.interfaces import (
    IUniversity, IDataCenter, IWAeUPSIRPPluggable,
    IDataCenterStorageMovedEvent, IObjectUpgradeEvent, )
from waeup.sirp.users import UserContainer
from waeup.sirp.utils.helpers import attrs_to_fields

class University(grok.Application, grok.Container):
    """A university.
    """
    grok.implements(IUniversity)

    # Setup authentication for this app. Note: this is only
    # initialized, when a new instance of this app is created.
    grok.local_utility(
        PluggableAuthentication, provides = IAuthentication,
        setup = setup_authentication,)

    @property
    def logger(self):
        """The application logger.

        Returns a standard logger object as provided by :mod:`logging`
        module from the standard library.

        Other components can use this logger to perform log entries
        into the 'main' logfile.

        The logger is initialized the first time, it is called.
        """
        sitename = self.__name__
        loggername = 'waeup.sirp.%s' % sitename
        logger = logging.getLogger(loggername)
        if not logger.handlers:
            logger = self._setupLogger(logger)
        return logger

    def __init__(self, name=None, skin=None, title=None,
                 frontpage=None, **kw):
        super(University, self).__init__(**kw)
        if name is not None:
            self.name = name
        if skin is not None:
            self.skin = skin
        if title is not None:
            self.title = title
        if frontpage is not None:
            self.frontpage = frontpage
        self.setup()

    def setup(self):
        """Setup some hard-wired components.

        Create local datacenter, containers for users, students and
        the like.
        """
        self['users'] = UserContainer()
        self['datacenter'] = DataCenter()

        self['students'] = createObject(u'waeup.StudentContainer')
        self['hostels'] = createObject(u'waeup.HostelContainer')
        self._createPlugins()

    def _createPlugins(self):
        """Create instances of all plugins defined somewhere.
        """
        for name, plugin in getUtilitiesFor(IWAeUPSIRPPluggable):
            plugin.setup(self, name, self.logger)
        return

    def updatePlugins(self):
        """Lookup all plugins and call their `update()` method.
        """
        name = getattr(self, '__name__', '<Unnamed>')
        self.logger.info('Fire upgrade event for site %s' % name)
        grok.notify(ObjectUpgradeEvent(self))
        self.logger.info('Done.')
        self.logger.info('Now upgrading any plugins.')
        for name, plugin in getUtilitiesFor(IWAeUPSIRPPluggable):
            plugin.update(self, name, self.logger)
        self.logger.info('Plugin update finished.')
        return

    def _setupLogger(self, logger):
        """Setup general application logger.

        The logfile will be stored in the datacenter logs/ dir.
        """
        logdir = os.path.join(self['datacenter'].storage, 'logs')
        if not os.path.exists(logdir):
            os.mkdir(logdir)
        filename = os.path.join(logdir, 'main.log')

        # Create a rotating file handler logger for application.
        handler = logging.handlers.RotatingFileHandler(
            filename, maxBytes=5*1024**1, backupCount=7)
        formatter = logging.Formatter(
            '%(asctime)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)

        # Don't send log msgs to ancestors. This stops displaying
        # logmessages on the commandline.
        logger.propagate = False
        logger.addHandler(handler)
        return logger
attrs_to_fields(University)

@grok.subscribe(IDataCenter, IDataCenterStorageMovedEvent)
def handle_storage_move(obj, event):
    """Event handler, in case datacenter storage moves.

    We initialize the application log again, then.
    """
    app = grok.getSite()
    if app is None:
        return
    if obj is not app['datacenter']:
        return
    logger = app.logger
    logger.warn('Log Dir moved. Closing.')
    handlers = logger.handlers
    for handler in handlers:
        logger.removeHandler(handler)
    app._setupLogger(logger)
    logger.warn('Log file moved. Opening.')

class ObjectUpgradeEvent(ObjectEvent):
    """An event fired, when datacenter storage moves.
    """
    grok.implements(IObjectUpgradeEvent)
