"""
    eventcal.py  Version 0.80  2005. 10. 31.
                                                                                                           
    This parser gives a parsed format of the event which will be shown at the EventCalendar macro.
                                                                                                           
    @copyright: 2005 by Seungik Lee <seungiklee<at>gmail.com>  http://cds.icu.ac.kr/~silee/
    @license: GPL

    Usage: 
        To insert an event, enclose the information about ONE event record with 'eventcal' parser.
        E.g., 
            {{{#!eventcal
                = [Title] =
                [Start Date] ... [End Date]
                [Start Time] ... [End Time]
            }}}
        
            [Title] should be enclosed with heading marker ('='), double quatation mark ("), wiki italic or bold ('', ''')
                Title can be omitted and it will be titled as 'No Title'
                e.g., == Title ==, === Title ===, "Title", ''Title'', '''Title'''
            
            [Start|End Date] should be in YYYY/MM/DD or YYYY-MM-DD. End date can be omitted.
                e.g, 2005/10/20, 2005-10-20
            
            [Start|End Time] should be in HH:MM in 24-hour format. Both of start|end Time can be omitted but not either of them.
                e.g., 08:00, 12:00, 18:00
            
            In the parsing block of eventcal, it pops out first two date formatted text (startdate and enddate), 
            two time formatted text (starttime, endtime), and quoted or heading titie. 
            
            It ignores further occurrence of the targeted string. 
            The order of each fields (date, time, title) does not matter .
            The start date|time should precede the end date|time in the occurrence respectively.

        There is no input parameter yet.

    Features:
        Shows the event information in a parsed format.
        Validates the date, time format.

    Change Log:
        Oct. 31, 2005 - Version 0.80 
            - The initial version is released.

    To do list:
        Clean the ugly codes!!! (need help)
            raw html codes
        Date/time Format customization (e.g., 2005/10, Oct. 2005, 18:00, 06:00pm)
        Custom background color per event 
        Custom icon per event 
        More input parameters
        Parsing description field

    Notes:
        Much buggy.. :please report bugs and suggest your ideas.
        
    Usage Examples:
    
        {{{#!eventcal
        === Event Calendar Development === # title
         * Duration: 2005/10/20 ~ 2005/10/27 # startdate, enddate
         * It was very hard! # ignored
         * FYI, today's 2005/10/28 # ignored
        }}}

        {{{#!eventcal
        On 2005/10/10 20:00~22:00, we have "3rd Project meeting". Please don't miss it. # startdate, starttime, endtime, title
        }}}
        
        {{{#!eventcal
        '''My Business Trip'''  # title
         From 2005-10-02 to 2005/10/07. See you later~ # startdate, enddate
        }}}
        
        {{{#!eventcal
         Please come to my office on 2005-10-10. Thanks. # (title: 'No Title'), startdate
        }}}


"""

import datetime, re, wiki
import urllib
from MoinMoin import config

