"""
    EventCalendar.py  Version 0.93  January 18, 2006
                                                                                                           
    This macro gives a list of the events recorded in the sub-pages in the form of monthly view and list view.
                                                                                                           
    @copyright: 2006 by Seungik Lee <seungiklee<at>gmail.com>  http://www.silee.net/
    @license: GPL
    
    For more information, please visit http://moinmoin.wikiwikiweb.de/MacroMarket/EventCalendar
    
    Usage: 
        * To list the events in a page, just insert [[EventCalendar]]
        * To insert an event, insert the event information in any pages of specified category (CategoryEventCalendar by default).
        
        * For example,
        
            [default_description:: [default_description_text]]
            [default_bgcolor:: [default_custom_background_color]]
        
            == <title> ==
             start:: <startdate> [starttime]
             [end:: [enddate] [endtime]]
             [description:: [description_text]]
             [bgcolor:: [custom_background_color]]
             [recur:: <recur_freq> <recur_type> [until <recur_until>]]
            
            ...
            
            ----
            CategoryEventCalendar
            

            * title: event title. required
                * should be enclosed with heading marker ('='), Title cannot be omitted.

            * startdate: date of start. required
                * should be in YYYY/MM/DD or YYYY-MM-DD
                
            * starttime: time of start. optional
                * should be in HH:MM in 24-hour format
                
            * enddate: date of end. optional
                * should be in YYYY/MM/DD or YYYY-MM-DD. If omitted, it will be assigned equal to <startdate>.
                
            * endtime: time of end. optional
                * should be in HH:MM in 24-hour format. Both of start|end Time can be omitted but not either of them.
                
            * description: description of the event. optional
                * any text with no markup. should be in a line.
                
            * bgcolor: custom background color of the event in monthly view. optional
                * e.g., #abcdef
            
            * recur: recurrence information of the event, optional
                * recur_freq: how many intervals, digit, required
                * recur_type: [day|week|weekday|month|year], required
                    * day: every [recur_freq] days
                    * week: every [recur_freq] weeks
                    * weekday: on the same weekday of [recur_freq]-th week of the month
                    * month: on the same day of [recur_freq]-th month
                    * year: on the same day of [recur_freq]-th year
                * recur_until: recurred until when, YYYY/MM/DD or YYYY-MM-DD, optional
                
                * e.g., 10 day, 2 week until 2006-06-31, 3 weekday, 6 month until 2007-12-31, 1 year

            * default_bgcolor, default_description: default values of bgcolor and description in the page if unavailable
            
            * The order of the fields after an event title does not matter.

    Notes:
        * 'MonthCalendar.py' developed by Thomas Waldmann <ThomasWaldmann@gmx.de> has inspired this macro.
        * Much buggy.. : please report bugs and suggest your ideas.
        * If you missed to add css for EventCalender, monthly view may not be readable.
            * Insert the EventCalendar css classes into the screen.css of an appropriate theme.


    Event Data Examples:

= Default values =
 default_bgcolor:: #c0c0c0
 default_description:: testing...

=== Test event ===
 start:: 2006-01-10 14:00
 end:: 2006-01-12 17:00
 description:: test event
 bgcolor:: #cfcfcf
  
=== Jinah's Birthday ===
 start:: 1977-10-20
 recur:: 1 year
 
=== Weekly meeting ===
 start:: 2006-01-17 19:00
 end:: 21:00
 recur:: 1 week until 2006-12-31

----
CategoryEventCalendar  


    Caching-related Information:
        * It caches all the page list of the specified category and the event information.
        * If you added/removed a page into/from a category, you need to do 'Delete cache' in the macro page.
        

"""

from MoinMoin import wikiutil, config, search, caching
from MoinMoin.Page import Page
from MoinMoin.parser import wiki
import re, calendar, time, datetime
import codecs, os, urllib, sha

try:
    import cPickle as pickle
except ImportError:
    import pickle

# Set pickle protocol, see http://docs.python.org/lib/node64.html
PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL


# The following line sets the calendar to have either Sunday or Monday as
# the first day of the week. Only SUNDAY or MONDAY (case sensitive) are
# valid here.  All other values will not make good calendars.
# XXX change here ----------------vvvvvv
calendar.setfirstweekday(calendar.SUNDAY)


class Globs:
    month_style_us = 1  # 1: October 2005; 2: 2005 / 10
    defaultcategory = 'CategoryEventCalendar'
    upcomingrange = 7   # days
    pagename = ''
    baseurl = ''
    subname = ''
    wkend = ''
    months = ''
    wkdays = ''
    today = ''
    request = None
    formatter = None
    cal_action = ''
    debugmsg = ''
    page_action = ''
    events = None


class Params:
    menubar = 0
    monthlywidth = ''
    simplewidth = ''
    firstview = ''
    curdate = ''
    bgcolor = ''
    category = ''
    upcomingrange = 0
    debug = 0
    

def execute(macro, args):
    
    request = macro.request
    formatter = macro.formatter
    
    # INITIALIZATION ----------------------------------------
    setglobalvalues(macro)
    getparams(args)
    
    # allowed actions
    allowed_action = ['monthly', 'list', 'simple', 'upcoming']
    default_action = Params.firstview
    
    # Internal variables
    cal_action = ''
    form_vals = {}

    # PROCESSING ARGUEMENTS ----------------------------------------
    if args:
        args=request.getText(args)

    for item in macro.form.items():
        if not form_vals.has_key(item[0]):
	    try:
		    form_vals[item[0]]=item[1][0]
	    except AttributeError:
	        pass
    
    # PROCESSING ACTIONS ----------------------------------------
    cal_action = form_vals.get('calaction', default_action)
    page_action = form_vals.get('action', 'show')
    
    if not cal_action in allowed_action:
        cal_action = default_action
    
    form_vals['calaction'] = cal_action

    # CONTROL FUNCTIONS ----------------------------------------
    
    html = []
    html_result = ''
    
    Globs.cal_action = cal_action
    Globs.page_action = page_action
    
    
    # redirect to the appropriate view
    if cal_action == 'monthly':
        html_result = showcalendar(form_vals)
        
    if cal_action == 'list':
        html_result = showeventlist(form_vals)

    if cal_action == 'simple':
        html_result = showsimplecalendar(form_vals)
    
    if cal_action == 'upcoming':
        html_result = showupcomingeventlist(form_vals)
    
    
    # format output
    html.append( html_result )
    html.append( showmenubar(form_vals) )
    
    if Params.debug and Globs.debugmsg:
        html.append(u'<p><b>Debug messages:</b><font color="#aa0000"><ol>%s</ol></font>' % Globs.debugmsg)
        
    return formatter.rawHTML(u''.join(html))



def getparams(args):
    # process arguments
    
    params = {}
    if args:
        # Arguments are comma delimited key=value pairs
        sargs = args.split(',')
    
        for item in sargs:
            sitem = item.split('=')
        
            if len(sitem) == 2:
                key, value = sitem[0], sitem[1]
                params[key.strip()] = value.strip()

    # category name: 
    # default: 'CategoryEventCalendar'
    Params.category = params.get('category', Globs.defaultcategory)
    
    # menu bar: shows menubar or not (1: show, 0: no menubar)
    # default: 1
    try:
        Params.menubar = int(params.get('menubar', 1))
    except (TypeError, ValueError):
        Params.menubar = 1
        
    # calendar width in pixel or percent (monthly)
    # default: 600px
    Params.monthlywidth = params.get('monthlywidth', '600')
    if Params.monthlywidth:
        # Params.monthlywidth = Params.monthlywidth.replace('%', '%%')
        Params.monthlywidth = ' width="%s" ' % Params.monthlywidth
        
    # calendar width in pixel or percent (simply)
    # default: 150px
    Params.simplewidth = params.get('simplewidth', '150')
    if Params.simplewidth:
        # Params.simplewidth = Params.simplewidth.replace('%', '%%')
        Params.simplewidth = ' width="%s" ' % Params.simplewidth
    
    # calendar view: monthly, list, simple
    # default: 'monthly'
    Params.firstview = params.get('firstview', 'monthly')

    # calendar date: in YYYYMM format (in monthly, simple view)
    # default: current month
    Params.curdate = params.get('curdate', '')
    
    # upcoming range: # of days for upcoming event list
    # default: 7
    try:
        Params.upcomingrange = int(params.get('upcomingrange', Globs.upcomingrange))
    except (TypeError, ValueError):
        Params.upcomingrange = Globs.upcomingrange
        
    # default bgcolor
    Params.bgcolor = '#ddffdd'
    

