# -*- coding: iso-8859-1 -*-
"""
    This is improved the old Calendar processor.

    @copyright: 2005 by Karel Zak <kzak@redhat.com> 
    @license: GNU GPL, see COPYING for details.

    @version: 20060216
"""

import calendar
import time
import locale

Dependencies = []
EnglishDayText = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

class CalendarOptions:
    def __init__(self, items):
        self.options = {}
        self.defaults = {}

        # name of first week day is independent on locales
        self.defaults['firstWeekDay'] = {
                'VALUE': 'Sunday'
        }
        self.defaults['headerMonth'] = {
                'VALUE': 'enable', 
                'STYLE': 'text-align: center; font-weight: bold; background-color: #fcfdc1; font-size: 150%'
        } 
        self.defaults['headerDayName'] = {
                'VALUE': 'enable', 
                'STYLE': 'text-align: center; font-weight: bold; background-color: #fff0ac; width: 10em'
        } 
        self.defaults['headerWeekendName'] = {
                'VALUE': 'enable', 
                'STYLE': 'text-align: center; font-weight: bold; background-color: #d6e0b7; width: 10em'
        }
        self.defaults['headerDay'] = {
                'STYLE': 'text-align: center; font-weight: bold; background-color: #fff7d2; border-bottom: white 0px none'
        } 
        self.defaults['eventDay'] = {
                'STYLE': 'border-top: white 0px none;'
        }
        self.defaults['headerWeekend'] = {
                'VALUE': 'enable',
                'STYLE': 'text-align: center; font-weight: bold; background-color: #e5ead6; border-bottom: white 0px none'
        } 
        self.defaults['headerToday'] = {
                'VALUE': 'enable',
                'STYLE': 'border: #a6a49a 1px solid; background-color: #d3f6d1'
        } 
        self.defaults['calendarsOrder'] = {
                'VALUE': 'normal'
        } 
        self.defaults['calendarCollapse'] = {
                'VALUE': 'disable'
        } 

        self.mergeOptionsAndDefaults(items)

    def mergeOptionsAndDefaults(self, items):
        if len(items) <= 0:
            self.options = self.defaults
            return
        for k, defaults in self.defaults.iteritems():
            if not items.has_key(k):
                self.options[k] = defaults
            else:
                options = items[k]
                for key, val in defaults.iteritems():
                    if not options.has_key(key):
                        options[key] = val
                self.options[k] = options

    def get(self, name):
        if len(self.options) > 0:
            if self.options.has_key(name):
                return self.options[name]
        return None

    def getBool(self, name):
        val = self.getValue(name)
        if val and len(val):
            if val.lower() == 'true' or val.lower() == 'enable':
                return 1
        return 0

    def getItem(self, name, key):
        items = self.get(name)
        if items and items.has_key(key):
            return items[key]
        return None

    def getValue(self, name):
        return self.getItem(name, 'VALUE')

    def getStyle(self, name):
        return self.getItem(name, 'STYLE')

