"""
    MoinMoin - MonthCalendar Macro

    You can use this macro to put a months calendar page on a Wiki page.

    The days are links to Wiki pages following this naming convention:
    BasePageName/year-month-day

    Copyright (c) 2002 by Thomas Waldmann <ThomasWaldmann@gmx.de>
    Licensed under GNU GPL - see COPYING for details.

    Please review this code, this is one of my first python / MoinMoin pieces of code.

----
    
    Revisions:
    * first revision without a number (=1.0):
        * was only online for a few hours and then replaced by 1.1
    * 1.1:
        * changed name to MonthCalendar to avoid conflict with "calendar" under case-insensitive OSes like Win32
        * days as subpages
        * basepage argument
        * change order of year/month argument
        * browsing links to prev/next month/year
	    * current limitation: you can only browse one calendar on the same
              page/url, if you try 
to browse another calendar on the same page, the first one jumps back to its original display
	* show basepage in calendar header if basepage<>currentpage
    * 1.2:
        * minor fixes in argument parsing
	* cosmetic fix for netscape, other cosmetic changes, changed css
        * i18n support (weekday short names)
    * 1.3:
        * fixes to run with MoinMoin 0.11, thanks to JuergenHermann
	* fix: withspace before "," allowed in argument list
        * BasePage in calendar header (if present) is a link now
	* more i18n
        * HTML cleanup, generating code avoids bracketing errors
        * colour cosmetics
    * 1.4:
        * new parameter for enabling fixed height of 6 "calendar weeks",
	  if you want to show a whole year's calendar, this just looks
	  better than having some months with 4, some with 5 and some with 6.
	* group calendaring functions:
	  * you can give mutliple BasePages UserName1*UserName2*UserName3
	  * first BasePage is considered "your" Basepage,
            used days are bright red
	  * 2nd and all other BasePages are considered "others" BasePages
	    and lead to an increasing green component the more "used" days
	    the others have. So white gets greener and red gets more yellowish.
          * in the head part of the calendar, you can click on each name
	    to get to the Page of the same name
	  * colouring of my and others BasePage is done in a way to show
	    the colouring used in the calendar:
	    * others use green colouring (increasingly green if multiply used)
	    * I use red colouring, which gets more and more yellowish as
	      the day is used by more and more others, too
    * 1.5:
        * fixed username colouring when using a BasePage
        * fixed navigation header of MonthCalendar not to get broken into
	  multiple lines
	* fixed SubPage handling (please do not use relative SubPages like
	  /SubPage yet. Use MyName/SubPage.)
			      
TODO:
    * integrate patch for including day page contents directly into cal
     * still thinking over: does this make sense in a MonthCalendar?
     * it would be definitely nice in a week or day calendar (more space to
       burn)
    * integr. daycal -link-> monthcal

----

    Usage:
        [[MonthCalendar(BasePage,year,month,monthoffset,monthoffset2,height6)]]
        each parameter can be empty and then defaults to currentpage or currentdate or monthoffset=0

    Samples (paste that to one of your pages for a first try):

Calendar of current month for current page:
[[MonthCalendar]]

Calendar of last month:
[[MonthCalendar(,,,-1)]]

Calendar of next month:
[[MonthCalendar(,,,+1)]]

Calendar of Page SampleUser, this years december:
[[MonthCalendar(SampleUser,,12)]]

Calendar of current Page, this years december:
[[MonthCalendar(,,12)]]

Calendar of December, 2001:
[[MonthCalendar(,2001,12)]]

Calendar of the month two months after December, 2001
(maybe doesn't make much sense, but is possible)
[[MonthCalendar(,2001,12,+2)]]

Calendar of year 2002 (every month padded to height of 6):
||||||Year 2002||
||[[MonthCalendar(,2002,1,,,1)]]||[[MonthCalendar(,2002,2,,,1)]]||[[MonthCalendar(,2002,3,,,1)]]||
||[[MonthCalendar(,2002,4,,,1)]]||[[MonthCalendar(,2002,5,,,1)]]||[[MonthCalendar(,2002,6,,,1)]]||
||[[MonthCalendar(,2002,7,,,1)]]||[[MonthCalendar(,2002,8,,,1)]]||[[MonthCalendar(,2002,9,,,1)]]||
||[[MonthCalendar(,2002,10,,,1)]]||[[MonthCalendar(,2002,11,,,1)]]||[[MonthCalendar(,2002,12,,,1)]]||

Current calendar of me, also showing entries of A and B:
[[MonthCalendar(MyPage*TestUserA*TestUserB)]]

SubPage calendars:
[[MonthCalendar(MyName/CalPrivate)]]
[[MonthCalendar(MyName/CalBusiness)]]
[[MonthCalendar(MyName/CalBusiness*MyName/CalPrivate)]]

----

    You need to have some stylesheet entries like the following.
    Paste that to  default.css / moinmoin.css:
    
/* begin css for MonthCalendar macro */
/* days without and with pages linked to them */
a.cal-emptyday {
    color: #777777;
    text-align: center;
}
a.cal-usedday {
    font-weight: bold;
    color: #000000;
    text-align: center;
}
/* general stuff: workdays, weekend, today */
td.cal-workday {
    background-color: #DDDDFF;
    text-align: center;
}
td.cal-weekend {
    background-color: #FFDDDD;
    text-align: center;
}
td.cal-today {
    background-color: #CCFFCC;
    border-style: solid;
    border-width: 2pt;
    text-align: center;
}
/* invalid places on the monthly calendar sheet */
td.cal-invalidday {
    background-color: #CCCCCC;
}
/* links to prev/next month/year */
a.cal-link {
    color: #000000;
    text-decoration: none;
}
th.cal-header {
    background-color: #DDBBFF;
    text-align: center;
}
/* end css for MonthCalendar macro */

----

    If you want translated (german) messages, add something like this to
    i18n/de.py (if you have 0.11, the weekday translation might be already
    there):
    
'Mon':'Mo','Tue':'Di','Wed':'Mi','Thu':'Do','Fri':'Fr','Sat':'Sa','Sun':'So',
    
'Invalid MonthCalendar calparms "%s"!':
'Ung\366ltige MonthCalendar calparms "%s"!',

'Invalid MonthCalendar arguments "%s"!':
'Ung\366ltige MonthCalendar Argumente "%s"!',

"""

