# Copyright (c) 2005 Infrae. All rights reserved.
# See also LICENSE.txt

# Zope3
from persistent import Persistent
from zope.security.interfaces import NoInteraction
from zope.security.management import getInteraction
from zope.interface import implements

from hurry.workflow.interfaces import IWorkflowState

from zope.app import zapi
from zope.app.container.contained import Contained
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
from zope.app.intid.interfaces import IIntIds
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager
from zope.app.location.interfaces import ILocation

# documentlibrary
from documentlibrary.core import interfaces, handle, timezone
from documentlibrary.core import flow, history

class Document(Persistent, Contained):

    implements(interfaces.IDocumentContained)

    _persistent_file = None
    
    def __init__(self,
                 title=u'',
                 description=u'',
                 category=None,
                 foi_topic=None,
                 authors=None,
                 modificationdate=None,
                 versionstring=u'',
                 availabledate=None,
                 expirydate=None,
                 access=None,
                 owner=u'',
                 file=None,
                 file_available=False,
                 pdf=None,
                 generate_pdf=False,
                 plaintext=None,
                 generate_plaintext=False,
                 note=u'',
                 deletion_requested=False,
                 deletion_receipt=None):
        # set basic properties
        self.title = title
        self.description = description
        self.category = category
        self.foi_topic = foi_topic
        self._authors = []
        self.authors = authors or []
        self.modificationdate = modificationdate
        self.versionstring= versionstring
        self.availabledate = availabledate
        self.expirydate = expirydate
        self.access = access or []
        self.owner = owner
        self.file = file
        self.file_available = file_available
        self.pdf = pdf
        self.generate_pdf = generate_pdf
        self.plaintext = plaintext
        self.generate_plaintext = generate_plaintext
        self.note = note
        self.deletion_requested = deletion_requested
        self.deletion_receipt = deletion_receipt
        # initialize last_changed to now upon creation
        self.last_changed = timezone.nowInUTC()
         
    def _set_authors(self, authors):
        manager = IPrincipalRoleManager(self)
        lookup = zapi.getUtility(interfaces.IUserInfoLookup)
        for firstname, lastname, email in self._authors:
            user = lookup.getUserInfoForEmail(email)
            if user is None:
                continue
            username = 'documentlibrary' + user.login
            manager.unsetRoleForPrincipal('documentlibrary.DocumentOwner',
                                          username)

        authors = [(first, last, email.lower()) for
                   (first, last, email) in authors]
        
        self._authors = authors
            
        for firstname, lastname, email in authors:
            user = lookup.getUserInfoForEmail(email)
            if user is None:
                continue
            # cannot give this role to someone who is not a submitter
            if user.role not in [
                interfaces.SUBMITTER, interfaces.LIBRARIAN,
                interfaces.MANAGER]:
                continue
            username = 'documentlibrary' + user.login
            manager.assignRoleToPrincipal('documentlibrary.DocumentOwner',
                                          username)

    def _get_authors(self):
        return self._authors

    authors = property(_get_authors, _set_authors)

    def _get_handle_id(self):
        return str(IWorkflowState(self).getId())

    handle_id = property(_get_handle_id)
                            
    def authorEmails(self):
        return [email for (firstname, lastname, email) in
                self._authors]
    
    def getCategory(self):
        if self.category is None:
            return None
        util = zapi.getUtility(IIntIds)
        return util.getObject(self.category)
    
    def getCategoryName(self):
        # XXX This is not correct, but at least it unbreaks things for now
        if self.category is None:
            return ''
        return self.getCategory().name

    def getFoiTopic(self):
        if self.foi_topic is None:
            return None
        util = zapi.getUtility(IIntIds)
        return util.getObject(self.foi_topic)
    
    def getFoiTopicName(self):
        # XXX This is not correct, but at least it unbreaks things for now
        if self.foi_topic is None:
            return ''
        return self.getFoiTopic().name
    
    def getStateName(self):
        return flow.getStateName(IWorkflowState(self).getState())

    def getOwnerPrincipal(self):
        try:
            return zapi.getUtility(IAuthentication).getPrincipal(self.owner)
        except PrincipalLookupError:
            return UnknownPrincipal(self.owner)

    def copy(self):
        return Document(
                title=self.title,
                description=self.description,
                category=self.category,
                foi_topic=self.foi_topic,
                authors=self.authors,
                modificationdate=self.modificationdate,
                versionstring=self.versionstring,
                availabledate=self.availabledate,
                expirydate=self.expirydate,
                access=self.access,
                file=self.file,
                file_available=self.file_available,
                pdf=self.pdf,
                generate_pdf=self.generate_pdf,
                plaintext=self.plaintext,
                owner=self.owner,
                note=self.note,
                deletion_requested=False,
                deletion_receipt=None,
            )