def setglobalvalues(macro):
    
    request = macro.request
    formatter = macro.formatter
    
    # Useful variables
    Globs.baseurl = request.getBaseURL() + '/'
    Globs.pagename = formatter.page.page_name
    Globs.request = request
    Globs.formatter = formatter
    
    # This fixes the subpages bug. subname is now used instead of pagename when creating certain urls
    Globs.subname = Globs.pagename.split('/')[-1]

    pagepath = formatter.page.getPagePath()
    Globs.pagepath = formatter.page.getPagePath()
    
    # european / US differences
    months = ('January','February','March','April','May','June','July','August','September','October','November','December')
    
    # Set things up for Monday or Sunday as the first day of the week
    if calendar.firstweekday() == calendar.MONDAY:
        wkend = 6
        wkdays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
    elif calendar.firstweekday() == calendar.SUNDAY:
        wkend = 0
        wkdays = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
    
    Globs.months = months
    Globs.wkdays = wkdays
    Globs.wkend = wkend

    year, month, day, h, m, s, wd, yd, ds = request.user.getTime(time.time())
    Globs.today = datetime.date(year, month, day)
    
    Globs.debugmsg = ''


def showReferPageParsed(event, targettext='title', showdesc=0):
    request = Globs.request
    pagename = Globs.pagename
    
    refer = event['refer']
    targettext = event[targettext]
    startdate = event['startdate']
    enddate = event['enddate']
    description = event['description']
    starttime = event['starttime']
    endtime = event['endtime']
    hid = event['hid']
    
    if showdesc:
        if (startdate == enddate) and (starttime and endtime):
            timedescription = '%s:%s ~ %s:%s' % (starttime[:2], starttime[2:], endtime[:2], endtime[2:])
            if description:
                timedescription = '%s | ' % timedescription
        else:
            timedescription = ''
        
        targetlink = '<a href="%s#%s" title="%s%s">%s</a>' % ( refer, hid, timedescription, wikiutil.escape(description), wikiutil.escape(targettext) )
    else:
        targetlink = '<a href="%s#%s">%s</a>' % ( refer, hid, wikiutil.escape(targettext))
    
    return targetlink


def getheadingid(request, referpage, title):

    pntt = (referpage + title).encode(config.charset)
    hid = "head-" + sha.new(pntt).hexdigest()
    request._page_headings.setdefault(pntt, 0)
    request._page_headings[pntt] += 1
    if request._page_headings[pntt] > 1:
        hid += '-%d'%(request._page_headings[pntt],)
    
    return hid


def getquerystring(form_vals, req_fields):
    
    m_query = []
    tmp_form_vals = form_vals
    
    # format querystring
    # action should be poped out
    for field in req_fields:
        if tmp_form_vals.has_key(field):
            m_query.append(u'%s=%s' % (field, tmp_form_vals[field]) )
    
    if 'prevcalaction' in req_fields:
        if not tmp_form_vals.has_key('prevcalaction'):
            m_query.append(u'%s=%s' % ('prevcalaction', tmp_form_vals['calaction']) )
            
    m_query = u'&'.join(m_query)
    return m_query


# bottom menu bar
def showmenubar(form_vals):
    
    cal_action = Globs.cal_action
    page_name = Globs.pagename
    
    if not Params.menubar: return ''

    if cal_action == 'simple':
        menuwidth = Params.simplewidth
    elif cal_action == 'monthly':
        menuwidth = Params.monthlywidth
    else:
        menuwidth = ''
    
    left_menu_selected = []
    right_menu_selected = []
    
    # Go Today
    year, month, day = gettodaydate()
    mnu_curmonthcal = u'<a href="%s?calaction=%s&caldate=%d%02d" title="Go Today">[Today]</a>' % (page_name, cal_action, year, month)
    
    # List View
    mnu_listview = u'<a href="%s?calaction=list" title="List of all events">[List]</a>' % page_name
    
    # Monthly View
    mnu_monthview = u'<a href="%s?calaction=monthly&%s" title="Monthly view">[Monthly]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
    
    # Simple Calendar View
    mnu_simpleview = u'<a href="%s?calaction=simple&%s" title="Simple calendar view">[Simple]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
    
    # Upcoming Event List
    mnu_upcomingview = u'<a href="%s?calaction=upcoming&%s" title="Upcoming event list">[Upcoming]</a>' % (page_name, getquerystring(form_vals, ['caldate']) )
    
    html = [
        u'\r\n',
        u'<table class="eventcalendar_menubar" %s>',
        u'  <tr>',
        u'  <td class="eventcalendar_menubar" align="left">%s</td>',
        u'  <td class="eventcalendar_menubar" align="right">%s</td>',
        u'  </tr>',
        u'</table>',
        ]
        
    if cal_action == 'list':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_simpleview)
        right_menu_selected.append(mnu_upcomingview)

    elif cal_action == 'simple':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_listview)
        right_menu_selected.append(mnu_upcomingview)
        right_menu_selected.append(mnu_curmonthcal)
    
    elif cal_action == 'upcoming':
        left_menu_selected.append(mnu_monthview)
        left_menu_selected.append(mnu_simpleview)
        left_menu_selected.append(mnu_listview)
        
    else:
        left_menu_selected.append(mnu_listview)
        left_menu_selected.append(mnu_simpleview)
        right_menu_selected.append(mnu_upcomingview)
        right_menu_selected.append(mnu_curmonthcal)

    left_menu_selected = u'\r\n'.join(left_menu_selected)
    right_menu_selected = u'\r\n'.join(right_menu_selected)
    
    html = u'\r\n'.join(html)
    html = html % (menuwidth, left_menu_selected, right_menu_selected)

    return html
        

def getdatefield(str_date):
    str_year = ''
    str_month = ''
    str_day = ''
    
    if len(str_date) == 6:
        # year+month
        str_year = str_date[:4]
        str_month = str_date[4:]
        str_day = '1'

    elif len(str_date) == 8:
        # year+month+day
        str_year = str_date[:4]
        str_month = str_date[4:6]
        str_day = str_date[6:]
    
    elif len(str_date) == 10:
        # year+?+month+?+day
        str_year = str_date[:4]
        str_month = str_date[5:7]
        str_day = str_date[8:]
    
    else:
        raise ValueError
    
    # It raises exception if the input date is incorrect
    temp = datetime.date(int(str_year), int(str_month), int(str_day))

    return temp.year, temp.month, temp.day


def gettimefield(str_time):
    str_hour = ''
    str_min = ''
    
    if len(str_time) == 4:
        # hour+minute
        str_hour = str_time[:2]
        str_min = str_time[2:]
    
    elif len(str_time) == 5:
        # hour+?+minute
        str_hour = str_time[:2]
        str_min = str_time[3:]
        
    else:
        raise ValueError
    
    # It raises exception if the input date is incorrect
    temp = datetime.time(int(str_hour), int(str_min))

    return temp.hour, temp.minute


def gettodaydate():
    today = Globs.today
    return today.year, today.month, today.day    


def cal_listhead():

    html = [
        u'  <tr>',
        u'      <td class="list_head">Title</td>',
        u'      <td class="list_head">Start Date</td>',
        u'      <td class="list_head">End Date</td>',
        u'      <td class="list_head">Recurrence</td>',
        u'      <td class="list_head">Description</td>',
        u'      <td class="list_head">Reference</td>',
        u'  </tr>',
        ]
        
    return u'\r\n'.join(html)