# Imports
from MoinMoin import config, user, wikiutil, webapi
from MoinMoin.Page import Page
import calendar, cgi, time, re, string
calendar.setfirstweekday(calendar.SUNDAY)

# in MoinMoin version >= 0.11 we need "_" - that's what we try first
# in MoinMoin version <= 0.10 we need "user.current.text" - try that if first fails
try:
    from MoinMoin.i18n import _
except ImportError:
    _ = user.current.text

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 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 parseargs(args, defpagename, defyear, defmonth, defoffset, defoffset2, defheight6):

    strpagename = args.group('basepage')
    if strpagename:
        parmpagename=strpagename
    else:
        parmpagename=defpagename
    # multiple pagenames separated by "*" - split into list of pagenames
    parmpagename = re.split(r'\*',parmpagename)

    stryear = args.group('year')
    if stryear:
        parmyear=int(stryear)
    else:
        parmyear=defyear

    strmonth = args.group('month')
    if strmonth:
        parmmonth=int(strmonth)
    else:
        parmmonth=defmonth
    
    stroffset = args.group('offset')
    if stroffset:
        parmoffset=int(stroffset)
    else:
        parmoffset=defoffset

    stroffset2 = args.group('offset2')
    if stroffset2:
        parmoffset2=int(stroffset2)
    else:
        parmoffset2=defoffset2

    strheight6 = args.group('height6')
    if strheight6:
        parmheight6=int(strheight6)
    else:
        parmheight6=defheight6

    return (parmpagename,parmyear,parmmonth,parmoffset,parmoffset2,parmheight6)
        
# FIXME:                          vvvvvv is there a better way for matching a pagename ?
_arg_basepage = r'\s*(?P<basepage>[^, ]+)?\s*'
_arg_year = r',\s*(?P<year>\d+)?\s*'
_arg_month = r',\s*(?P<month>\d+)?\s*'
_arg_offset = r',\s*(?P<offset>[+-]?\d+)?\s*'
_arg_offset2 = r',\s*(?P<offset2>[+-]?\d+)?\s*'
_arg_height6 = r',\s*(?P<height6>[+-]?\d+)?\s*'
_args_re_pattern = r'^(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?$' % \
                   (_arg_basepage,_arg_year,_arg_month, \
		    _arg_offset,_arg_offset2,_arg_height6)