class Parser:

    def __init__(self, raw, request, **kw):
        self.lines = raw
        self.request = request

    def format(self, formatter):
        """ Send the "parsed" text.
        """
        
        self.formatter = formatter
        
        # need help on regular expressions for more efficient/flexible form
        regex_date = r'(\d{4}[/-]\d{2}[/-]\d{2})'
        regex_time = r'(\d{2}[:]\d{2})'
        # regex_title = r'["\'=-]+(\s*.*[^"\'=-]+)["\'=-]+'
        # regex_title = r'["]+ | [\']{2,5} | [=-]+ (\s*.*[^"\'=-]+) [=-]+ | ["]+ | [\']{2,5}'
        # regex_title = r'[=-]+|[\']{2,5}(\s*.*[^"\'=-]+)[=-]|[\']{2,5}'
        regex_title = r'["]+(\s*.*?)["]+|[\']{2,}(\s*.*?)[\']{2,}|[=]+(\s*.*?)[=]+'
        
        msg = ''
        title = ''
        startdate = ''
        enddate = ''
        starttime = ''
        endtime = ''
        
        lines = self.lines
        
        # retrieve date
        pattern = re.compile(regex_date, re.UNICODE + re.MULTILINE)
        
        match = pattern.findall(lines)
        
        if not len(match):
            msg = 'at least one date field should be specified'
            self.printoutput('Parse Error', msg, '', '', 0)
            return
        
        # (month, day)-only date should be handled later
        
        startdate = match[0]
        if len(match) > 1:
            enddate = match[1]
        
        # retrieve time
        pattern = re.compile(regex_time, re.UNICODE + re.MULTILINE)
        
        match = pattern.findall(lines)
        
        if len(match) >= 2:
            starttime = match[0]
            endtime = match[1]
        elif len(match) == 0:
            starttime = ''
            endtime = ''
        else:
            msg = 'no or 2 time field should be specified'
            self.printoutput('Parse Error', msg, '', '', 0)
            return
            
        # retrieve title
        pattern = re.compile(regex_title, re.UNICODE + re.MULTILINE + re.DOTALL)
        
        match = pattern.search(lines)
        
        if not match:
            title = 'No title'
        else:
            for item in match.groups():
                if item:
                    title = item
                    break
        
        # if no enddate, it's 1-day event
        if (not enddate) and (starttime and endtime):
            enddate = startdate
        
       
        # check the validity of date/time
        try:
            syear, smonth, sday = getdatefield(startdate)
            if enddate:
                eyear, emonth, eday = getdatefield(enddate)
        except:
            msg = 'invalid date format'
            self.printoutput('Parse Error', msg, '', '', 0)
            return
        
        if startdate and enddate:
            if datetime.date(syear, smonth, sday) > datetime.date(eyear, emonth, eday):
                msg = 'startdate should precede enddate'
                self.printoutput('Parse Error', msg, '', '', 0)
                return

        if starttime and endtime:
            try:
                shour, smin = gettimefield(starttime)
                ehour, emin = gettimefield(endtime)
            except:
                msg = 'invalid time format'
                self.printoutput('Parse Error', msg, '', '', 0)
                return
        
            if startdate == enddate:
                if datetime.time(shour, smin) > datetime.time(ehour, emin):
                    msg = 'starttime should precede endtime'
                    self.printoutput('Parse Error', msg, '', '', 0)
                    return
        
        # format output
        fromstring = '%s %s' % (startdate, starttime)
        if enddate:
            tostring = '%s %s' % (enddate, endtime)
        else:
            tostring = ''
        
        startdateforbookmark = u'%d%02d%02d' % (syear, smonth, sday)
        
        self.printoutput(title, fromstring, tostring, startdateforbookmark, 1)
    
    def printoutput(self, title, fromstring, tostring, startdateforbookmark, successflag):
        
        formatter = self.formatter
        lines = self.lines
        
        if startdateforbookmark:
            bookmark = u'%s%s' % (title.replace(' ', ''), startdateforbookmark)
            # bookmark = urllib.quote_plus(bookmark.encode(config.charset))
            html = u'<a name="%s"></a>' % bookmark
            self.request.write(formatter.rawHTML(html))
        
        html = [
            u'<table width="100%%" style="border-width:0px;"><tr><td width="100%%" style="border-width:0px; text-align: left; vertical-align: top;">',
            u'',
            ]

        html = u'\n'.join(html)
        self.request.write(formatter.rawHTML(html))
            
        wikiparser = wiki.Parser( lines, self.request )
        wikiparser.format( formatter )
        
        html = [
            u'</td>',
            u'<td style="border-width: 0px; padding: 0px; margin: 0px; border-top-width: 1px; width: 4px;">&nbsp;</td>',
            # u'<table width="100%%" height="100%%" style="padding: 0px; margin: 0px;">',
            # u'<tr><td colspan="2" width="2" style="border-width: 0px; background-color: #c0c0c0;">&nbsp;</td></tr>',
            # u'<tr><td width="1"></td><td width="1" style="line-height: 100%%; border-width: 0px; background-color: #c0c0c0;">&nbsp;</td></tr>',
            # u'<tr><td colspan="2" style="border-width: 0px; background-color: #c0c0c0;">&nbsp;</td></tr></table>',
            # u'<table width="100%%" style="border-width:0px;"><tr><td width="50%%" style="border-width:0px;">&nbsp;</td>',
            u'<td style="border-width:0px; border-left-width: 1px; text-align:right; vertical-align: bottom; padding: 0px; margin: 0px;"><table style="border-width:0px; align: right; padding: 0px; margin: 0px;">',
            u'<tr><td style="border-width:1px; border-left-width: 0px; line-height: 5px; padding: 0px; margin: 0px;" nowrap>',
            ]

        html = u'\n'.join(html)
        self.request.write(formatter.rawHTML(html))
        
        if tostring:
            fromto = '%s ~ %s' % (fromstring, tostring)
        else:
            fromto = '%s' % fromstring
        
        if successflag:
            flag = '(!)'
        else:
            flag = '{X}'
        
        wikiparser = wiki.Parser( '%s \'\'\'~-%s-~\'\'\' ~-%s-~' % (flag, title, fromto), self.request )
        wikiparser.format( formatter )
        
        html = [
            #u'  <font style="font-size: 8pt;">%s</font>' % msg,
            u'</td></tr></table></td></tr></table>',
            u'',
            ]

        html = u'\n'.join(html)
            
        self.request.write(formatter.rawHTML(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