def showeventlist(form_vals):
    
    debug('Show Calendar: List view')
    
    request = Globs.request
    formatter = Globs.formatter
    
    html_event_rows = []
    html_list_header = cal_listhead()
    
    # read all the events
    events, cal_events = loadEvents()
    
    # sort events
    sorted_eventids = events.keys()
    sorted_eventids.sort(cmp=lambda x,y: cmp(events[x]['startdate'], events[y]['startdate']))
    
    for eid in sorted_eventids:
        if not events[eid]['clone']:
            html_event_rows.append( listshow_event(events[eid], form_vals) )
    
    html_event_rows = u'\r\n'.join(html_event_rows)
    
    html_list_table = [
        u'\r\n<div id="eventlist">',
        u'<table class="eventlist">',
        u'%s' % html_list_header,
        u'%s' % html_event_rows,
        u'</table>',
        u'</div>',
        ]
    html_list_table = u'\r\n'.join(html_list_table)
        
    return html_list_table


def listshow_event(event, form_vals):
    
    if event['recur_freq']:
        recur_desc = 'every %d %s' % (event['recur_freq'], event['recur_type'])
        if event['recur_until']:
             recur_desc = '%s until %s' % (recur_desc, formatcfgdatetime(event['recur_until']))
    else:
        recur_desc = ''

    html = [
        u'  <tr>',
        u'  <td class="list_entry">%s</td>' % converttext(event['title']),
        u'  <td class="list_entry">%s</td>' % formatcfgdatetime(event['startdate'], event['starttime']),
        u'  <td class="list_entry">%s</td>' % formatcfgdatetime(event['enddate'], event['endtime']),
        u'  <td class="list_entry">%s</td>' % recur_desc,
        u'  <td class="list_entry">%s</td>' % converttext(event['description']),
        u'  <td class="list_entry">%s</td>' % showReferPageParsed(event, 'refer'),
        u'  </tr>',
        ]
    
    return u'\r\n'.join(html)


def showupcomingeventlist(form_vals):
    
    debug('Show Calendar: Upcoming Event View')
    
    request = Globs.request
    formatter = Globs.formatter
    
    html_event_rows = []
    html_list_header = cal_listhead()
    
    year, month, day = gettodaydate()
    day_delta = datetime.timedelta(days=Params.upcomingrange)
    cur_date = datetime.date(year, month, day)
    next_range = cur_date + day_delta
    
    # set ranges of events
    datefrom = u'%04d%02d%02d' % (year, month, day)
    dateto = u'%04d%02d%02d' % (next_range.year, next_range.month, next_range.day)
    
    # read all the events (no cache)
    events, cal_events = loadEvents(datefrom, dateto, 1)
    
    datefrom = u'%04d-%02d-%02d' % (year, month, day)
    dateto = u'%04d-%02d-%02d' % (next_range.year, next_range.month, next_range.day)
    
    # sort events
    sorted_eventids = events.keys()
    sorted_eventids.sort(cmp=lambda x,y: cmp(events[x]['startdate'], events[y]['startdate']))
    
    for eid in sorted_eventids:
        html_event_rows.append( listshow_event(events[eid], form_vals) )
    
    html_event_rows = u'\r\n'.join(html_event_rows)
    
    html_list_table = [
        u'\r\n<div id="eventlist">',
        u'<table class="eventlist">',
        u'<tr><td colspan="7" class="list_entry" style="border-width: 0px;"><b>Upcoming Event List: %s ~ %s</b><p><br><p></td></tr>' % (datefrom, dateto),
        u'%s' % html_list_header,
        u'%s' % html_event_rows,
        u'</table>',
        u'</div>',
        ]
    html_list_table = u'\r\n'.join(html_list_table)
        
    return html_list_table




def showcalendar(form_vals):
    
    request = Globs.request
    formatter = Globs.formatter
    
    if form_vals.has_key('caldate'):
        try:
            year, month, str_temp = getdatefield(form_vals['caldate'])
        except (TypeError, ValueError):
            debug('Invalid target date: e.g., "200510"')
            year, month, dy = gettodaydate()
    elif Params.curdate:
        try:
            year, month, str_temp = getdatefield(Params.curdate)
        except (TypeError, ValueError):
            debug('Invalid target date: e.g., "200510"')
            year, month, dy = gettodaydate()
            
    else:
        year, month, dy = gettodaydate()
    
    html = showeventcalendar(year, month)
    
    return u''.join(html)



    
def showsimplecalendar(form_vals):
    
    request = Globs.request
    formatter = Globs.formatter
    
    if form_vals.has_key('caldate'):
        try:
            year, month, str_temp = getdatefield(form_vals['caldate'])
        except (TypeError, ValueError):
            debug('Invalid target date: e.g., "200510"')
            year, month, dy = gettodaydate()
    elif Params.curdate:
        try:
            year, month, str_temp = getdatefield(Params.curdate)
        except (TypeError, ValueError):
            debug('Invalid target date: e.g., "200510"')
            year, month, dy = gettodaydate()
    else:
        year, month, dy = gettodaydate()
    
    html = showsimpleeventcalendar(year, month)
    
    return u''.join(html)



# sort events in cal_events by length of days of the event
def comp_cal_events(xid, yid):
    events = Globs.events
    
    if events[xid]['date_len'] > events[yid]['date_len']:
        return -1
    elif events[xid]['date_len'] == events[yid]['date_len']:
        if events[xid]['date_len'] == 1:
            return cmp(events[xid]['starttime'], events[yid]['starttime'])
        else:
            return 0
    else:
        return 1