class EventCalendar:
    def __init__(self, y, m, options, y_today, m_today, d_today):
        self.y = y
        self.m = m
        self.days = {}
        self.options = options
        self.debug = []
        self.y_today = y_today
        self.m_today = m_today
        self.d_today = d_today
        self.footnotes = []

        val = self.getValue('firstWeekDay')
        start = self.dayToNumber(val)
        if start <= 5:
            self.weekend = [5-start, 6-start]
        else:
            self.weekend = [0,6]

        calendar.setfirstweekday(start)
        lastDay = calendar.monthrange(y, m)[1]
        for i in xrange(1,lastDay+1):
            self.days[i] = []

    def isToday(self, d):
        if self.y == self.y_today and self.m == self.m_today and d == self.d_today:
            return 1
        return 0

    def getValue(self, name):
        return self.options.getValue(name)

    def getStyle(self, name):
        return self.options.getStyle(name)

    def addEvent(self,d,event):
        self.days[d].append(event)

    def dayToNumber(self, day):
        for i in xrange(0, 7):
            if EnglishDayText[i].lower()==day.lower():
                return i
        return None

    def getDayName(self, idx):
        lc = [locale.DAY_2, locale.DAY_3, locale.DAY_4, locale.DAY_5, locale.DAY_6, locale.DAY_7, locale.DAY_1]
        return locale.nl_langinfo(lc[idx])

    def getMonthName(self, idx):
        lc = [locale.MON_1, locale.MON_2, locale.MON_3, locale.MON_4, locale.MON_5, locale.MON_6, 
              locale.MON_7, locale.MON_8, locale.MON_9, locale.MON_10,locale.MON_11,locale.MON_12]
        return locale.nl_langinfo(lc[idx])

    def format(self, req, fmt):

        id = '%s_%s' % (self.y, self.m)
        if self.y==self.y_today and self.m==self.m_today:
            display = 'block'
        else:
            display = 'none'
       
        if self.options.getBool('calendarCollapse'):
            req.write('''<p onclick="showCalendar('%s')" style="color: blue; cursor: pointer; cursor: hand; margin-bottom: 0px">''' % id)
            req.write(fmt.text("%d / %s" % (self.y, self.getMonthName(self.m-1))))
            req.write('</p>\n')
            req.write('<div id="%s" style="display: %s">\n' % (id, display))
        
        req.write('<table style="background-color: transparent">\n')

        # Year / Month
        if self.options.getBool('headerMonth')==1:
            req.write(fmt.table_row(1))
            req.write(fmt.table_cell(1, {'colspan': '7', 'style': self.getStyle('headerMonth') }))
            req.write(fmt.text("%d / %s" % (self.y, self.getMonthName(self.m-1))))
            req.write(fmt.table_cell(0))
            req.write(fmt.table_row(0))

        # Names of Days
        if self.options.getBool('headerDayName')==1:
            req.write(fmt.table_row(1))
            for i in range(calendar.firstweekday(), calendar.firstweekday()+7):
                d = i%7
                if self.options.getBool('headerWeekendName') and d in [5,6]:
                    req.write(fmt.table_cell(1, {'style': self.getStyle('headerWeekendName') }))
                else:
                    req.write(fmt.table_cell(1, {'style': self.getStyle('headerDayName') }))
                req.write(fmt.text(self.getDayName(d)))
                req.write(fmt.table_cell(0))
            req.write(fmt.table_row(0))

        calList = calendar.monthcalendar(self.y, self.m)
        weekRows = len(calList)

        for week in calList:
            # date
            req.write(fmt.table_row(1))
            xd = 0
            for day in week:
                if self.options.getBool('headerWeekend') and xd in self.weekend:
                    req.write(fmt.table_cell(1, {'style': self.getStyle('headerWeekend') }))
                else:
                    req.write(fmt.table_cell(1, {'style': self.getStyle('headerDay') }))
                if day > 0:
                    req.write('<div id="%d_%d_%d">' % (self.y,self.m,day))
                    req.write(fmt.text("-%d-" % day))
                    req.write('</div>')
                req.write(fmt.table_cell(0))
                xd += 1
            req.write(fmt.table_row(0))
            # event
            req.write(fmt.table_row(1))
            for day in week:
                req.write(fmt.table_cell(1, {'style': self.getStyle('eventDay'), 'valign': 'top' }))
                if day > 0:
                    for event in self.days[day]:
                        if event.has_key('TEXT'):
                            if event.has_key('STYLE'):
                                req.write('<div style="%s">' % event['STYLE'])
                            else:
                                req.write('<div>')
                            req.write(fmt.text(event['TEXT']))
                            if event.has_key('FOOTNOTE'):
                                dt = '%d-%d-%d' % (self.y,self.m,day)
                                self.footnotes.append((dt, event['FOOTNOTE']))
                                req.write(fmt.anchorlink(1, name=dt))
                                req.write(fmt.text(' (%d)' % len(self.footnotes))) 
                                req.write(fmt.anchorlink(0))
                            req.write('</div>')
                req.write(fmt.table_cell(0))
            req.write(fmt.table_row(0))

        req.write('</table>\n');

        if len(self.footnotes):
            req.write(fmt.strong(1))
            req.write(fmt.text('Footnotes:'))
            req.write(fmt.strong(0))
            req.write(fmt.number_list(1))
            for d,f in self.footnotes:
                req.write(fmt.listitem(1))
                req.write(fmt.anchordef(d))
                req.write(fmt.strong(1))
                req.write(fmt.text(d+': '))
                req.write(fmt.strong(0))
                req.write(fmt.text(f))
                req.write(fmt.listitem(0))
            req.write(fmt.number_list(0))

        if len(self.debug) > 0:
            req.write('DEBUG: %s' % str(self.debug))

        if self.options.getBool('calendarCollapse'):
            req.write('</div>\n');

