from zope.app import zapi
from zope import event
from zope.app.event import objectevent
from zope.formlib import form
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.component.hooks import getSite
from zope.app.mail.interfaces import ISMTPMailer

from hurry.workflow.interfaces import IWorkflow, IWorkflowVersions

from documentlibrary.core import interfaces, flow, email, timezone
from documentlibrary.core.browser.form import FormMixin

class ConfigureUtilityForm(FormMixin, form.FormBase):

    template = ViewPageTemplateFile('templates/form.pt')
    
    def _getUtility(self):
        raise NotImplementedError

    def setUpWidgets(self, ignore_request=False):
        util = self._getUtility()
        self.adapters = {}
        self.widgets = form.setUpEditWidgets(
            self.form_fields, self.prefix, util, self.request,
            adapters=self.adapters, ignore_request=ignore_request
            )

    @form.action('save changes')
    def handleEditAction(self, action, data):
        util = self._getUtility()
        if form.applyChanges(util, self.form_fields, data, self.adapters):
            event.notify(objectevent.ObjectModifiedEvent(util))
            self.addFeedback('Updated')
        else:
            self.addFeedback('No changes')

QUEUE_PROCESSOR_STARTED = False

class ConfigureEmailForm(ConfigureUtilityForm):
    
    form_fields = form.Fields(interfaces.IPersistentSMTPMailer)
    
    label = u'email configuration'
    
    def _getUtility(self):
        return zapi.getUtility(ISMTPMailer)

    # XXX argh need to replicate this..
    @form.action('save changes')
    def handleEditAction(self, action, data):
        util = self._getUtility()
        if form.applyChanges(util, self.form_fields, data, self.adapters):
            event.notify(objectevent.ObjectModifiedEvent(util))
            self.addFeedback('Updated')
        else:
            self.addFeedback('No changes')
            
    @form.action('start mail queue processor')
    def handleStartQueueProcessor(self, action, data):
        global QUEUE_PROCESSOR_STARTED
        if not QUEUE_PROCESSOR_STARTED:
            email.startQueueProcessor(zapi.getUtility(ISMTPMailer))
            QUEUE_PROCESSOR_STARTED = True
            self.addFeedback('Mail queue processor started')
        else:
            self.addFeedback('Mail queue processor already running')
    
class ConfigureFilenameValidationForm(ConfigureUtilityForm):

    form_fields = form.Fields(interfaces.IFilenameValidation)

    label = u'filename validation configuration'

    def _getUtility(self):
        return zapi.getUtility(interfaces.IFilenameValidation)

class ConfigureCustomTextForm(ConfigureUtilityForm):

    form_fields = form.Fields(interfaces.ICustomText)

    label = u'custom text configuration'

    def _getUtility(self):
        return zapi.getUtility(interfaces.ICustomText)

class ConfigureMaximumRetentionPeriodForm(ConfigureUtilityForm):
    form_fields = form.Fields(interfaces.IMaximumRetentionPeriod)

    label = u'maximum retention period configuration'

    def _getUtility(self):
        return zapi.getUtility(interfaces.IMaximumRetentionPeriod)

class ConfigureTimezoneForm(ConfigureUtilityForm):

    form_fields = form.Fields(interfaces.ITimezone)

    label = u'document library timezone'

    def _getUtility(self):
        return zapi.getUtility(interfaces.ITimezone)
    
class DevelopmentForm(FormMixin, form.PageEditForm):

    form_fields = ()
    
    label = u'development'
    
    template = ViewPageTemplateFile('templates/form.pt')

    @form.action('Refresh workflow')
    def handleRefreshWorkflow(self, action, data):
        workflow = zapi.getUtility(IWorkflow)
        workflow.refresh(flow.createWorkflow())
        self.addFeedback('Workflow updated')

    @form.action('Fire automatic workflow transitions')
    def handleWorkflowFire(self, action, data):
        wf_versions = zapi.getUtility(IWorkflowVersions)
        wf_versions.fireAutomatic()
        self.addFeedback('Automatic workflow transitions fired')

    @form.action('Refresh templates')
    def handleRefreshTemplates(self, action, data):
        templates = getSite()['templates']
        for name in list(templates.keys()):
            del templates[name]
        from documentlibrary.core import library
        library._registerTemplates(getSite())
        self.addFeedback('Templates have been refreshed')

    # Two actions that will be removed later on - there're for testing
    @form.action('Generate test content')
    def handleGenerateTestContent(self, action, data):
        _generateTestContent()
        wf_versions = zapi.getUtility(IWorkflowVersions)
        wf_versions.fireAutomatic()
        self.addFeedback('Test content has been generated')

    @form.action('Approve some')
    def handleGenerateTestContent(self, action, data):
        _makeAvailable()
        wf_versions = zapi.getUtility(IWorkflowVersions)
        wf_versions.fireAutomatic()
        self.addFeedback('Some documents have been approved')
        
# Generating test content will be removed later on - there're for testing
def _generateTestContent():
    # add users
    # add groups
    # use random categories
    # use random FOI topics
    # assign submitters and librarians
    # set reasonable available and expiry dates
    # use lorem.txt as source text, lorem.pdf as pdf
    # use lorem content to generate titles, descriptions
    # make sure to fire creation/modification events
    lines = _lorem().readlines()
    _addUsers(lines[0].split())
    _addGroups(lines[1].split())
    # add 100 documents, using the lorem file
    for i in range(100):
        _addDocument(_lorem(), i)
        