# load events from wiki pages
def loadEvents(datefrom='', dateto='', nocache=0):
    
    request = Globs.request
    
    debug('Loading event information.')
    
    events = {}
    cal_events = {}
    raw_events = loadEventsFromWikiPages()
    
    # handling cal_events
    if datefrom or dateto:
        
        # cache configurations
        arena = Page(request, Globs.pagename)
        eventkey = 'events'
        filteredeventkey = 'events_%s-%s' % (datefrom, dateto)
        caleventkey = 'calevents_%s-%s' % (datefrom, dateto)
        
        cache_events = caching.CacheEntry(request, arena, eventkey)
        cache_filteredevents = caching.CacheEntry(request, arena, filteredeventkey)
        cache_calevents = caching.CacheEntry(request, arena, caleventkey)
        
        dirty = 1
        
        debug('Checking cal_events cache')
        
        if not (cache_calevents.needsUpdate(cache_events._filename()) or cache_filteredevents.needsUpdate(cache_events._filename())):
        
            try:
                events = pickle.loads(cache_filteredevents.content())
                cal_events = pickle.loads(cache_calevents.content())
                debug('Cached event (filtered) information is used: total %d events' % len(events))
                dirty = 0
            except (pickle.UnpicklingError, IOError, EOFError, ValueError):
                debug('Picke error at fetching cached events (filtered)')
                events = {}
                cal_events = {}


        # if cache is dirty, update the cache
        if dirty:
        
            debug('Checking event cache: it\'s dirty or requested to refresh')
            debug('Building new cal_event information')
        
            try:
                datefrom, dateto = int(datefrom), int(dateto)
            except (TypeError, ValueError):
                datefrom, dateto = 0, 0
        
            clone_num = 0
            
            for e_id in raw_events.keys():
                
                cur_event = raw_events[e_id]
                
                # handling event recurrence
                recur_freq = cur_event['recur_freq']
                
                if recur_freq:
                    
                    if not (cur_event['recur_until'] and int(cur_event['recur_until']) < datefrom) or int(cur_event['startdate']) > dateto:

                        # generating cal_events for iteself
                        events[e_id] = cur_event.copy()
                        insertcalevents(cal_events, datefrom, dateto, e_id, cur_event['startdate'], cur_event['enddate'])
                        
                        delta_date_len = datetime.timedelta(days = int(cur_event['date_len']) - 1 )
                        
                        if cur_event['recur_type'] == 'day':
                        
                            day_delta = int(recur_freq)
                            startdate = getdatetimefromstring(cur_event['startdate'])
                            datefrom_date = getdatetimefromstring(datefrom)
                            
                            if int(datefrom) > int(cur_event['startdate']):
                                diffs = datefrom_date - startdate
                                q_delta = diffs.days / day_delta
                                if diffs.days % day_delta > 0:
                                    q_delta += 1
                            else:
                                q_delta = 1
                            
                            while 1:
                                
                                if q_delta == 0:
                                    q_delta += 1
                                    continue
                                
                                recurred_startdate = startdate + datetime.timedelta(days = q_delta * day_delta )
                                recurred_enddate = recurred_startdate + delta_date_len
                                
                                new_startdate = formatdateobject(recurred_startdate)
                                new_enddate = formatdateobject(recurred_enddate)
                                
                                if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
                                    break
                                
                                clone_num += 1
                                clone_id = 'c%d' % clone_num
                                
                                events[clone_id] = cur_event.copy()
                                events[clone_id]['id'] = clone_id
                                events[clone_id]['startdate'] = new_startdate
                                events[clone_id]['enddate'] = new_enddate
                                events[clone_id]['clone'] = 1
                                
                                insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
                                
                                q_delta += 1
                        
                        elif cur_event['recur_type'] == 'week':
                            
                            day_delta = int(recur_freq) * 7
                            
                            startdate = getdatetimefromstring(cur_event['startdate'])
                            datefrom_date = getdatetimefromstring(datefrom)
                            
                            if int(datefrom) > int(cur_event['startdate']):
                                diffs = datefrom_date - startdate
                                q_delta = diffs.days / day_delta
                                if diffs.days % day_delta > 0:
                                    q_delta += 1
                            else:
                                q_delta = 1
                            
                            while 1:
                                
                                if q_delta == 0:
                                    q_delta += 1
                                    continue
                                
                                recurred_startdate = startdate + datetime.timedelta(days = q_delta * day_delta )
                                recurred_enddate = recurred_startdate + delta_date_len
                                
                                new_startdate = formatdateobject(recurred_startdate)
                                new_enddate = formatdateobject(recurred_enddate)
                                
                                if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
                                    break
                                
                                clone_num += 1
                                clone_id = 'c%d' % clone_num
                                
                                events[clone_id] = cur_event.copy()
                                events[clone_id]['id'] = clone_id
                                events[clone_id]['startdate'] = new_startdate
                                events[clone_id]['enddate'] = new_enddate
                                events[clone_id]['clone'] = 1
                                
                                insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
                                
                                q_delta += 1
                        
                        
                        elif cur_event['recur_type'] == 'weekday':
                            
                            syear, smonth, sday = getdatefield(cur_event['startdate'])
                            cyear, cmonth, cday = getdatefield(str(datefrom))
                            
                            recur_weekday = calendar.weekday(syear, smonth, sday)
                            
                            
                            while 1:
                                
                                firstweekday, daysinmonth = calendar.monthrange(cyear, cmonth)
                                firstmatch = (recur_weekday - firstweekday) % 7 + 1
                                
                                #XXX should handle error
                                try:
                                    therecur_day = xrange(firstmatch, daysinmonth + 1, 7)[recur_freq-1]
                                except IndexError:
                                    therecur_day = xrange(firstmatch, daysinmonth + 1, 7)[-1]
                                
                                recurred_startdate = datetime.date(cyear, cmonth, therecur_day)
                                recurred_enddate = recurred_startdate + delta_date_len
                                
                                new_startdate = formatdateobject(recurred_startdate)
                                new_enddate = formatdateobject(recurred_enddate)
                                
                                if int(new_startdate) < int(datefrom):
                                    cyear, cmonth = yearmonthplusoffset(cyear, cmonth, 1)
                                    continue
                                
                                if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
                                    break
                                
                                clone_num += 1
                                clone_id = 'c%d' % clone_num
                                
                                events[clone_id] = cur_event.copy()
                                events[clone_id]['id'] = clone_id
                                events[clone_id]['startdate'] = new_startdate
                                events[clone_id]['enddate'] = new_enddate
                                events[clone_id]['clone'] = 1
    
                                insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
                                
                                cyear, cmonth = yearmonthplusoffset(cyear, cmonth, 1)
                                
                        
                        elif cur_event['recur_type'] == 'month':
                            
                            cyear, cmonth, therecurday = getdatefield(cur_event['startdate'])
                            
                            while 1:
                                
                                cyear, cmonth = yearmonthplusoffset(cyear, cmonth, recur_freq)
                                firstweekday, daysinmonth = calendar.monthrange(cyear, cmonth)
                                recur_day = therecurday 
                                if daysinmonth < recur_day:
                                    recur_day = daysinmonth
                                new_startdate = formatDate(cyear, cmonth, recur_day)
                                
                                if int(new_startdate) < int(datefrom):
                                    continue
                                
                                recurred_startdate = datetime.date(cyear, cmonth, recur_day)
                                recurred_enddate = recurred_startdate + delta_date_len
                                
                                new_startdate = formatdateobject(recurred_startdate)
                                new_enddate = formatdateobject(recurred_enddate)
                                
                                if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
                                    break
                                
                                clone_num += 1
                                clone_id = 'c%d' % clone_num
                                
                                events[clone_id] = cur_event.copy()
                                events[clone_id]['id'] = clone_id
                                events[clone_id]['startdate'] = new_startdate
                                events[clone_id]['enddate'] = new_enddate
                                events[clone_id]['clone'] = 1
    
                                insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
                        
                        elif cur_event['recur_type'] == 'year':
                            
                            ryear, rmonth, rday = getdatefield(cur_event['startdate'])
                            cyear, cmonth, cday = getdatefield(str(datefrom))
                            
                            while 1:
                                
                                ryear += recur_freq
                                new_startdate = formatDate(ryear, rmonth, rday)
                                
                                if int(new_startdate) < int(datefrom):
                                    continue
                                    
                                if int(new_startdate) > dateto or (cur_event['recur_until'] and int(cur_event['recur_until']) < int(new_startdate)):
                                    break
                                
                                recurred_startdate = datetime.date(ryear, rmonth, rday)
                                recurred_enddate = recurred_startdate + delta_date_len
                                
                                new_startdate = formatdateobject(recurred_startdate)
                                new_enddate = formatdateobject(recurred_enddate)
                                
                                clone_num += 1
                                clone_id = 'c%d' % clone_num
                                
                                events[clone_id] = cur_event.copy()
                                events[clone_id]['id'] = clone_id
                                events[clone_id]['startdate'] = new_startdate
                                events[clone_id]['enddate'] = new_enddate
                                events[clone_id]['clone'] = 1
    
                                insertcalevents(cal_events, datefrom, dateto, clone_id, new_startdate, new_enddate)
                
                else:
                    
                    if not (int(cur_event['enddate']) < datefrom or int(cur_event['startdate']) > dateto):
                        events[e_id] = cur_event.copy()
                        insertcalevents(cal_events, datefrom, dateto, e_id, cur_event['startdate'], cur_event['enddate'])
            
            # cache update
            if not nocache:
                cache_filteredevents.update(pickle.dumps(events, PICKLE_PROTOCOL))
                cache_calevents.update(pickle.dumps(cal_events, PICKLE_PROTOCOL))
        
    else:
        events = raw_events
            
        
        # sort cal_events
        # store event list into global variables in order to sort them
        Globs.events = events
    
        for eachdate in cal_events.keys():
            cal_events[eachdate].sort(comp_cal_events)

    debug(u'Total %d of events are loaded finally.' % len(events))
    
    #debug('Events:')
    #for key in events.keys():
    #    debug('__ %s' % events[key])
    
    return events, cal_events