class Parser:
    """ Forma calendar as a table
    """
    caching = 0
    Dependencies = []

    def __init__(self, raw, request, **kw):
        """ Store the source text.
        """
        self.raw = raw
        self.request = request
        self.form = request.form
        self._ = request.getText
        self.events = {}
        self.optionsItems = {}
        self.options = None
        self.calendars = []
        self.debug = []

    def stringNormalization(self, text):
        text = text.strip()
        data = ''
        blnk = 0
        for p in text:
            if p==' ' or p=='\t':
                if blnk==1:
                    continue
                blnk=1
            blnk = 0
            data += p
        text = data.replace(' = "', '="').replace('=" ', '="').replace(' ="', '="')
        #self.debug.append('TEXT: %s<br>' % text)
        return text

    def stringToItems(self, text):
        items = {}
        type = None
        text = self.stringNormalization(text)
        if len(text)==0:
            return None, None
        chunks = text.split('" ')
        for chunk in chunks:
            chunk = chunk.strip()
            x = chunk.split('=')
            if len(x)!=2:
                continue
            name, value = x
            value = value.strip()
            name = name.strip()
            if value and len(value)>0:
                if value[0] == '"':
                    value = value[1:]
                if value[-1] == '"':
                    value = value[:-1]
            items[name] = value
            if type==None:
                type = name
        return type, items
        
    def parseDate(self, text):
        if text==None or len(text) < 3:
            return None, None, None
        x = text.strip().split('-')
        if len(x) < 3:
            return None, None, None
        return int(x[0]), int(x[1]), int(x[2])

    def appendEvent(self, y, m, d, items):
        if items and len(items) > 0:
            if not self.events.has_key(y):
                self.events[y] = { }
            if not self.events[y].has_key(m):
                self.events[y][m] = { }
            if not self.events[y][m].has_key(d):
                    self.events[y][m][d] = [ ]
            self.events[y][m][d].append(items)

    def parseEvent(self, linetype, items):
        if linetype==None or len(linetype) == 0:
            return
        from_y, from_m, from_d = self.parseDate(linetype)
        if from_y==None:
            return

        # store the fisrt variable as two separate variables
        items['TEXT'] = items[linetype]
        items['FROM'] = linetype
        del items[linetype]

        if items.has_key('TO'):
            # range
            to_y, to_m, to_d = self.parseDate(items['TO'])
            if to_y==None:
                return

            for y in xrange(from_y, to_y+1):
                max_m = to_m
                min_m = from_m
                if y < to_y:
                    max_m = 12
                if y > from_y:
                    min_m = 1
                for m in xrange(min_m, max_m+1):
                    max_d = to_d
                    min_d = from_d
                    if y < to_y or m < max_m:
                        max_d = calendar.monthrange(y, m)[1]
                    if y > from_y or m > from_m:
                        min_d = 1
                    for d in xrange(min_d, max_d+1):
                        self.appendEvent(y, m, d, items)
        else:
            # simple event
            self.appendEvent(from_y, from_m, from_d, items)
        
    def parse(self, raw):
        """ Parse to events or options
        """
        if raw==None or len(raw)==0:
            return

        lines = raw.split('\n')

        for line in lines:
            text = line.strip()
            if len(text) == 0:
                continue
            linetype, items = self.stringToItems(text)
            if items==None:
                continue
            if linetype == 'OPTION':
                name = items['OPTION']
                self.optionsItems[name] = items
            else:
                self.parseEvent(linetype, items)

    def calendarCreate(self):
        y_today = int( time.strftime("%Y"))
        m_today = int( time.strftime("%m"))
        d_today = int( time.strftime("%d"))

        self.options = CalendarOptions(self.optionsItems);

        for y in self.events.keys():
            for m in self.events[y].keys():
                cal = EventCalendar(y, m, self.options, y_today, m_today, d_today)
                for d in self.events[y][m].keys( ):
                    for event in self.events[y][m][d]:
                        cal.addEvent(d,event)
                self.calendars.append((y,m,cal))

        self.calendars.sort()

        if self.options.getValue('calendarsOrder')=='reverse':
            self.calendars.reverse()

    def format(self, formatter):

        self.parse(self.raw)
        self.calendarCreate()

        if len(self.calendars)==0:
            return

        self.request.write('''
            <script language="javascript" type="text/javascript">
              function showCalendar(id)
              {
                 el=document.getElementById(id).style;
                 el.display=(el.display == 'block')?'none':'block';
              }
            </script>\n''')

        for y,m,cal in self.calendars:
            cal.format(self.request, formatter)

        if len(self.debug) > 0:
            self.request.write('DEBUG: %s' % self.debug)

# vim: set tabstop=4:
# vim: set shiftwidth=4:
# vim: set expandtab:

