##
## interfaces.py
import os
from hurry.workflow.interfaces import IWorkflow, IWorkflowInfo
from zc.sourcefactory.basic import BasicSourceFactory
from zope import schema
from zope.component import getUtility
from zope.component.interfaces import IObjectEvent
from zope.interface import Interface, Attribute, implements
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm

default_frontpage = u'' + open(os.path.join(
        os.path.dirname(__file__), 'frontpage.rst'), 'rb').read()

class FatalCSVError(Exception):
    """Some row could not be processed.
    """
    pass

class DuplicationError(Exception):
    """An exception that can be raised when duplicates are found.

    When raising :exc:`DuplicationError` you can, beside the usual
    message, specify a list of objects which are duplicates. These
    values can be used by catching code to print something helpful or
    similar.
    """
    def __init__(self, msg, entries=[]):
        self.msg = msg
        self.entries = entries

    def __str__(self):
        return '%r' % self.msg

def SimpleWAeUPVocabulary(*terms):
    """A well-buildt vocabulary provides terms with a value, token and
       title for each term
    """
    return SimpleVocabulary([
            SimpleTerm(value, value, title) for title, value in terms])

class RoleSource(BasicSourceFactory):
    """A source for global roles.
    """
    def getValues(self):
        # late import: in interfaces we should not import local modules
        from waeup.sirp.permissions import getWAeUPRoleNames
        return getWAeUPRoleNames()

    def getTitle(self, value):
        # late import: in interfaces we should not import local modules
        from waeup.sirp.permissions import getRoles
        roles = dict(getRoles())
        if value in roles.keys():
            title = roles[value].title
            if '.' in title:
                title = title.split('.', 2)[1]
        return title

class IWAeUPObject(Interface):
    """A WAeUP object.

    This is merely a marker interface.
    """

class IUniversity(IWAeUPObject):
    """Representation of a university.
    """
    name = schema.TextLine(
        title = u'Name of University',
        default = u'Sample University',
        required = True,
        )

    title = schema.TextLine(
        title = u'Title of frontpage',
        default = u'Welcome to the Student Information and Registration ' +
                  u'Portal of Sample University',
        required = False,
        )

    skin = schema.Choice(
        title = u'Skin',
        default = u'waeuptheme-gray1.css',
        vocabulary = 'waeup.sirp.browser.theming.ThemesVocabulary',
        required = True,
        )

    frontpage = schema.Text(
        title = u'Content in reST format',
        required = False,
        default = default_frontpage,
        )


class IWAeUPContainer(IWAeUPObject):
    """A container for WAeUP objects.
    """

class IWAeUPContained(IWAeUPObject):
    """An item contained in an IWAeUPContainer.
    """

class IStudentContainer(IWAeUPContainer):
    """A container for StudentObjects.
    """


class IHostelContainer(IWAeUPContainer):
    """A container for hostels.
    """
    def addHostel(hostel):
        """Add an IHostel object.

        Returns the key, under which the object was stored.
        """

class IHostel(IWAeUPObject):
    """Representation of a hostel.
    """
    name = schema.TextLine(
        title = u'Name of Hostel',
        default = u'Nobody',
        required = True,
        )


class IWAeUPExporter(Interface):
    """An exporter for objects.
    """
    def export(obj, filepath=None):
        """Export by pickling.

        Returns a file-like object containing a representation of `obj`.

        This is done using `pickle`. If `filepath` is ``None``, a
        `cStringIO` object is returned, that contains the saved data.
        """

class IWAeUPXMLExporter(Interface):
    """An XML exporter for objects.
    """
    def export(obj, filepath=None):
        """Export as XML.

        Returns an XML representation of `obj`.

        If `filepath` is ``None``, a StringIO` object is returned,
        that contains the transformed data.
        """

class IWAeUPXMLImporter(Interface):
    """An XML import for objects.
    """
    def doImport(filepath):
        """Create Python object from XML.

        Returns a Python object.
        """

