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

import os.path, glob

# Zope 3
from zope.interface import implements

from zope.app import zapi

from zope.app.component.interfaces import ISite
from zope.app.component.interfaces.registration import ActiveStatus
from zope.app.component.site import LocalSiteManager
from zope.app.component.site import UtilityRegistration
from zope.app.component.hooks import setSite
from zope.app.authentication.authentication import PluggableAuthentication

from zope.app.catalog.interfaces import ICatalog
from zope.app.catalog.catalog import Catalog
from zope.app.catalog.field import FieldIndex

from zope.app.intid.interfaces import IIntIds
from zope.app.intid import IntIds

from zope.app.container.interfaces import INameChooser

from zope.app.folder import Folder
from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.security.interfaces import IAuthentication
from zope.app.mail.interfaces import ISMTPMailer, IQueuedMailDelivery

from zc.catalog.catalogindex import SetIndex
from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
from hurry.workflow import workflow
from hurry.workflow.interfaces import IWorkflowVersions, IWorkflow

from documentlibrary.core import configuration

# documentlibrary
from documentlibrary.core.authentication import\
     DocumentLibraryAuthenticator
from documentlibrary.core import flow, handle, email, interfaces, user
from documentlibrary.core.category import CategoryTree, CategoriesUtility
from documentlibrary.core.foitopic import FOITopicTree, FOITopicsUtility
from documentlibrary.core.foitopic import setupTopics
from documentlibrary.core.history import HistoryFolder

class DocumentLibrary(Folder):

    implements(interfaces.IDocumentLibrary)

    def __init__(self):
        super(DocumentLibrary, self).__init__()
        self['categories'] = CategoryTree()
        self['foi_topics'] = FOITopicTree()
        self['templates'] = Folder()
        self['history'] = HistoryFolder()
        self['users'] = user.UserFolder()
        self['groups'] = user.GroupFolder()
        self['ipgroups'] = user.IpGroupFolder()
        self['handle'] = handle.Root()
        
def setupDocumentLibrary(documentlibrary):
    sitemanager = LocalSiteManager(documentlibrary)
    documentlibrary.setSiteManager(sitemanager)

    setSite(documentlibrary)
    default = zapi.traverse(documentlibrary, '++etc++site/default')

    # create pluggable authentication and principal folder
    _registerAuthentication(default)
    
    # set up catalog facilities
    _registerUtility(default, IntIds, IIntIds)
    _registerUtility(default, Catalog, ICatalog, u'document_catalog')
    _registerUtility(default, Catalog, ICatalog, u'history_catalog')
    _registerUtility(default, Catalog, ICatalog, u'user_catalog')
    _registerDocumentIndices(documentlibrary)
    _registerHistoryIndices(documentlibrary)
    _registerUserIndices(documentlibrary)
    
    # set up categories and foi topics
    
    categories_utililty = addLocalUtility(
        documentlibrary, 'categories', 
        interfaces.IHierarchicalTermsUtility, CategoriesUtility())

    # XXX: demo content creation
    from testcontent import createTestCategories
    createTestCategories(documentlibrary)
        
    foi_topics_utililty = addLocalUtility(
        documentlibrary, 'foi_topics', 
        interfaces.IHierarchicalTermsUtility, FOITopicsUtility())
    setupTopics(documentlibrary)

    # history and email action templates
    _registerTemplates(documentlibrary)

    # register configuration
    _registerConfiguration(documentlibrary)
    
    # set up emailing facility
    _registerEmail(documentlibrary)
    
    # workflow
    _registerWorkflow(documentlibrary)
    
def _registerUtility(context, class_, interface, name=u''):
    if not name:
        cname = class_.__name__
    else:
        cname = name
    if name and name in context:
        raise ValueError, u'Utility %s already registered!' % name
    utility = class_()
    context[cname] = utility
    registration = UtilityRegistration(name, interface, utility)
    key = context.registrationManager.addRegistration(registration)
    zapi.traverse(context.registrationManager, key).status = ActiveStatus
    return utility

def addLocalUtility(site, name, iface, utility, package='default'):
    """Add a utility to a site
    
    The utility is added to the package and activated.
    This assumes the site has already a Utility Service.
    """
    # preconditions
    if not ISite.providedBy(site):
        raise TypeError('ISite required.')

    # get site manager and site management folder
    sitemanager = site.getSiteManager()
    default = sitemanager[package]

    # add utility to site management folder
    chooser = INameChooser(default)
    folder_name = chooser.chooseName(utility.__name__, utility)
    default[folder_name] = utility

    # create service registration
    path = zapi.getPath(utility)
    registration = UtilityRegistration(name, iface, utility)
    key = default.registrationManager.addRegistration(registration)
    zapi.traverse(default.registrationManager, key).status = ActiveStatus
    return zapi.traverse(sitemanager, path) 