def loadEventsFromWikiPages():
    
    events = {}
    
    eventrecord_list = []
    eventpages = []
    
    request = Globs.request
    category = Params.category
    
    # cache configurations
    arena = Page(request, Globs.pagename)
    eventkey = 'events'
    pagelistkey = 'eventpages'
    
    cache_events = caching.CacheEntry(request, arena, eventkey)
    cache_pages = caching.CacheEntry(request, arena, pagelistkey)
    
    
    # page list cache

    debug('Checking page list cache')
    
    # check the time at which page list cache has been created
    
    cp_mtime = cache_pages.mtime()
    timedelta_days = 9999
    
    if cp_mtime:
        cp_date = datetime.datetime.fromtimestamp(cp_mtime)
        today = datetime.datetime.fromtimestamp(time.time())
        datediff = today - cp_date
        timedelta_days = datediff.days
        debug('Time from page list cache built = %s' % datediff)

    
    if Globs.page_action == 'refresh' or cache_pages.needsUpdate(arena._text_filename()) or timedelta_days >= 1:
        categorypages = searchPages(request, category)
        for page in categorypages:
            eventpages.append(page.page_name)
        cache_pages.update('\n'.join(eventpages), True)
        debug('New page list is built: %d pages' % len(eventpages))
    else:
        eventpages = cache_pages.content(True).split('\n')
        debug('Cached page list is used: %d pages' % len(eventpages))
    

    # generating events
    
    e_num = 0
    dirty = 0
    debug_records = {}
    
    # fetch event records from each page in the category
    for page_name in eventpages:
        
        p = Page(request, page_name)
        e_ref = page_name
        
        eventrecordkey = 'eventrecords'
        cache_eventrecords = caching.CacheEntry(request, p, eventrecordkey)
        
        if cache_eventrecords.needsUpdate(p._text_filename()) or Globs.page_action == 'refresh':
            dirty = 1
            page_content = p.get_raw_body()
            eventrecords, e_num = getEventRecordFromPage(page_content, e_ref, e_num)
            debug_records[e_ref] = '%d eventrecords are fetched from %s' % (len(eventrecords), e_ref)
            cache_eventrecords.update(pickle.dumps(eventrecords, PICKLE_PROTOCOL))
        else:
            try:
                eventrecords = pickle.loads(cache_eventrecords.content())
                debug_records[e_ref] = '%d cached eventrecords are used from %s' % (len(eventrecords), e_ref)
            except (pickle.UnpicklingError, IOError, EOFError, ValueError):
                dirty = 1
                page_content = p.get_raw_body()
                eventrecords, e_num = getEventRecordFromPage(page_content, e_ref, e_num)
                debug_records[e_ref] = '%d eventrecords are fetched from %s due to pickle error' % (len(eventrecords), e_ref)
                cache_eventrecords.update(pickle.dumps(eventrecords, PICKLE_PROTOCOL))

        eventrecord_list.append(eventrecords)

    # if no dirty, just fetch the cache
    if not (dirty or Globs.page_action == 'refresh'):
        
        debug('Checking event cache: still valid')
        
        try:
            events = pickle.loads(cache_events.content())
            debug('Cached event information is used: total %d events' % len(events))
        except (pickle.UnpicklingError, IOError, EOFError, ValueError):
            debug('Picke error at fetching cached events')
            events = {}

    else:
        
        debug('Checking event cache: it\'s dirty or requested to refresh')
    
    # if there is no events (if it needs refreshed), generate events dictionary
    if not len(events.keys()):

        # XXX: just debugging
        debug('Bulding new event information')
        for page_name in eventpages:
            debug(debug_records[page_name])

            
        day_delta = datetime.timedelta(days=1)
        
        for eventrecords in eventrecord_list:
        
            for evtrecord in eventrecords:
                
                e_id = evtrecord['id']
                
                # generating events
                events[e_id] = evtrecord
                #debug('event %s: %s' % (evtrecord['id'], evtrecord))

        # after generating updated events, update the cache
        cache_events.update(pickle.dumps(events, PICKLE_PROTOCOL))
        
        debug('Event information is newly built: total %d events' % len(events))

    # end of updating events block    
    
    
    return events
    