class IBatchProcessor(Interface):
    """A batch processor that handles mass-operations.
    """
    name = schema.TextLine(
        title = u'Importer name'
        )

    mode = schema.Choice(
        title = u'Import mode',
        values = ['create', 'update', 'remove']
        )

    def doImport(path, headerfields, mode='create', user='Unknown',
                 logger=None):
        """Read data from ``path`` and update connected object.

        `headerfields` is a list of headerfields as read from the file
        to import.

        `mode` gives the import mode to use (``'create'``,
        ``'update'``, or ``'remove'``.

        `user` is a string describing the user performing the
        import. Normally fetched from current principal.

        `logger` is the logger to use during import.
        """

class IUserAccount(IWAeUPObject):
    """A user account.
    """
    name = schema.TextLine(
        title = u'User ID',
        description = u'Login name of user',
        required = True,)
    title = schema.TextLine(
        title = u'Name',
        description = u'Real name of user',
        required = False,)
    description = schema.TextLine(
        title = u'Description',
        required = False,)
    password = schema.Password(
        title = u'Password',
        required = True,)
    roles = schema.List(
        title = u'Global roles',
        value_type = schema.Choice(source=RoleSource()))


class IUserContainer(IWAeUPObject):
    """A container for users (principals).

    These users are used for authentication purposes.
    """

    def addUser(name, password, title=None, description=None):
        """Add a user.
        """

    def delUser(name):
        """Delete a user if it exists.
        """

class ILocalRolesAssignable(Interface):
    """The local roles assignable to an object.
    """
    def __call__():
        """Returns a list of dicts.

        Each dict contains a ``name`` referring to the role assignable
        for the specified object and a `title` to describe the range
        of users to which this role can be assigned.
        """

class IDataCenter(IWAeUPObject):
    """A data center.

    TODO : declare methods, at least those needed by pages.
    """
    pass

class IDataCenterFile(Interface):
    """A data center file.
    """

    name = schema.TextLine(
        title = u'Filename')

    size = schema.TextLine(
        title = u'Human readable file size')

    uploaddate = schema.TextLine(
        title = u'Human readable upload datetime')

    lines = schema.Int(
        title = u'Number of lines in file')

    def getDate():
        """Get creation timestamp from file in human readable form.
        """

    def getSize():
        """Get human readable size of file.
        """

    def getLinesNumber():
        """Get number of lines of file.
        """

class IDataCenterStorageMovedEvent(IObjectEvent):
    """Emitted, when the storage of a datacenter changes.
    """

class IObjectUpgradeEvent(IObjectEvent):
    """Can be fired, when an object shall be upgraded.
    """

class ILocalRoleSetEvent(IObjectEvent):
    """A local role was granted/revoked for a principal on an object.
    """
    role_id = Attribute(
        "The role id that was set.")
    principal_id = Attribute(
        "The principal id for which the role was granted/revoked.")
    granted = Attribute(
        "Boolean. If false, then the role was revoked.")

class IQueryResultItem(Interface):
    """An item in a search result.
    """
    url = schema.TextLine(
        title = u'URL that links to the found item')
    title = schema.TextLine(
        title = u'Title displayed in search results.')
    description = schema.Text(
        title = u'Longer description of the item found.')

class IWAeUPSIRPPluggable(Interface):
    """A component that might be plugged into a WAeUP SIRP app.

    Components implementing this interface are referred to as
    'plugins'. They are normally called when a new
    :class:`waeup.sirp.app.University` instance is created.

    Plugins can setup and update parts of the central site without the
    site object (normally a :class:`waeup.sirp.app.University` object)
    needing to know about that parts. The site simply collects all
    available plugins, calls them and the plugins care for their
    respective subarea like the applicants area or the datacenter
    area.

    Currently we have no mechanism to define an order of plugins. A
    plugin should therefore make no assumptions about the state of the
    site or other plugins being run before and instead do appropriate
    checks if necessary.

    Updates can be triggered for instance by the respective form in
    the site configuration. You normally do updates when the
    underlying software changed.
    """
    def setup(site, name, logger):
        """Create an instance of the plugin.

        The method is meant to be called by the central app (site)
        when it is created.

        `site`:
           The site that requests a setup.

        `name`:
           The name under which the plugin was registered (utility name).

        `logger`:
           A standard Python logger for the plugins use.
        """

    def update(site, name, logger):
        """Method to update an already existing plugin.

        This might be called by a site when something serious
        changes. It is a poor-man replacement for Zope generations
        (but probably more comprehensive and better understandable).

        `site`:
           The site that requests an update.

        `name`:
           The name under which the plugin was registered (utility name).

        `logger`:
           A standard Python logger for the plugins use.
        """

