#
#  Copyright (c) 2012 Helmut Merz helmutm@cy55.de
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

"""
Definition of view classes and other browser related stuff for tasks.
"""

import calendar
from datetime import date, datetime, timedelta
from urllib import urlencode
from zope.app.container.interfaces import INameChooser
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import interface, component
from zope.traversing.api import getName

from cybertools.browser.action import actions
from cybertools.meta.interfaces import IOptions
from loops.browser.action import DialogAction, TargetAction
from loops.browser.concept import ConceptView
from loops.browser.form import CreateConceptPage, CreateConcept
from loops.browser.form import EditConceptPage, EditConcept
from loops.browser.node import NodeView
from loops.common import adapted, baseObject
from loops.concept import Concept
from loops.organize.work.meeting import MeetingMinutes
from loops.organize.tracking.report import TrackDetails
from loops.setup import addAndConfigureObject
from loops.util import _
from loops import util


organize_macros = ViewPageTemplateFile('view_macros.pt')


actions.register('createEvent', 'portlet', DialogAction,
        title=_(u'Create Event...'),
        description=_(u'Create a new event.'),
        viewName='create_concept.html',
        dialogName='createEvent',
        typeToken='.loops/concepts/event',
        fixedType=True,
        prerequisites=['registerDojoDateWidget'],
        permission='loops.AssignAsParent',
)

actions.register('editEvent', 'portlet', DialogAction,
        title=_(u'Edit Event...'),
        description=_(u'Modify event.'),
        viewName='edit_concept.html',
        dialogName='editEvent',
        prerequisites=['registerDojoDateWidget'],
)

actions.register('createFollowUpEvent', 'portlet', TargetAction,
        title=_(u'Create Follow-up Event...'),
        description=_(u'Create an event that is linked to this one.'),
        viewName='create_followup_event.html',
        prerequisites=['registerDojoDateWidget'],
)

actions.register('editFollowUpEvent', 'portlet', TargetAction,
        title=_(u'Edit Event...'),
        description=_(u'Modify follow-up event.'),
        viewName='edit_followup_event.html',
        prerequisites=['registerDojoDateWidget'],
)

actions.register('createAgendaItem', 'portlet', DialogAction,
        title=_(u'Create Agenda Item...'),
        description=_(u'Create a new agenda item.'),
        viewName='create_concept.html',
        dialogName='createAgendaItem',
        typeToken='.loops/concepts/agendaitem',
        fixedType=True,
        innerForm='inner_concept_form.html',
        prerequisites=['registerDojoDateWidget'],
)

actions.register('editAgendaItem', 'portlet', DialogAction,
        title=_(u'Edit Agenda Item...'),
        description=_(u'Modify agenda item.'),
        viewName='edit_concept.html',
        dialogName='editAgendaItem',
)


class Events(ConceptView):

    @Lazy
    def macro(self):
        return organize_macros.macros['events']

    def getActions(self, category='object', page=None, target=None):
        acts = super(Events, self).getActions(category, page, target)
        if category == 'portlet':
            acts.extend(actions.get(category, ['createEvent'],
                                view=self, page=page, target=target))
        return acts

    @Lazy
    def selectedDate(self):
        year = int(self.request.get('cal_year') or 0)
        month = int(self.request.get('cal_month') or 0)
        day = int(self.request.get('cal_day') or 0)
        if year and month and day:
            return date(year, month, day)
        return None

    def events(self):
        cm = self.loopsRoot.getConceptManager()
        tEvent = cm['event']
        hasType = cm.getTypePredicate()
        now = datetime.today()
        delta = int(self.request.get('delta',
                        IOptions(adapted(self.context))('delta', [0])[0]))
        sort = lambda x: x.adapted.start or now
        relViews = (self.childViewFactory(r, self.request, contextIsSecond=True)
                        for r in tEvent.getChildRelations([hasType], sort=None))
        if self.selectedDate:
            #end = self.selectedDate + timedelta(1)
            return sorted((rv for rv in relViews
                                if rv.adapted.start.date() <= self.selectedDate and
                                   rv.adapted.end.date() >= self.selectedDate),
                        key=sort)
        else:
            return sorted((rv for rv in relViews
                            if not rv.adapted.end or
                                rv.adapted.end >= now - timedelta(delta)),
                        key=sort)


