#!/usr/bin/python
import os, sys, urllib, libxml2, libxslt, ldap, time, datetime, re, pytz

#
# globals
#
cscUri = "http://csclub.uwaterloo.ca/xsltproc"
cscTerms = ["Winter", "Spring", "Fall"]
cscShortTerms = ['w', 's', 'f']
cscLdapUri = "ldap://ldap1.csclub.uwaterloo.ca ldap://ldap2.csclub.uwaterloo.ca"
cscLdap = None
cscPeopleBase = 'ou=People,dc=csclub,dc=uwaterloo,dc=ca'
cscPositions = {
    # position            name                       exec    elected   order
    'president'       : ( 'President',               True,   True,     1, ),
    'vice-president'  : ( 'Vice-president',          True,   True,     2, ),
    'treasurer'       : ( 'Treasurer',               True,   True,     3, ),
    'secretary'       : ( 'Secretary',               True,   True,     4, ),
    'sysadmin'        : ( 'System Administrator',    True,   False,    5, ),
    'cro'             : ( 'Chief Returning Officer', False,  False,    6, ),
    'offsck'          : ( 'Office Manager',          False,  False,    7, ),
    'librarian'       : ( 'Librarian',               False,  False,    8, ),
    'imapd'           : ( 'Imapd',                   False,  False,    9, ),
    'webmaster'       : ( 'Web Master',              False,  False,    10, ),
}
cscYesNo = { True : 'yes', False : 'no' }

def xslArgToString(arg):
    if type(arg) == type([]):
        return libxml2.xmlNode(arg[0]).getContent()
    else:
        return arg

#
# cscLdapConnect
#
def cscLdapConnect():
    global cscLdap
    cscLdap = ldap.initialize(cscLdapUri)
    cscLdap.simple_bind_s("", "")

#
# csc:encode-for-uri
#
def cscEncodeForUri(ctx, uri):
    uri = xslArgToString(uri)
    return urllib.quote(uri)

#
# csc:term
#
def cscTerm(ctx, date):
    date = xslArgToString(date)
    try:
        [year, month, day] = date.split("-")
        term = (int(month) - 1) / 4
        return cscTerms[term] + " " + year
    except:
        print "Invalid date '%s'" % date
        raise

#
# csc:member-list
#
def cscMemberList(ctx, _):
    try:
        if cscLdap == None:
            cscLdapConnect()
        curDate = time.strftime('%d-%m-%Y')
        year = time.localtime().tm_year
        term = cscShortTerms[int(time.localtime().tm_mon - 1) / 4]
        members = cscLdap.search_s(cscPeopleBase, ldap.SCOPE_SUBTREE,
            '(&(objectClass=member)(term=%s%d))' % (term, year))
        doc = libxml2.newDoc("1.0")
        root = doc.newChild(None, "root", None)
        for (_, member) in members:
            node = root.newChild(None, "member", None)
            node.setProp("userid", member['uid'][0])
            node.setProp("name", member['cn'][0])
            if not 'program' in member:
                member['program'] = ['']
            node.setProp("program", member['program'][0])
        return [root]
    except Exception, e:
        print e
        raise

#
# csc:position-list
#
def cscPositionList(ctx, _):
    try:
        if cscLdap == None:
            cscLdapConnect()
        members = cscLdap.search_s(cscPeopleBase, ldap.SCOPE_SUBTREE,
            '(&(objectClass=member)(position=*))')
        doc = libxml2.newDoc("1.0")
        root = doc.newChild(None, "root", None)
        positions = {}
        for (_, member) in members:
            for position in member['position']:
                if not position in positions:
                    positions[position] = []
                positions[position].append(member)
        for position in cscPositions.keys():
            if not position in positions:
                positions[position] = []
        for (position, members) in positions.iteritems():
            node = root.newChild(None, "position", None)
            node.setProp("position", position)
            if not position in cscPositions:
                raise Exception("Position '%s' not known" % position)
            position = cscPositions[position]
            node.setProp("name", position[0])
            node.setProp("exec", cscYesNo[position[1]])
            node.setProp("elected", cscYesNo[position[2]])
            node.setProp("order", str(position[3]))
            for member in members:
                child = node.newChild(None, "holder", None)
                child.setProp("userid", member['uid'][0])
                child.setProp("name", member['cn'][0])
        return [root]
    except Exception, e:
        print e
        raise

#
# csc:ical-datetime
#
def cscIcalDatetime(ctx, date, time, addmin = "0"):
    date = xslArgToString(date)
    time = xslArgToString(time)
    addmin = int(xslArgToString(addmin))
    r = re.search("(\d*)-(\d*)-(\d*)", date)
    year, month, day = 0, 0, 0
    if r != None:
        year, month, day = (int(i) for i in r.groups())
    r = re.search("(\d*):(\d*)\s*([apAP])", time)
    hour, minute = (0, 0)
    if r != None:
        hour, minute = (int(i) for i in r.groups()[:2])

        # 12-hour times
        if r.group(3) in 'aA':
            hour %= 12 #hour % 12
        elif r.group(3) in 'pP':
            hour %= 12
            hour += 12

        # 24-hour time
        else:
            hour %= 24
            
    dt = datetime.datetime(year, month, day, hour, minute)
    dt = pytz.timezone('Canada/Eastern').localize(dt)
    dt += datetime.timedelta(0, 0, 0, 0, addmin)
    return dt.astimezone(pytz.utc).strftime("%Y%m%dT%H%M%SZ")

#
# csc:ical-escape
#
def cscIcalEscape(ctx, str):
    str = xslArgToString(str)
    str = str.replace("\n", " ")
    #str = str.replace(":", "")
    #str = str.replace(";", "")
    #str = str.replace(",", "")
    str = re.sub("\s+", " ", str)
    str = re.sub("^\s+", "", str)
    str = re.sub("\s+$", "", str)
    return str

#
# csc:email
#
def cscEmail(ctx, email):
    email = xslArgToString(email)
    return "_EMAIL_TODO_"

#
# main
#

# check argv
if len(sys.argv) < 4:
    print "Usage: xsltproc.py input-file style-sheet output-file [params...]"
    sys.exit(1)
inFile = sys.argv[1]
xsltFile = sys.argv[2]
outFile = sys.argv[3]
rawParams = sys.argv[4:]

# check params
params = {}
for p in rawParams:
    try:
        [key, val] = p.split("=")
        params[key] = "'" + val + "'"
    except:
        print "Missing value for parameter '%s'" % p
        sys.exit(1)

try:
    # register extensions
    libxslt.registerExtModuleFunction("encode-for-uri", cscUri, cscEncodeForUri)
    libxslt.registerExtModuleFunction("term", cscUri, cscTerm)
    libxslt.registerExtModuleFunction("member-list", cscUri, cscMemberList)
    libxslt.registerExtModuleFunction("position-list", cscUri, cscPositionList)
    libxslt.registerExtModuleFunction("ical-datetime", cscUri, cscIcalDatetime)
    libxslt.registerExtModuleFunction("ical-escape", cscUri, cscIcalEscape)
    libxslt.registerExtModuleFunction("email", cscUri, cscEmail)

    # parse xml/xslt and apply style-sheet
    style = libxslt.parseStylesheetFile(xsltFile)
    if style == None: sys.exit(1)
    doc = libxml2.parseFile(inFile)
    res = style.applyStylesheet(doc, params)
    ret = style.saveResultToFilename(outFile, res, 0)

except Exception, e:
    print e
    sys.exit(1)