class DeletionReceipt(Persistent, Contained):
    implements(interfaces.IDeletionReceipt)
    
    def __init__(self, requester_name='',
                 requester_email='', explanation='', deletion_date=None):
        self.requester_name = requester_name
        self.requester_email = requester_email
        self.explanation = explanation
        self.deletion_date = deletion_date

# this is an adapter factory; the adapter itself is in fact an
# attribute on the object
def deletionReceiptFactory(context):
    if context.deletion_receipt is None:
        receipt = context.deletion_receipt = DeletionReceipt()
        from zope.security.proxy import removeSecurityProxy
        receipt.__parent__ = removeSecurityProxy(context)
        receipt.__name__ = 'deletion_receipt'
    return context.deletion_receipt

class DownloadTraverser(handle.BaseTraverser):
        
    def publishTraverse(self, request, name):
        context = self.context
        # only allow download of file if it's made available or user has
        # enough permissions to actually edit it
        download = None
        if (context.file_available or
            getInteraction().checkPermission('documentlibrary.EditDocument',
                                             context)):
            download = self.getDownload(context.file, name)
        if download is None:
            download = self.getDownload(context.pdf, name)
        if download is None:
            download = self.getDownload(context.plaintext, name)
        if download is not None:
            return download
        return super(DownloadTraverser, self).publishTraverse(request, name)
        
    def getDownload(self, file, name):
        if file is not None and file.filename == name:
            return Download(file.data, name, self.context)
        else:
            return None
        
class Download(object):
    implements(ILocation, interfaces.IDownload)
    
    def __init__(self, data, name, parent):
        self.data = data
        self.__name__ = self.filename = name
        self.__parent__ = parent

class UnknownPrincipal:
    title = "Unknown"
    email = "Unknown"
    
    def __init__(self, id):
        self.id = id    
            
def on_document_created(event):
    doc = event.object
    if not interfaces.IDocument.providedBy(doc):
        return
    principal = getPrincipal()
    if principal is theNullPrincipal:
        return
    manager = IPrincipalRoleManager(doc)
    # take owner from existing doc if possible, otherwise from
    # principal
    if doc.owner:
        owner = doc.owner
    else:
        owner = principal.id
    manager.assignRoleToPrincipal(
        'documentlibrary.DocumentOwner',
        owner)
    doc.owner = owner
    
def on_document_added(event):
    doc = event.object
    if not interfaces.IDocument.providedBy(doc):
        return
    principal = getPrincipal()
    # XXX this handler can go
    
def on_document_state_changed(event):
    doc = event.object
    if not interfaces.IDocument.providedBy(doc):
        return
    # XXX ugh, hack to make sure we can do what follows even
    # if document is not attached to anything else (so that
    # role information is unknown)
    from zope.security.proxy import removeSecurityProxy
    doc = removeSecurityProxy(doc)

    # always timestamp doc when state changes
    doc.last_changed = timezone.nowInUTC()

    principal = getPrincipal()
    history.createEntry(event, principal)
    history.sendEmail(event, principal)

class NullPrincipal(object):
    def __init__(self):
        self.id = 'NullPrincipal'

theNullPrincipal = NullPrincipal()

def getPrincipal():
    """This is scary but makes life a lot easier...
    """
    try:
        return getInteraction().participations[0].principal
    except NoInteraction:
        return theNullPrincipal