class CalendarInfo(NodeView):

    monthNames = ('January', 'February', 'March', 'April', 'May', 'June',
                  'July', 'August', 'September', 'October', 'November', 'December')

    weekDays = ('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')

    def __init__(self, context, request):
        self.context = context
        self.request = request

    @Lazy
    def today(self):
        return date.today()

    @Lazy
    def selectedYear(self):
        return int(self.request.get('cal_year') or self.today.year)

    @Lazy
    def selectedMonth(self):
        return int(self.request.get('cal_month') or self.today.month)

    @Lazy
    def previousYear(self):
        return self.selectedYear - 1

    @Lazy
    def nextYear(self):
        return self.selectedYear + 1

    @Lazy
    def previousMonth(self):
        m = self.selectedMonth
        y = self.selectedYear
        if m == 1:
            y, m = y - 1, 12
        else:
            m -= 1
        return dict(year=y, month=m)

    @Lazy
    def nextMonth(self):
        m = self.selectedMonth
        y = self.selectedYear
        if m == 12:
            y, m = y + 1, 1
        else:
            m += 1
        return dict(year=y, month=m)

    @Lazy
    def monthCalendar(self):
        return calendar.monthcalendar(self.selectedYear, self.selectedMonth)

    def isToday(self, day):
        if not day:
            return False
        return date(self.selectedYear, self.selectedMonth, day) == self.today

    def getWeekNumber(self, week):
        for day in week:
            if day:
                break
        return datetime(self.selectedYear, self.selectedMonth, day).isocalendar()[1]

    @Lazy
    def eventListQuery(self):
        calOption = self.globalOptions('organize.showCalendar')
        if isinstance(calOption, list):
            qu = self.conceptManager.get(calOption[0])
            return Events(qu, self.request)
        return None

    @Lazy
    def events(self):
        eventList = [[] for i in range(31)]
        cm = self.loopsRoot.getConceptManager()
        tEvent = cm['event']
        hasType = cm.getTypePredicate()
        start = datetime(self.selectedYear, self.selectedMonth, 1)
        fday, ndays = calendar.monthrange(self.selectedYear, self.selectedMonth)
        end = start + timedelta(ndays)
        view = self.eventListQuery
        if view is not None:
            relViews = (view.childViewFactory(r, self.request, contextIsSecond=True)
                            for r in tEvent.getChildRelations([hasType], sort=None))
            events = sorted((rv for rv in relViews
                            #if rv.adapted.start >= start and rv.adapted.start < end),
                            if rv.adapted.end >= start and rv.adapted.start <= end),
                        key=lambda x: (x.adapted.start, x.adapted.end))
            for ev in events:
                startDay = ev.adapted.start.day
                if ev.adapted.start < start:
                    startDay = 1
                endDay = ev.adapted.end.day
                if ev.adapted.end > end:
                    endDay = 31
                for d in range(startDay, endDay+1):
                    eventList[d-1].append(ev)
        return eventList

    def getEventsUrl(self, day):
        v = self.eventListQuery
        if v is not None:
            baseUrl = self.getUrlForTarget(v)
            params = dict(cal_year=self.selectedYear, cal_month=self.selectedMonth,
                          cal_day=day)
            return '?'.join((baseUrl, urlencode(params)))

    def getCssClass(self, day, tag='td'):
        if not day:
            return ''
        classes = []
        if tag == 'td':
            if self.isToday(day):
                classes.append('today')
            if self.events[day-1]:
                classes.append('has_events')
        return ' '.join(classes)

    def getEventTitles(self, day):
        events = self.events[day-1]
        return '; '.join(ev.title for ev in events)


# special forms

class CreateFollowUpEventForm(CreateConceptPage, MeetingMinutes):

    fixedType = True
    typeToken = '.loops/concepts/event'
    form_action = 'create_followup_event'
    showAssignments = False

    @Lazy
    def macro(self):
        return organize_macros.macros['create_followup_event']

    @Lazy
    def baseEvent(self):
        return adapted(self.virtualTargetObject)

    @Lazy
    def title(self):
        event = self.baseEvent
        evView = ConceptView(event, self.request)
        eventTitle = u'%s, %s' % (event.title, evView.data['start'])
        return _(u'Create Follow-up Event for: $event', 
                    mapping=dict(event=eventTitle))

    @Lazy
    def data(self):
        data = self.getData()
        data['title'] = self.baseEvent.title
        data['description'] = self.baseEvent.description
        data['participants'] = self.baseEvent.participants
        return data

    def results(self):
        return self.reportInstance.getResults(
                dict(tasks=util.getUidForObject(self.virtualTargetObject)))


class EditFollowUpEventForm(EditConceptPage, CreateFollowUpEventForm):

    pass


# form controllers

class BaseFollowUpController(object):

    pass


class CreateFollowUpEvent(CreateConcept, BaseFollowUpController):

    defaultTypeToken = '.loops/concepts/event'

    @Lazy
    def followsPredicate(self):
        return self.view.conceptManager['follows']

    @Lazy
    def baseEvent(self):
        return adapted(self.view.virtualTargetObject)

    def update(self):
        result = super(CreateFollowUpEvent, self).update()
        form = self.request.form
        toBeAssigned = form.get('cb_select_tasks') or []
        taskId = newTask = None
        workItems = self.view.loopsRoot.getRecordManager()['work']
        for id in sorted(toBeAssigned):
            if not '.' in id:
                taskId = id
                task = util.getObjectForUid(id)
                newTask = self.createFollowUpTask(adapted(task))
            else:
                tId, trackId = id.split('.')
                if tId == taskId:
                    track = workItems.get(trackId)
                    if track is not None:
                        td = TrackDetails(self.view, track)
                        newTId = self.view.getUidForObject(newTask)
                        track.doAction('move', td.personId, task=newTId)
        return result

    def createFollowUpTask(self, source):
        cm = self.view.conceptManager
        stask = baseObject(source)
        bevt = baseObject(self.baseEvent)
        taskType = stask.conceptType
        taskName = getName(stask)
        name = INameChooser(cm).chooseName(taskName, stask)
        newTask = addAndConfigureObject(cm, Concept, name,
                        conceptType=taskType, 
                        title=source.title, 
                        description=source.description,
                        responsible=source.responsible,
                        discussion=source.discussion,
                        consequences=source.consequences)
        stask.assignChild(newTask, self.followsPredicate)
        for rel in stask.getParentRelations():
            if rel.predicate not in (
                        self.view.typePredicate, self.followsPredicate):
                if rel.first == bevt:
                    parent = self.object
                else:
                    parent = rel.first
                newTask.assignParent(parent, rel.predicate, 
                                     order=rel.order, relevance=rel.relevance)
        return newTask

    def assignConcepts(self, obj):
        bevt = baseObject(self.baseEvent)
        bevt.assignChild(obj, self.followsPredicate)
        for rel in bevt.getParentRelations():
            if rel.predicate not in (self.view.typePredicate, self.followsPredicate):
                obj.assignParent(rel.first, rel.predicate,
                                 order=rel.order, relevance=rel.relevance)


class EditFollowUpEvent(EditConcept, BaseFollowUpController):

    pass