def _makeAvailable():        
    import random
    submitted_documents = list(_submittedDocuments())
    for document in random.sample(submitted_documents, random.randint(0, 20)):
        _approveDocument(document)
        
def _approveDocument(document):
    from hurry.workflow.interfaces import IWorkflowInfo, InvalidTransitionError
    try:
        IWorkflowInfo(document).fireTransition('approve')
    except InvalidTransitionError:
        return
    
def _submittedDocuments():
    from documentlibrary.core import flow
    from hurry.query import Eq, And, In
    category_ids = getSite()['categories'].getOwnedCategoryIds()
    return flow.allVersions(
        And(
            In(('document_catalog', 'category'), category_ids),
            Eq(('document_catalog', 'workflow_state'), flow.SUBMITTED)))
    
def _lorem():
    import os, os.path
    dir, filename = os.path.split(__file__)
    return open(os.path.join(dir, 'lorem.txt'), 'r')

def _addUsers(names):
    import random
    from zope.exceptions import DuplicationError
    from documentlibrary.core.interfaces import VIEWER, SUBMITTER, LIBRARIAN
    from documentlibrary.core.user import UserInfo
    users = getSite()['users']
    # login, password, role, title, email, description, groups):
    for name in names:
        try:
            # just pick a role from author, librarian or viewer
            randomrole = random.choice([VIEWER, SUBMITTER, LIBRARIAN])
            user = UserInfo(
                name.lower(), name.lower(), randomrole, name.title(), 
                'jw+%s@infrae.com' % name.lower(), 
                'Generated Test User (%s)' % randomrole, [])
            contained_name = users.chooseName(name.lower(), user)
            users[contained_name] = user
            event.notify(objectevent.ObjectCreatedEvent(user))
        except DuplicationError:
            continue
            
def _addGroups(names):
    import random
    from zope.exceptions import DuplicationError
    from documentlibrary.core.user import GroupInfo
    groups = getSite()['groups']
    users = getSite()['users']
    # login, title, description, users
    for name in names:
        try:
            # just pick 5 users for the group
            usernames = [user.login for user in random.sample(users.values(), 5)]
            group = GroupInfo(
                name.lower(), name.title(), 'Generated test group', usernames)
            contained_name = groups.chooseName(name.lower(), group)
            # if name is already a username, skip it.
            if contained_name in users:
                continue
            groups[contained_name] = group
            event.notify(objectevent.ObjectCreatedEvent(group))
        except DuplicationError:
            continue

def _randomCategory():
    import random
    from documentlibrary.core import category
    c = category.vocabulary(None)
    # just pick a category
    return random.choice(c._terms).value

def _randomFOITopic():
    import random
    from documentlibrary.core import foitopic
    c = foitopic.vocabulary(None)
    # just pick a FOI topic
    return random.choice(c._terms).value

def _dates():
    """
    modification date <= today
    available date >= today
    expiry date >= today + 1 day
    """
    import random
    from datetime import timedelta
    today = timezone.nowInUTC()
    availability = random.randint(0, 5)
    expiry = availability + random.randint(3, 7)
    return (
        today - timedelta(days=1), 
        today + timedelta(availability), 
        today + timedelta(expiry))

def _addDocument(file, nr):
    import random
    from zope.app.intid.interfaces import IIntIds
    from zope.app.container.interfaces import INameChooser
    from hurry.workflow.interfaces import IWorkflowInfo
    from hurry.file.file import HurryFile
    from documentlibrary.core.user import getUsernamesForRoles
    from documentlibrary.core.document import Document
    """
    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''
    """    
    
    lines = file.readlines()
    
    file = HurryFile('lorem-ipsum.txt', '\n'.join(lines))
    first_few_words = ' '.join(
        [word.title() for word in random.choice(lines).split()[:5]])
    title = '%s (%s)' % (first_few_words, nr)
    description = ' '.join(lines[:4])
    category = _randomCategory()
    foi_topic = random.choice([None, _randomFOITopic()])
    # get all authors, pick three
    authornames = random.sample(getUsernamesForRoles(
        [interfaces.SUBMITTER, interfaces.LIBRARIAN, interfaces.MANAGER]), 5)
    authors = [
        (name.title(), name.title(), 'jw+%s@infrae.com' % name.lower()) 
        for name in authornames]
    versionstring = '1.0'
    modificationdate, availabledate, expirydate = _dates()
    groups = getSite()['groups']
    groupids = [group.login for group in random.sample(groups.values(), 5)]
    if foi_topic is None:
        access = random.sample(groupids, 2)
    else:
        access = ['zope.Everybody']
    file_available = True
    note = u'Generated test document'
    
    doc = Document(
        title=title,
        description=description, 
        category=category, 
        foi_topic=foi_topic, 
        authors=authors, 
        versionstring=versionstring, 
        modificationdate=modificationdate, 
        availabledate=availabledate,
        expirydate=expirydate, 
        access=access,
        file=file, 
        pdf=file, 
        plaintext=file,
        note=note
        )
        
    event.notify(objectevent.ObjectCreatedEvent(doc))
    documents = zapi.getUtility(IIntIds).getObject(category)['documents']
    name = INameChooser(documents).chooseName('', doc)
    documents[name] = doc
    IWorkflowInfo(doc).fireTransition('submit')