def getEventRecordFromPage(pagecontent, referpage, e_num):
    
    request = Globs.request
    
    eventrecords = []
    page_bgcolor = ''
    page_description = ''
    
    # fetch the page default bgcolor
    regex_page_bgcolor = r"""
^\s+default_bgcolor::\s*
(?P<pagebgcolor>\#[0-9a-fA-F]{6})
\s*
$
"""

    pattern = re.compile(regex_page_bgcolor, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    match = pattern.search(pagecontent)
        
    if match:
        page_bgcolor = match.group('pagebgcolor')

    # fetch the page default description
    regex_page_description = r"""
^\s+default_description::\s*
(?P<pagedescription>.*?)
$
"""
   
    pattern = re.compile(regex_page_description, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    match = pattern.search(pagecontent)
        
    if match:
        page_description = match.group('pagedescription')
    

    # fetch event item
    regex_eventitem = r"""
(?P<eventitem>
	(?P<heading>^\s*(?P<hmarker>=+)\s(?P<eventtitle>.*?)\s(?P=hmarker) $)
	(?P<eventdetail>.*?
		(?=
			^\s*(?P<nexthmarker>=+)\s(?P<nexteventtitle>.*?)\s(?P=nexthmarker) $
			| \Z
        )
	)
)
"""
    
    pattern = re.compile(regex_eventitem, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    match = pattern.findall(pagecontent)
        
    if match:
        
        for matchitem in match:
            
            eventitem = {}
            
            eventtitle = matchitem[3]
            eventdetail = matchitem[4]
            
            try:
                e_start_date, e_start_time, e_end_date, e_end_time, e_bgcolor, e_description, e_recur_freq, e_recur_type, e_recur_until = geteventfield(eventdetail)
            except (TypeError, ValueError):
                #debug('An event data is corrupted: invalid event format')
                continue
            
            # set default values
            if not e_bgcolor:
                e_bgcolor = page_bgcolor
                
            if not e_description:
                e_description = page_description
            
            e_num += 1
            e_id = 'e%d' % e_num
            
            eventitem['id'] = e_id
            eventitem['title'] = eventtitle
            eventitem['startdate'] = e_start_date
            eventitem['starttime'] = e_start_time
            eventitem['enddate'] = e_end_date
            eventitem['endtime'] = e_end_time
            eventitem['title'] = eventtitle
            eventitem['refer'] = referpage
            eventitem['bgcolor'] = e_bgcolor
            eventitem['description'] = e_description
            eventitem['recur_freq'] = e_recur_freq
            eventitem['recur_type'] = e_recur_type
            eventitem['recur_until'] = e_recur_until
            
            eventitem['date_len'] = diffday(e_start_date, e_end_date) + 1
            eventitem['clone'] = 0
            eventitem['hid'] = getheadingid(request, referpage, eventtitle)
            
            eventrecords.append(eventitem)

    #debug('matched records: %d' % len(match))

    return eventrecords, e_num
    


def geteventfield(detail):
    
    regex_startdate = r"""
^\s+start::\s*
(?P<startdate>\d{4}[/-]\d{2}[/-]\d{2})
\s*
(?P<starttime>\d{2}[:]\d{2})*
\s*
$
"""

    regex_enddate = r"""
^\s+end::\s*
(?P<enddate>\d{4}[/-]\d{2}[/-]\d{2})*
\s*
(?P<endtime>\d{2}[:]\d{2})*
\s*
$
"""

    regex_bgcolor = r"""
^\s+bgcolor::\s*
(?P<bgcolor>\#[0-9a-fA-F]{6})
\s*
$
"""

    regex_description = r"""
^\s+description::\s*
(?P<description>.*?)
$
"""

    regex_recur = r"""
^\s+recur::\s*
(?P<recur_freq>\d+)
\s*
(?P<recur_type>day|week|weekday|month|year)
\s*
(
	until
	\s*
	(?P<recur_until>\d{4}[/-]\d{2}[/-]\d{2})
)*
$
"""

    # need help on regular expressions for more efficient/flexible form
    
    # compile regular expression objects
    
    pattern_startdate = re.compile(regex_startdate, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    pattern_enddate = re.compile(regex_enddate, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    pattern_bgcolor = re.compile(regex_bgcolor, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    pattern_description = re.compile(regex_description, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    pattern_recur = re.compile(regex_recur, re.UNICODE + re.MULTILINE + re.IGNORECASE + re.DOTALL + re.VERBOSE)
    
    ##################### retrieve startdate
    match = pattern_startdate.search(detail)
    
    if match:
        startdate = match.group('startdate')
        starttime = match.group('starttime')
    else:
        startdate = ''
        starttime = '' 
    
    ##################### retrieve enddate
    match = pattern_enddate.search(detail)
    
    if match:
        enddate = match.group('enddate')
        endtime = match.group('endtime')
    else:
        enddate = ''
        endtime = ''

    ##################### retrieve bgcolor
    match = pattern_bgcolor.search(detail)
    
    if match:
        bgcolor = match.group('bgcolor')
    else:
        bgcolor = ''
        
    ##################### retrieve description
    match = pattern_description.search(detail)
    
    if match:
        description = match.group('description')
    else:
        description = ''
        
    ##################### retrieve recurrence
    match = pattern_recur.search(detail)
    
    if match:
        recur_freq = int(match.group('recur_freq'))
        recur_type = match.group('recur_type')
        recur_until = match.group('recur_until')
        
    else:
        recur_freq = 0
        recur_type = ''
        recur_until = ''
        

    # check validity of each fields
    
    if not startdate:
        #debug('start date is not specified')
        # self.printoutput('Parse Error', msg, '')
        # ERROR
        return '','','','','','','','','',''
    
    if (starttime or endtime) and not (starttime and endtime):
        #debug('no or 2 time field should be specified')
        # ERROR
        return '','','','','','','','','',''

    # if no time, it's 1-day event
    if not enddate:
        enddate = startdate
    
    try:
        syear, smonth, sday = getdatefield(startdate)
        eyear, emonth, eday = getdatefield(enddate)
    except (TypeError, ValueError):
        #debug('invalid date format: %s, %s' % (startdate, enddate))
        return '','','','','','','','','',''
    
    if datetime.date(syear, smonth, sday) > datetime.date(eyear, emonth, eday):
        #debug('startdate should precede enddate')
        return '','','','','','','','','',''
    
    # format date
    startdate = formatDate(syear, smonth, sday)
    enddate = formatDate(eyear, emonth, eday)
    
    if (starttime and endtime):
        try:
            shour, smin = gettimefield(starttime)
            ehour, emin = gettimefield(endtime)
        except (TypeError, ValueError):
            #debug('invalid time format: %s, %s' % (startdate, enddate))
            return '','','','','','','','','',''
        
        if startdate == enddate:
            if datetime.time(shour, smin) > datetime.time(ehour, emin):
                #debug('starttime should precede endtime')
                return '','','','','','','','','',''
                
        # format time
        starttime = u'%02d%02d' %(shour, smin)
        endtime = u'%02d%02d' %(ehour, emin)
    
    # check recurrent data
    event_len = diffday(startdate, enddate)
    if recur_freq:
        
        if recur_type == 'day':
            if event_len > int(recur_freq):
                debug('event length should be smaller than recurrence interval')
                return '','','','','','','','','',''
        
        elif recur_type == 'week':
            if event_len > int(recur_freq) * 7:
                debug('event length should be smaller than recurrence interval')
                return '','','','','','','','','',''
        
        elif recur_type == 'weekday':
            if event_len > 25:
                debug('event length should be smaller than recurrence interval')
                return '','','','','','','','','',''
        
        elif recur_type == 'month':
            if event_len > int(recur_freq) * 25:
                debug('event length should be smaller than recurrence interval')
                return '','','','','','','','','',''
        
        elif recur_type == 'year':
            if event_len > int(recur_freq) * 365:
                debug('event length should be smaller than recurrence interval')
                return '','','','','','','','','',''
        
        if recur_until:
            try:
                ryear, rmonth, rday = getdatefield(recur_until)
            except (TypeError, ValueError):
                debug('invalid date format: %s' % recur_until)
                return '','','','','','','','','',''
            
            recur_until = formatDate(ryear, rmonth, rday)
            
            if int(recur_until) < int(enddate):
                debug('recur_until should precede enddate')
                return '','','','','','','','','',''
    
    return startdate, starttime, enddate, endtime, bgcolor, description, recur_freq, recur_type, recur_until



def converttext(targettext):
    # Converts some special characters of html to plain-text style
    # What else to handle?

    targettext = targettext.replace(u'&', '&amp')
    targettext = targettext.replace(u'>', '&gt;')
    targettext = targettext.replace(u'<', '&lt;')
    targettext = targettext.replace(u'\n', '<br>')
    targettext = targettext.replace(u'"', '&quot;')
    targettext = targettext.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp')
    targettext = targettext.replace(u'  ', '&nbsp;&nbsp;')
        
    return targettext


# monthly view
def showeventcalendar(year, month):
    
    debug('Show Calendar: Monthly View')
    
    request = Globs.request
    formatter = Globs.formatter
    _ = request.getText
    
    wkend = Globs.wkend
    months= Globs.months
    wkdays = Globs.wkdays
    
    # get the calendar
    monthcal = calendar.monthcalendar(year, month)

    # shows current year & month
    html_header_curyearmonth = calhead_yearmonth(year, month, 'head_yearmonth')
    
    r7 = range(7)
    
    # shows header of week days
    html_header_weekdays = []
    
    for wkday in r7:
        wday = _(wkdays[wkday])
        html_header_weekdays.append( calhead_weekday(wday, 'head_weekday') )
    html_header_weekdays = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_header_weekdays)
 
    # pending events for next row
    next_pending = []
    
    # gets previous, next month
    day_delta = datetime.timedelta(days=-1)
    cur_month = datetime.date(year, month, 1)
    prev_month = cur_month + day_delta
    
    day_delta = datetime.timedelta(days=15)
    cur_month_end = datetime.date(year, month, 25)
    next_month = cur_month_end + day_delta
    
    prev_monthcal = calendar.monthcalendar(prev_month.year, prev_month.month)
    next_monthcal = calendar.monthcalendar(next_month.year, next_month.month)
    
    # shows days
    html_week_rows = []
    
    # set ranges of events
    datefrom = u'%04d%02d21' % (prev_month.year, prev_month.month)
    dateto = u'%04d%02d06' % (next_month.year, next_month.month)
    
    # read all the events
    events, cal_events = loadEvents(datefrom, dateto)
    
    #debug(u'  events: %s' % events)
    #debug(u'  cal_events: %s' % cal_events)
    
    for week in monthcal:
        
        # day head rows
        html_headday_cols = []
        html_events_rows = []
        
        for wkday in r7:
             
            day = week[wkday]
            
            if not day:
                if week == monthcal[0]:
                    nb_day = prev_monthcal[-1][wkday]
                else:
                    nb_day = next_monthcal[0][wkday]
                    
                html_headday_cols.append( calhead_day_nbmonth(nb_day) )
            else:
                html_headday_cols.append( calhead_day(year, month, day, wkday) )
        
        html_headday_row = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_headday_cols)
        html_week_rows.append(html_headday_row)
        
        # dummy rows
        html_headdummy_cols = []
        
        for wkday in r7:
            day = week[wkday]
            if not day:
                html_headdummy_cols.append( calshow_blankbox('head_dummy_nbmonth') )
            else:
                html_headdummy_cols.append( calshow_blankbox('head_dummy') )
        
        html_headdummy_cols = u'\r\n'.join(html_headdummy_cols)
        html_week_rows.append(' <tr>\r\n%s </tr>\r\n' % html_headdummy_cols)
        
        # pending events for next row
        pending = next_pending
        next_pending = []
        
        # show events
        while 1: 
            event_left = 7
            colspan = -1
            html_events_cols = []

            for wkday in r7:
             
                day = week[wkday]
                
                if not day:
                    if week == monthcal[0]:
                        cur_date = formatDate(prev_month.year, prev_month.month, prev_monthcal[-1][wkday])
                    else:
                        cur_date = formatDate(next_month.year, next_month.month, next_monthcal[0][wkday])
                else:
                    cur_date = formatDate(year, month, day)

                # if an event is already displayed with colspan
                if colspan > 0:
                    colspan -= 1
                    if cal_events.has_key(cur_date) and lastevent in cal_events[cur_date]:
                        cal_events[cur_date].remove(lastevent)
                    
                    continue
                    
                # if there is any event for this date
                if cal_events.has_key(cur_date):
                    if len(cal_events[cur_date]) > 0:
                        
                        # if there is any pending event in the previous week
                        if wkday == 0 and len(pending) > 0:
                            todo_event_id = pending.pop(0)
                            if todo_event_id in cal_events[cur_date]:
                                cur_event = events[todo_event_id]
                                temp_len = diffday(cur_date, cur_event['enddate']) + 1
                                
                                # calculate colspan value
                                if (7-wkday) < temp_len:
                                    colspan = 7 - wkday
                                    next_pending.append(cur_event['id'])
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )

                                else:
                                    colspan = temp_len
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
                                
                                
                                cal_events[cur_date].remove(todo_event_id)

                                colspan -= 1
                                lastevent = todo_event_id
                            else:
                                debug('Warning: no such event in cal_events')
                            
                            continue
                        
                        # if there is no pending event in the previous week, start a new event
                        event_found = 0
                        for e_id in cal_events[cur_date]:
                            
                            # if the start date of the event is current date    
                            if events[e_id]['startdate'] == cur_date:
                                
                                cur_event = events[cal_events[cur_date].pop(cal_events[cur_date].index(e_id))]
                                
                                # calculate colspan value
                                if (7-wkday) < cur_event['date_len']:
                                    colspan = 7 - wkday
                                    next_pending.append(cur_event['id'])
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, 'pending', cur_date) )

                                else:
                                    colspan = cur_event['date_len']
                                    html_events_cols.append( calshow_eventbox(cur_event, colspan, '', cur_date) )
                                
                                colspan -= 1
                                lastevent = cur_event['id']
                                event_found = 1
                                break
                            
                            # if the start date of the event is NOT current date
                            else:
                                
                                # pending event from previous month
                                if wkday == 0 and week == monthcal[0]:
                                    
                                    cur_event = events[cal_events[cur_date].pop(0)]
                                    temp_len = diffday(cur_date, cur_event['enddate']) + 1
                                    
                                    # calculate colspan value
                                    if (7-wkday) < temp_len:
                                        colspan = 7 - wkday
                                        next_pending.append(cur_event['id'])
                                        html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append_pending', cur_date) )
                                    else:
                                        colspan = temp_len
                                        html_events_cols.append( calshow_eventbox(cur_event, colspan, 'append', cur_date) )
                                    
                                    colspan -= 1
                                    lastevent = cur_event['id']
                                    event_found = 1
                                    break
                                
                        # if there is no event to start
                        if not event_found:
                            if not day:
                                html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                            else:
                                html_events_cols.append( calshow_blankbox('cal_noevent') )
                            event_left -= 1
                                
                    else:
                        if not day:
                            html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                        else:
                            html_events_cols.append( calshow_blankbox('cal_noevent') )
                        
                        event_left -= 1        
                
                # if there is NO event for this date
                else:
                    if not day:
                        html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                    else:
                        html_events_cols.append( calshow_blankbox('cal_noevent') )
                        
                    event_left -= 1
            
            # if no event for this entry
            if not event_left:
                # ignore the previous entry
                break
            else:
                html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
            
        # show dummy blank slots for week height
        left_blank_rows = 2 - len(html_events_rows)
        
        # remove the followings
        if left_blank_rows > 0 and 0:
            for i in range(left_blank_rows):
                html_events_cols = []
                for wkday in r7:
                    day = week[wkday]
                    if not day:
                        html_events_cols.append( calshow_blankbox('cal_nbmonth') )
                    else:
                        html_events_cols.append( calshow_blankbox('cal_noevent') )
                
                html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
        
        
        # close the week slots
        html_events_cols = []
        for wkday in r7:
            day = week[wkday]
            if not day:
                html_events_cols.append( calshow_blankbox('cal_last_nbmonth') )
            else:
                html_events_cols.append( calshow_blankbox('cal_last_noevent') )
    
        html_events_rows.append(' <tr>\r\n%s </tr>\r\n' % u'\r\n'.join(html_events_cols))
        
        html_events_rows = u'\r\n'.join(html_events_rows)
        html_week_rows.append(html_events_rows)
            
    html_calendar_rows = u'\r\n'.join(html_week_rows)
    
    html_cal_table = [
        u'\r\n<div id="eventcalendar">',
        u'<table class="eventcalendar" %s>' % Params.monthlywidth,
        u'%s' % html_header_curyearmonth,
        u'%s' % html_header_weekdays,
        u'%s' % html_calendar_rows,
        u'</table>',
        u'</div>',
        ]
    html_cal_table = u'\r\n'.join(html_cal_table)
        
    return html_cal_table

# simple view
def showsimpleeventcalendar(year, month):
    
    debug('Show Calendar: Simple View')
    
    request = Globs.request
    formatter = Globs.formatter
    _ = request.getText
    
    wkend = Globs.wkend
    months= Globs.months
    wkdays = Globs.wkdays
    
    # get the calendar
    monthcal = calendar.monthcalendar(year, month)

    # shows current year & month
    html_header_curyearmonth = calhead_yearmonth(year, month, 'simple_yearmonth')
    
    r7 = range(7)
    
    # shows header of week days
    html_header_weekdays = []
    
    for wkday in r7:
        wday = wkdays[wkday]
        html_header_weekdays.append( calhead_weekday(wday, 'simple_weekday') )
    html_header_weekdays = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_header_weekdays)
 
    # gets previous, next month
    day_delta = datetime.timedelta(days=-1)
    cur_month = datetime.date(year, month, 1)
    prev_month = cur_month + day_delta
    
    day_delta = datetime.timedelta(days=15)
    cur_month_end = datetime.date(year, month, 25)
    next_month = cur_month_end + day_delta
    
    prev_monthcal = calendar.monthcalendar(prev_month.year, prev_month.month)
    next_monthcal = calendar.monthcalendar(next_month.year, next_month.month)
    
    # shows days
    html_week_rows = []

    # set ranges of events
    datefrom = u'%04d%02d21' % (prev_month.year, prev_month.month)
    dateto = u'%04d%02d06' % (next_month.year, next_month.month)
    
    # read all the events
    events, cal_events = loadEvents(datefrom, dateto)
    
    for week in monthcal:
        
        # day head rows
        html_headday_cols = []
        html_events_rows = []
        
        for wkday in r7:
             
            day = week[wkday]
            
            if not day:
                if week == monthcal[0]:
                    nb_day = prev_monthcal[-1][wkday]
                else:
                    nb_day = next_monthcal[0][wkday]
                    
                html_headday_cols.append( simple_eventbox(year, month, day, nb_day, 'simple_nb') )
            else:
                cur_date = formatDate(year, month, day)
                if cal_events.has_key(cur_date):
                    html_headday_cols.append( simple_eventbox(year, month, day, wkday, 'simple_event') )
                else:
                    html_headday_cols.append( simple_eventbox(year, month, day, wkday, 'simple_noevent') )
        
        html_headday_row = '    <tr>\r\n%s\r\n</tr>\r\n' % u'\r\n'.join(html_headday_cols)
        html_week_rows.append(html_headday_row)
            
    html_calendar_rows = u'\r\n'.join(html_week_rows)
    
    html_cal_table = [
        u'\r\n<div id="eventcalendar">',
        u'<table class="simplecalendar" %s>' % Params.simplewidth,
        u'%s' % html_header_curyearmonth,
        u'%s' % html_header_weekdays,
        u'%s' % html_calendar_rows,
        u'</table>',
        u'</div>',
        ]
    html_cal_table = u'\r\n'.join(html_cal_table)
        
    return html_cal_table


# show weekday
def calhead_yearmonth(year, month, headclass):
    
    months = Globs.months
    monthstyle_us = Globs.month_style_us
    cal_action = Globs.cal_action
    page_name = Globs.pagename
    
    nextyear, nextmonth = yearmonthplusoffset(year, month, 1)
    prevyear, prevmonth = yearmonthplusoffset(year, month, -1)
    
    prevlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, prevyear, prevmonth)
    nextlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, nextyear, nextmonth)
    curlink = u'%s?calaction=%s&caldate=%d%02d' % (page_name, cal_action, year, month)
    
    if monthstyle_us:
        stryearmonth = u'%s %d' % (months[month-1], year)
        strnextmonth = u'%s %d' % (months[nextmonth-1], nextyear)
        strprevmonth = u'%s %d' % (months[prevmonth-1], prevyear)
    else:
        stryearmonth = u'%d / %02d' % (year, month)
        strnextmonth = u'%d / %02d' % (nextyear, nextmonth)
        strprevmonth = u'%d / %02d' % (prevyear, prevmonth)
    
    html = [
        u'  <tr>',
        u'      <td class="%s"><a href="%s" title="%s">&lt;</a></td>' % (headclass, prevlink, strprevmonth),
        u'      <td colspan="5" class="%s"><a href="%s" title="Refresh">%s</a></td>' % (headclass, curlink, stryearmonth),
        u'      <td class="%s"><a href="%s" title="%s">&gt;</a></td>' % (headclass, nextlink, strnextmonth),
        u'  </tr>',
        ]
        
    return u'\r\n'.join(html)