def _registerAuthentication(default):
    pau = _registerUtility(default, PluggableAuthentication,
                           IAuthentication)
    # now register principal folder in it
    pf = _registerUtility(pau, DocumentLibraryAuthenticator,
                          IAuthenticatorPlugin,
                          'DocumentLibraryAuthenticator')
    # set prefix for pf XXX this is not unique enough
    # do *NOT* set prefix in PAU
    pf.prefix = u'documentlibrary'
    # and set plugins
    pau.credentialsPlugins = ['No Challenge if Authenticated',
                              'Zope Realm Basic-Auth']
    pau.authenticatorPlugins = [pf.__name__]
    
def _registerDocumentIndices(documentlibrary):
    from interfaces import IDocument
    catalog = zapi.getUtility(ICatalog, u'document_catalog')
    catalog['state'] = FieldIndex('state', IDocument)
    catalog['last_changed'] = FieldIndex('last_changed', IDocument)
    catalog['category'] = FieldIndex('category', IDocument)
    catalog['owner'] = FieldIndex('owner', IDocument)
    catalog['name'] = FieldIndex('__name__', IDocument)
    catalog['emails'] = SetIndex('authorEmails', IDocument, True)
    catalog['deletion_requested'] = FieldIndex('deletion_requested', IDocument)
    catalog['access'] = SetIndex('access', IDocument)
    
    catalog['workflow_state'] = FieldIndex('getState', IWorkflowState, True)
    catalog['workflow_id'] = FieldIndex('getId', IWorkflowState, True)
    catalog['workflow_auto'] = FieldIndex('hasAutomaticTransitions',
                                          IWorkflowInfo, True)

def _registerHistoryIndices(documentlibrary):
    from interfaces import IDocument, IHistoryEntry
    catalog = zapi.getUtility(ICatalog, u'history_catalog')
    catalog['docid'] = FieldIndex('docid', IHistoryEntry)
    catalog['action'] = FieldIndex('action', IHistoryEntry)
    catalog['change_time'] = FieldIndex('change_time', IHistoryEntry)
    catalog['actor'] = FieldIndex('actor', IHistoryEntry)

def _registerUserIndices(documentlibrary):
    from interfaces import IUserInfo
    catalog = zapi.getUtility(ICatalog, u'user_catalog')
    catalog['login'] = FieldIndex('login', IUserInfo)
    catalog['email'] = FieldIndex('email', IUserInfo)
    catalog['groups'] = SetIndex('groups', IUserInfo)
    catalog['role'] = FieldIndex('role', IUserInfo)

def _registerTemplates(documentlibrary):
    from zope.app.dtmlpage.dtmlpage import DTMLPage

    templates = documentlibrary['templates']

    # read all initial templates in templates directory
    templates_dir = os.path.join(_getModuleDir(), 'templates')

    for entry in glob.glob(os.path.join(templates_dir, '*.txt')):
        dummy, filename = os.path.split(entry)
        name, dummy = os.path.splitext(filename)
        f = open(entry, 'rb')
        data = unicode(f.read(), 'UTF-8')
        f.close()
        template = DTMLPage()
        template.setSource(data, 'text/plain')
        templates[name] = template

def _registerEmail(documentlibrary):
    addLocalUtility(
        documentlibrary, u'',
        ISMTPMailer, email.PersistentSMTPMailer())
    addLocalUtility(
        documentlibrary, u'',
        IQueuedMailDelivery, email.PersistentQueuedMailDelivery())

def _registerConfiguration(documentlibrary):
    addLocalUtility(
        documentlibrary, u'',
        interfaces.ITimezone, configuration.Timezone())
    addLocalUtility(
        documentlibrary, u'',
        interfaces.IFilenameValidation, configuration.FilenameValidation())
    addLocalUtility(
       documentlibrary, u'',
       interfaces.ICustomText, configuration.CustomText())
    addLocalUtility(
        documentlibrary, u'',
        interfaces.IMaximumRetentionPeriod,
        configuration.MaximumRetentionPeriod())
    
def _registerWorkflow(documentlibrary):
    addLocalUtility(
        documentlibrary, u'',
        IWorkflow, workflow.Workflow(flow.createWorkflow()))
    addLocalUtility(
        documentlibrary, u'',
        IWorkflowVersions, flow.WorkflowVersions())
        
def documentLibraryAddedHandler(event):
    if interfaces.IDocumentLibrary.providedBy(event.object):
        setupDocumentLibrary(event.object)

def _getModuleDir():
    P = os.path
    d = P.dirname(__file__)
    d = P.normpath(P.abspath(d))
    return d