def getTcUseDays(macro, pagename):    ### yong27's modification ###
    heading = re.compile(r"^\s*(?P<hmarker>=+)\s(.*)\s(?P=hmarker)$")
    baseindent = 0
    indent = 0
    lineno = 0
    tcUseDays={}
    #page=Page(pagename)

    for line in macro.parser.lines:
        # Filter out the headings
        lineno = lineno + 1
        match = heading.match(line)
        if not match: continue

        # Get new ident level
        newindent = len(match.group(1))
        if not indent:
            baseindent = newindent - 1
            indent = baseindent

        tcUseDays[match.group(2)]=lineno

        # Set new indent level
        indent = newindent
    return tcUseDays


def execute(macro, text, args_re=re.compile(_args_re_pattern)):
    result=''
    #print "pathinfo... ", webapi.getPathinfo()
    #print "subbed... ", re.sub('_2d(\d{2})_2d(\d{2})', r'_2d\1#\2', webapi.getPathinfo())
    (currentyear,currentmonth,currentday,h,m,s,wd,yd,ds) = time.localtime(time.time())
    thispage = macro.formatter.page.page_name
    # does the url have calendar params (= somebody has clicked on prev/next links in calendar) ?
    if macro.form.has_key('calparms'):
        text2 = macro.form['calparms'].value
        args2 = args_re.match(text2)
        if not args2:
            return ('<p><strong class="error">%s</strong></p>' % _('Invalid MonthCalendar calparms "%s"!')) % (text2,)
        else:
	    has_calparms = 1 # yes!
	    (cparmpagename,cparmyear,cparmmonth,cparmoffset,cparmoffset2,cparmheight6) = parseargs(args2,thispage,currentyear,currentmonth,0,0,0)
    else:
        has_calparms = 0
	
    if text is None: # macro call without parameters
        (parmpagename,parmyear,parmmonth,parmoffset,parmoffset2,parmheight6) = \
	([thispage],currentyear,currentmonth,0,0,0)
    else:
        # parse and check arguments
        args = args_re.match(text)
        if not args:
            return ('<p><strong class="error">%s</strong></p>' % _('Invalid MonthCalendar arguments "%s"!')) % (text,)
        else:
	    (parmpagename,parmyear,parmmonth,parmoffset,parmoffset2,parmheight6) = \
	    parseargs(args,thispage,currentyear,currentmonth,0,0,0)

    # does url have calendar params and is THIS the right calendar to modify (we can have multiple
    # calendars on the same page)?
    if has_calparms and (cparmpagename,cparmyear,cparmmonth,cparmoffset) == (parmpagename,parmyear,parmmonth,parmoffset):
        (year,month) = yearmonthplusoffset(parmyear,parmmonth,parmoffset+cparmoffset2)
	parmoffset2 = cparmoffset2
    else:
        (year,month) = yearmonthplusoffset(parmyear,parmmonth,parmoffset)

    # get the calendar
    monthcal = calendar.monthcalendar(year,month)
    colorstep=85
    p = Page(thispage)
    querystr = "calparms=%s,%d,%d,%d,%%d" % (string.join(parmpagename,'*'),parmyear,parmmonth,parmoffset)
    prevlink  = p.url(querystr % (parmoffset2-1 ))
    nextlink  = p.url(querystr % (parmoffset2+1 ))
    prevylink = p.url(querystr % (parmoffset2-12))
    nextylink = p.url(querystr % (parmoffset2+12))
    prevmonth = macro.formatter.url(prevlink,'&lt;-','cal-link')
    nextmonth = macro.formatter.url(nextlink,'-&gt;','cal-link')
    prevyear  = macro.formatter.url(prevylink,'&lt;&lt;-','cal-link')
    nextyear  = macro.formatter.url(nextylink,'-&gt;&gt;','cal-link')
    
    restable = '<table border="2" cellspacing="2" cellpadding="2">\n%s%s%s</table>\n\n'
    if parmpagename <> [thispage]:
        pagelinks = ''
        (r,g,b)=(255,0,0)
	l=len(parmpagename[0])
        steps=len(parmpagename)
	maxsteps=(255/colorstep)
	if steps>maxsteps:
	    steps=maxsteps
	chstep=int(l/steps)
	st=0
	while st < l:
	    ch = parmpagename[0][st:st+chstep]
	    (r,g,b) = cliprgb(r,g,b)
	    pagelinks = pagelinks+'<a style="%s" href="%s">%s</a>' % ('background-color:#%02x%02x%02x;color:#000000;text-decoration:none' % (r,g,b),Page(parmpagename[0]).url(),ch)
	    (r,g,b) = (r,g+colorstep,b)
	    st = st+chstep
        (r,g,b)=(255-colorstep,255,255-colorstep)
        for page in parmpagename[1:]:
	    pagelinks = pagelinks + '*<a style="%s" href="%s">%s</a>' % \
	                ('background-color:#%02x%02x%02x;color:#000000;text-decoration:none' % (r,g,b),Page(page).url(),page)
        showpagename = '   %s<BR>\n' % pagelinks
    else:
        showpagename = ''
    resth1 = '  <th colspan="7" class="cal-header">\n' \
             '%s' \
	     '   %s&nbsp;%s&nbsp;<b>&nbsp;%s&nbsp;/&nbsp;%s</b>&nbsp;%s\n&nbsp;%s\n' \
	     '  </th>\n' % (showpagename,prevyear,prevmonth,str(year),str(month),nextmonth,nextyear)
    restr1 = ' <tr>\n%s </tr>\n' % resth1

    r7=range(7)
    restr2 = ' <tr>\n%s </tr>\n'
    restd2 = ''

    ######### set first weekday to Sun by yong27 ###########
    #for wkday in r7:
    for wkday in [6,0,1,2,3,4,5]:
    ########################################################
        wday=_(calendar.day_abbr[wkday])
        if wkday==5 or wkday==6:
            cssday="cal-weekend"
        else:
            cssday="cal-workday"
        restd2 = restd2 + '  <td class="%s" width="12%%"><font size="2">%s</font></td>\n' % (cssday,wday)
    restr2 = restr2 % restd2

    if parmheight6:
        while len(monthcal)<6:
            monthcal = monthcal + [[0,0,0,0,0,0,0]]

    testpagename = wikiutil.quoteWikiname("%s/%4d-%02d"%(parmpagename[0],year,month))
    tcUseDays = getTcUseDays(macro, testpagename)

	
    restrn = ''
    for week in monthcal:
        restrn = restrn + ' <tr>\n%s </tr>\n'
	restdn = ''
        for wkday in r7:
            day=week[wkday]
            if not day:
                restdn = restdn + '  <td class="cal-invalidday">&nbsp;</td>\n'
            else:
	        csslink="cal-emptyday"
		(r,g,b,u) = (255,255,255,0)
		page = parmpagename[0]
                link = "%s/%4d-%02d-%02d" % (page,year,month,day)
                dayStr=link.split('/')[1]
                if dayStr in tcUseDays:
    	            csslink="cal-usedday"
		    (r,g,b,u) = (255,150,0,1)
                    link = "%s/%4d-%02d#line%d" % (page,year,month,tcUseDays[dayStr])
                else:
                    link = "%s/%4d-%02d" % (page,year,month)
                    
		for otherpage in parmpagename[1:]:
                    otherlink = "%s/%4d-%02d-%02d" % (otherpage,year,month,day)
                    dayStr=otherlink.split('/')[1]
                    if dayStr in tcUseDays:
    	                csslink="cal-usedday"
			if u==0:
			    (r,g,b) = (r-colorstep,g,b-colorstep)
			else:
			    (r,g,b) = (r,g+colorstep,b)
		(r,g,b) = cliprgb(r,g,b)
		style = 'background-color:#%02x%02x%02x' % (r,g,b)
                userquery = webapi.getPathinfo()
                if '/' in webapi.getPathinfo()[1:]:
                    link=link.split('/')[1]
	        fmtlink = macro.formatter.url(wikiutil.unquoteWikiname(link),str(day),csslink)
	        if day==currentday and month==currentmonth and year==currentyear:
	            cssday="cal-today"
		else:
		    cssday="cal-nottoday"
	        restdn = restdn + '  <td style="%s" class="%s">%s</td>\n' % (style,cssday,fmtlink)
        restrn = restrn % restdn

    result = result + '\n' + restable % (restr1,restr2,restrn)

    ## ToDo
    testpagename = wikiutil.quoteWikiname("%s/%4d-%02d"%(parmpagename[0],year,month))
    tp = Page(testpagename, formatter=macro.formatter.__class__())
    return result