# show days in simple
def simple_eventbox(year, month, day, wkday, boxclass):
    wkend = Globs.wkend
    if wkday == wkend:
        html_text = u'<font color="#aa7744">%s</font>' % day
    else:
        html_text = u'%s' % day
    
    cyear, cmonth, cday = gettodaydate()
    
    if boxclass == 'simple_nb':
        html = u'  <td class="%s">&nbsp;</td>\r\n' % boxclass
    else:
        if cyear == year and cmonth == month and cday == day:
            html = u'  <td class="%s_today">%s</td>\r\n' % (boxclass, html_text)
        else:
            html = u'  <td class="%s">%s</td>\r\n' % (boxclass, html_text)
       
    return html


# show weekday
def calhead_weekday(wday, headclass):
    if headclass == 'simple_weekday':
        html = u'       <td class="%s">%s</td>\r\n' % (headclass, wday[0])
    else:
        html = u'       <td class="%s">%s</td>\r\n' % (headclass, wday)

    return html


# show days of current month
def calhead_day(year, month, day, wkday):
    wkend = Globs.wkend
    if wkday == wkend:
        html_text = u'<font color="#FF3300">%s</font>' % day
    else:
        html_text = u'%s' % day
    
    cyear, cmonth, cday = gettodaydate()
    
    if cyear == year and cmonth == month and cday == day:
        html = u'  <td class="head_day_today">&nbsp;%s</td>\r\n' % html_text
    else:
        html = u'  <td class="head_day">&nbsp;%s</td>\r\n' % html_text
       
    return html