class IAuthPluginUtility(Interface):
    """A component that cares for authentication setup at site creation.

    Utilities providing this interface are looked up when a Pluggable
    Authentication Utility (PAU) for any
    :class:`waeup.sirp.app.University` instance is created and put
    into ZODB.

    The setup-code then calls the `register` method of the utility and
    expects a modified (or unmodified) version of the PAU back.

    This allows to define any authentication setup modifications by
    submodules or third-party modules/packages.
    """

    def register(pau):
        """Register any plugins wanted to be in the PAU.
        """

    def unregister(pau):
        """Unregister any plugins not wanted to be in the PAU.
        """

class IObjectConverter(Interface):
    """Object converters are available as simple adapters, adapting
       interfaces (not regular instances).

    """

    def fromStringDict(self, data_dict, context, form_fields=None):
        """Convert values in `data_dict`.

        Converts data in `data_dict` into real values based on
        `context` and `form_fields`.

        `data_dict` is a mapping (dict) from field names to values
        represented as strings.

        The fields (keys) to convert can be given in optional
        `form_fields`. If given, form_fields should be an instance of
        :class:`zope.formlib.form.Fields`. Suitable instances are for
        example created by :class:`grok.AutoFields`.

        If no `form_fields` are given, a default is computed from the
        associated interface.

        The `context` can be an existing object (implementing the
        associated interface) or a factory name. If it is a string, we
        try to create an object using
        :func:`zope.component.createObject`.

        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
        <DATA_DICT>)`` where

        ``<FIELD_ERRORS>``
           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
           error that happened when validating the input data in
           `data_dict`

        ``<INVARIANT_ERRORS>``
           is a list of invariant errors concerning several fields

        ``<DATA_DICT>``
           is a dict with the values from input dict converted.

        If errors happen, i.e. the error lists are not empty, always
        an empty ``<DATA_DICT>`` is returned.

        If ``<DATA_DICT>` is non-empty, there were no errors.
        """

class IObjectHistory(Interface):

    messages = schema.List(
        title = u'List of messages stored',
        required = True,
        )

    def addMessage(message):
        """Add a message.
        """

class IWAeUPWorkflowInfo(IWorkflowInfo):
    """A :class:`hurry.workflow.workflow.WorkflowInfo` with additional
       methods for convenience.
    """
    def getManualTransitions():
        """Get allowed manual transitions.

        Get a sorted list of tuples containing the `transition_id` and
        `title` of each allowed transition.
        """

class ISiteLoggers(Interface):

    loggers = Attribute("A list or generator of registered WAeUPLoggers")

    def register(name, filename=None, site=None, **options):
        """Register a logger `name` which logs to `filename`.

        If `filename` is not given, logfile will be `name` with
        ``.log`` as filename extension.
        """

    def unregister(name):
        """Unregister a once registered logger.
        """

class ILogger(Interface):
    """A logger cares for setup, update and restarting of a Python logger.
    """

    logger = Attribute("""A :class:`logging.Logger` instance""")


    def __init__(name, filename=None, site=None, **options):
        """Create a WAeUP logger instance.
        """

    def setup():
        """Create a Python :class:`logging.Logger` instance.

        The created logger is based on the params given by constructor.
        """

    def update(**options):
        """Update the logger.

        Updates the logger respecting modified `options` and changed
        paths.
        """

class ILoggerCollector(Interface):

    def getLoggers(site):
        """Return all loggers registered for `site`.
        """

    def registerLogger(site, logging_component):
        """Register a logging component residing in `site`.
        """

    def unregisterLogger(site, logging_component):
        """Unregister a logger.
        """