# show days of previous or next month
def calhead_day_nbmonth(day):
    html = u'  <td class="head_day_nbmonth">&nbsp;%s</td>\r\n' % day
    return html

    
# show blank calendar box
def calshow_blankbox(classname):
    html = u'  <td class="%s">&nbsp;</td>' % classname
    return html

# show eventbox
def calshow_eventbox(event, colspan, status, cur_date):
    if status:
        status = u'_%s' % status
    
    title = event['title']
    eid = event['id']
    startdate = event['startdate']
    enddate = event['enddate']
    starttime = event['starttime']
    endtime = event['endtime']
    description = event['description']
    bgcolor = event['bgcolor']
    
    year, month, day = getdatefield(cur_date)
    
    if bgcolor:
        bgcolor = 'background-color: %s;' % bgcolor
    else:
        bgcolor = 'background-color: %s;' % Params.bgcolor
    
    if (startdate == enddate) and starttime:
        shour, smin = gettimefield(starttime)
        
        link = [
            u'<table width="100%%" style="border-width: 0px; padding: 0px; margin: 0px;"><tr>\r\n',
            u'<td width="10" nowrap style="border-width: 0px; padding: 0px; margin: 0px; text-align: left; vertical-align: top; font-size: 7pt; color: #000000;">%02d:%02d&nbsp;</td>\r\n' % (shour, smin),
            u'<td style="border-width: 0px; padding: 0px; margin: 0px; text-align: left; vertical-align: top;font-size: 8pt;">',
            u'%s' % showReferPageParsed(event, 'title', 1),
            u'</td>\r\n</tr></table>',
            ]
        link = u''.join(link)
    else:
        link = u'%s' % showReferPageParsed(event, 'title', 1)
    
    
    html = [
        u'  <td class="cal_eventbox" colspan="%d"><table class="cal_event">' % colspan,
        u'      <tr><td id="cal_event_%s" class="cal_event%s" style="%s">%s</td></tr>' % (eid, status, bgcolor, link),
        u'      </table></td>',
        ]
        
    return u'\r\n'.join(html)
        

def insertcalevents(cal_events, datefrom, dateto, e_id, e_start_date, e_end_date):
    
    
    if not (int(e_start_date) > dateto or int(e_end_date) < datefrom):
        
        e_start_date = str(max(int(e_start_date), datefrom))
        e_end_date = str(min(int(e_end_date), dateto))
        
        day_delta = datetime.timedelta(days=1)
        e_start_year, e_start_month, e_start_day = getdatefield(e_start_date)
        cur_datetime = datetime.date(e_start_year, e_start_month, e_start_day)
        
        while 1:
            tmp_record_date = formatdateobject(cur_datetime)
            
            if not cal_events.has_key(tmp_record_date):
                cal_events[tmp_record_date] = []
            cal_events[tmp_record_date].append(e_id)
            
            if tmp_record_date == e_end_date:
                break
            
            cur_datetime = cur_datetime + day_delta   


# date format should be like '20051004' for 2005, Oct., 04
def diffday(date1, date2):
    
    try:
        year1, month1, day1 = getdatefield(date1)
        year2, month2, day2 = getdatefield(date2)
        tmp_diff = datetime.date(year2, month2, day2) - datetime.date(year1, month1, day1)
    except (TypeError, ValueError):
        debug('An event data is corrupted: invalid date format')
        return 0

    return tmp_diff.days


def formatDate(year, month, day):
    # returns like: '20051004'
    return u'%4d%02d%02d' % (year, month, day)

def formatdateobject(obj_date):

    return formatDate(obj_date.year, obj_date.month, obj_date.day)


def cliprgb(r,g,b): # don't use 255!
    if r < 0:   r=0
    if r > 254: r=254
    if b < 0:   b=0
    if b > 254: b=254
    if g < 0:   g=0
    if g > 254: g=254
    return r, g, b


def debug(astring):
    Globs.debugmsg += u'<li>%s\n' % astring


def yearmonthplusoffset(year, month, offset):
    month = month+offset
    # handle offset and under/overflows - quick and dirty, yes!
    while month < 1:
        month = month + 12
        year = year - 1
    while month > 12:
        month = month - 12
        year = year + 1
    return year, month


def formatcfgdatetime(strdate, strtime=''):
    
    if not strdate:
        return ''
    
    request = Globs.request
    
    if request.user.date_fmt:
        date_fmt = request.user.date_fmt
    else:
        date_fmt = request.cfg.date_fmt
    
    if request.user.datetime_fmt:
        datetime_fmt = request.user.datetime_fmt
    else:
        datetime_fmt = request.cfg.datetime_fmt
    
    ## XXX HACK
    datetime_fmt = datetime_fmt.replace(':%S', '')
    
    date_fmt = str(date_fmt)
    datetime_fmt = str(datetime_fmt)
    
    year, month, day = getdatefield(str(strdate))
    if strtime:
        hour, min = gettimefield(str(strtime))
        objdatetime = datetime.datetime(year, month, day, hour, min)
        return objdatetime.strftime(datetime_fmt)
    else:
        objdate = getdatetimefromstring(strdate)
        return objdate.strftime(date_fmt)
    

def getdatetimefromstring(strdate):
    year, month, day = getdatefield(str(strdate))
    return datetime.date( year, month, day )


def searchPages(request, needle):
    # Search the pages and return the results
    query = search.QueryParser().parse_query(needle)
    results = search.searchPages(request, query)
    #results.sortByPagename()
    
    return results.hits
    
    html = []
    for page in results.hits:
        html.append(page.page_name)
    
    html = u',<br>'.join(html)
    return u'%s<p>%s' % (Params.category, html)
    
