import sys, re

from bazbase.wiki import (macros,environment,illegal,divided,
                          WikiException, NoContentException,
                          safesplit, parse_macro_args, recurse,
                          baz_eval, full_eval, leafprop, render_propval,
                          render_allow_override, get_format,
                          get_element, get_current_element, get_propname,
                          get_topthis, get_context, get_moverride,
                          atom, eoverride, moverride, context,
                          makeRestricted, tokens_text, string_tokenize)
from bazbase.flavors import FORMATS

from redbeans.tokens import *
from redbeans.latex import LATEX_SIZES

from . import custom

def moverride_thunk(ename, pname, form):
    with moverride(dict(form=form)):
        for t in recurse(ename, pname):
            yield t

def get_headfoot(form, ename):
    return (moverride_thunk(ename, u'topleft', form),
            moverride_thunk(ename, u'topright', form),
            moverride_thunk(ename, u'bottomleft', form),
            moverride_thunk(ename, u'bottomright', form))

def html_div_headers(headfoot):
    yield Literal('<div class="lheader">')
    for t in headfoot[0]:
        yield t
    yield Literal('</div>')
    yield Literal('<div class="rheader">')
    for t in headfoot[1]:
        yield t
    yield Literal('</div>')
    yield Literal('<div class="lfooter">')
    for t in headfoot[2]:
        yield t
    yield Literal('</div>')
    yield Literal('<div class="rfooter">')
    for t in headfoot[3]:
        yield t
    yield Literal('</div>')
def html_table_header(headfoot):
    yield Literal('<tr class="head"><td><span>')
    for t in headfoot[0]:
        yield t
    yield Literal('</span><span>')
    for t in headfoot[1]:
        yield t
    yield Literal('</span></td><td></td></tr>')
def html_table_footer(headfoot):
    yield Literal('<tr class="foot"><td><span>')
    for t in headfoot[2]:
        yield t
    yield Literal('</span><span>')
    for t in headfoot[3]:
        yield t
    yield Literal('</span></td><td></td></tr>')
def latex_cmd_headers(headfoot):
    yield Literal('\\lhead{')
    for t in headfoot[0]:
        yield t
    yield Literal('}\\rhead{')
    for t in headfoot[1]:
        yield t
    yield Literal('}\\lfoot{')
    for t in headfoot[2]:
        yield t
    yield Literal('}\\rfoot{')
    for t in headfoot[3]:
        yield t
    yield Literal('}\n')
def latex_arg_headers(headfoot):
    first = True
    for hf in headfoot:
        if not first:
            yield Literal('}{')
        else:
            first = False
        for t in hf:
            yield t

def form(arglist, content=None):
    """Placeholder that evaluates to 'sheet' or 'card' or 'badge' when
    evaluating headers and footers."""
    return ""
macros.form = form

def sheet(arglist, content):
    """Render content as a sheet.  Takes an element to get headers and
    footers from, and optionally a 'color' parameter for paper color."""
    args = parse_macro_args(arglist)
    color = ""
    headfoot = [(),(),(),()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 1:
            headfoot = get_headfoot(u'sheet', val)
        else:
            raise WikiException("Unknown arg '%s' in sheet!"
                                % (key))
    if not color.strip():
        color = "white"
    if get_format() == FORMATS['html']:
        yield Literal('<div class="sheet %s">' % color)
        for t in html_div_headers(headfoot):
            yield t
        for t in content:
            yield t
        yield Entity(ENV_BREAK, True)
        yield Literal('</div>')
    elif get_format() == FORMATS['tex']:
        yield Literal('\\begin{sheet}\n')
        for t in latex_cmd_headers(headfoot):
            yield t
        for t in content:
            yield t
        yield Entity(ENV_BREAK, True)
        yield Literal('\n\\end{sheet}')
    else:
        for t in content:
            yield t
macros.sheet = sheet

# In inches, (width, height, font size).
SIZES = {
    'normal': (3.25, 2, 0), # item card sized
    'large': (3.6, 3.1, 0), # ability card sized
    'tall': (2.5, 3.4, 0), # approximately playing card sized
    'mini': (2.5, 1.5, -2),
    }

def card(arglist, content):
    """Render content as a card.  Takes an element to get headers and
    footers from, and optionally a 'color' parameter for paper color
    and a 'size' parameter."""
    parts = safesplit(content, ('flip',))
    args = parse_macro_args(arglist)
    color = ""
    size = "normal"
    headfoot = [(),(),(),()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 'size':
            size = baz_eval(val).strip()
        elif key == 1:
            headfoot = get_headfoot(u'card', val)
        else:
            raise WikiException("Unknown arg '%s' in card!"
                                % (key))
    if not color.strip():
        color = "white"
    if size not in SIZES:
        raise WikiException("Unknown card size '%s'; valid options are: %s."
                            % (size, ', '.join(SIZES.keys())))
    if len(parts) > 2:
        raise WikiException("More than one ##flip## in a ##card##!")
    else:
        if len(parts) < 2:
            front = parts[0]
            back = ""
        else:
            front,back = parts
        if get_format() == FORMATS['html']:
            yield Literal('<table class="card %s %s">\n' % (color, size))
            for t in html_table_header(headfoot):
                yield t
            yield Literal('<tr class="body">\n<td>\n')
            for t in front:
                yield t
            yield Literal('\n</td>\n')
            yield Literal('<td>\n')
            for t in back:
                yield t
            yield Literal('\n</td>\n</tr>\n')
            for t in html_table_footer(headfoot):
                yield t
            yield Literal('</table>\n')
        elif get_format() == FORMATS['tex']:
            w, h, font = SIZES[size]
            font = LATEX_SIZES[font]
            yield Literal('\\begin{card}{%sin}{%sin}{\\%s}\n' % (w, h, font))
            yield Literal('\\front{')
            for t in latex_cmd_headers(headfoot):
                yield t
            for t in front:
                yield t
            yield Literal('}\n\\back{')
            for t in back:
                yield t
            yield Literal('}\n\\end{card}\n')
        else:
            for t in front:
                yield t
            yield Entity(ENV_BREAK)
            for t in back:
                yield t

macros.card = card
macros.flip = illegal('flip', 'card', 'sign')

def packet(arglist,content):
    """Render content as a packet.  Takes a label for the outside and
    an element to get headers and footers from, and optionally a
    'color' parameter for paper color and a 'style' parameter ("envelope"
    or "folding")."""
    args = parse_macro_args(arglist)
    label = ""
    color = ""
    style = "Envelope"
    headfoot = [(), (), (), ()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 1:
            label = full_eval(val)
        elif key == 2:
            headfoot = get_headfoot(u'packet', val)
        elif key == 'style':
            style = baz_eval(val).strip().capitalize()
            if style not in ("Envelope","Folding"):
                raise WikiException('The only valid packet styles are "envelope" and "folding"!')
        else:
            raise WikiException("Unknown arg '%s' in packet!"
                                % (key))
    if not color.strip():
        color = "white"
    if get_format() == FORMATS['html']:
        yield Literal('<table class="card packet %s %s">\n' % (style.lower(),
                                                               color))
        for t in html_table_header(headfoot):
            yield t
        yield Literal('<tr class="body">\n<td>\n')
        for t in label:
            yield t
        yield Literal('\n</td>\n')
        yield Literal('<td>\n')
        for t in content:
            yield t
        yield Literal('\n</td>\n</tr>\n')
        for t in html_table_footer(headfoot):
            yield t
        yield Literal('</table>\n')
    elif get_format() == FORMATS['tex']:
        yield Literal('\\%sPacket{' % (style,))
        for t in latex_arg_headers(headfoot):
            yield t
        yield Literal('}{')
        for t in label:
            yield t
        yield Literal('}{')
        for t in content:
            yield t
        yield Literal('}')
    else:
        yield Text(u"Packet: ")
        for t in label:
            yield t
macros.packet = packet

def notebook(arglist,content):
    args = parse_macro_args(arglist)
    front = ""
    color = ""
    headfoot = [(), (), (), ()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 1:
            front = full_eval(val)
        elif key == 2:
            headfoot = get_headfoot(u'notebook', val)
        else:
            raise WikiException("Unknown arg '%s' in notebook!"
                                % (key))
    if not color.strip():
        color = "white"
    if get_format() == FORMATS['html']:
        yield Literal('<div class="notebook %s">\n' % (color))
        yield Literal('<div class="front">')
        for t in html_div_headers(headfoot):
            yield t
        for t in front:
            yield t
        yield Literal('</div>')
        for t in content:
            yield t
        yield Literal('</div>\n')
    elif get_format() == FORMATS['tex']:
        yield Literal('\\startnotebook{')
        for t in latex_arg_headers(headfoot):
            yield t
        yield Literal('}{')
        for t in front:
            yield t
        yield Literal('}')
        for t in content:
            yield t
        yield Literal('\n\\endnotebook\n')
    else:
        yield Text(u"Notebook: ")
        for t in front:
            yield t
macros.notebook = notebook
def page(arglist,content):
    """Dummy page for previewing outside a notebook."""
    args = parse_macro_args(arglist)
    if 1 in args:
        title = baz_eval(args[1]).strip()
    else:
        title = getattr(get_format(), 'gknextpage', 1)
        get_format().gknextpage = title+1
        title = unicode(title)
    if get_format() == FORMATS['html']:
        yield Literal('<div><h2>')
        yield Text(title)
        yield Literal('</h2>\n')
        for t in content:
            yield t
        yield Literal('</div>')
    elif get_format() == FORMATS['tex']:
        yield Literal(r"\begin{nbpage}{")
        yield Text(title)
        yield Literal('}\n')
        for t in content:
            yield t
        yield Literal('\n\\end{nbpage}\n')
    else:
        yield Start(HEADING, 2)
        yield Text(title)
        yield End(HEADING, 2)
        for t in content:
            yield t
macros.page = page

# TODO(xavid): unify badges and items more?
def badge(arglist,content):
    args = parse_macro_args(arglist)
    color = ""
    style = "light"
    size = "normal"
    headfoot = [(), (), (), ()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 1:
            headfoot = get_headfoot(u'badge', val)
        elif key == 'size':
            size = baz_eval(val).strip()
        elif key == 'style':
            style = baz_eval(val).strip()
            valid_styles = ("light", "dark", "simple", "mini")
            if style not in valid_styles:
                raise WikiException('The only valid badge styles are: %s!'
                                    % ', '.join(valid_styles))
        else:
            raise WikiException("Unknown arg '%s' in badge!"
                                % (key))
    if not color.strip():
        color = "white"
    if size not in SIZES:
        raise WikiException("Unknown badge size '%s'; valid options are: %s."
                            % (size, ', '.join(SIZES.keys())))
    if get_format() == FORMATS['html']:
        yield Literal('<div class="badge %s %s %s">\n' % (style.lower(), color,
                                                          size))
        for t in html_div_headers(headfoot):
            yield t
        for t in content:
            yield t
        yield Literal('</div>\n')
    elif get_format() == FORMATS['tex']:
        w, h, font = SIZES[size]
        font = LATEX_SIZES[font]
        yield Literal('\\NameBadge[%s]{%sin}{%sin}{\\%s}{'
                      % (style, w, h, font))
        for t in latex_arg_headers(headfoot):
            yield t
        yield Literal('}{')
        for t in content:
            yield t
        yield Literal('}')
    else:
        yield Text(u"Badge: ")
        for t in content:
            yield t
macros.badge = badge

def sign(arglist,content):
    args = parse_macro_args(arglist)
    parts = safesplit(content, ('flip',))
    assert len(parts) in (1, 2), parts
    name = None
    color = u""
    size = None
    location = u""
    blurb = None
    headfoot = [(), (), (), ()]
    for key,val in args.items():
        if key == 'color':
            color = baz_eval(val).strip()
        elif key == 'blurb':
            blurb = [Text(baz_eval(val).strip())]
        elif key == 'location':
            location = baz_eval(val).strip()
        elif key == 'size':
            size = baz_eval(val).strip()
        elif key == 1:
            headfoot_ename = val
            headfoot = get_headfoot(u'sign', val)
        elif key == 2:
            name = [[Text(baz_eval(val).strip())]] * 2
        else:
            raise WikiException("Unknown arg '%s' in packet!"
                                % (key))
    if not color.strip():
        color = "white"
    if name is None:
        name = [moverride_thunk(headfoot_ename, u'name', u'sign')]
        if len(parts) == 2:
            name.append(moverride_thunk(headfoot_ename, u'name', u'signback'))
    if blurb is None:
        blurb = moverride_thunk(headfoot_ename, u'blurb', u'sign')
    if size is None:
        size = baz_eval(u'%s.%s' % (headfoot_ename, u'size'))
    size = size.capitalize()
    if size not in ("Big","Medium","Small"):
        raise WikiException("Sign size must be big, medium, or small, not %s!"
                            % size)
    if get_format() == FORMATS['html']:
        for i in xrange(len(parts)):
            yield Literal('<div class="sign %s %s">\n' % (size.lower(), color))
            for t in html_div_headers(headfoot):
                yield t
            yield Literal('<h1 class="name">')
            for t in name[i]:
                yield t
            yield Literal('</h1>')
            yield Literal('<div class="content">\n')
            for t in parts[i]:
                yield t
            yield Literal('</div>\n')
            yield Literal('<div class="blurb">')
            for t in blurb:
                yield t
            yield Literal('</div>\n')
        yield Literal('</div>\n')
    elif get_format() == FORMATS['tex']:
        yield Literal(r'\headfoot{')
        for t in latex_arg_headers(headfoot):
            yield t
        yield Literal('}\\Sign[\\%s]{' % (size,))
        for t in name[0]:
            yield t
        yield Literal('}{')
        yield Text(location)
        yield Literal('}{')
        for t in parts[0]:
            yield t
        yield Entity(ENV_BREAK, True)
        yield Literal('}{')
        if len(parts) > 1:
            for t in name[1]:
                yield t
            yield Literal('}{')
            for t in parts[1]:
                yield t
            yield Entity(ENV_BREAK, True)            
        else:
            yield Literal('}{')
        yield Literal('}{')
        for t in blurb:
            yield t
        yield Literal('}')
    else:
        yield Text(u'Sign: ')
        for t in content:
            yield t
macros.sign = sign

def pagefold(arglist, content):
    args = parse_macro_args(arglist)
    parts = safesplit(content, ('break',))
    headfoot_ename = args.pop(1)
    headfoot = get_headfoot(u'pagefold', headfoot_ename)
    assert len(args) == len(parts)

    if get_format() == FORMATS['tex']:
        yield Literal('\\begin{pagefold}\n')
        for t in latex_cmd_headers(headfoot):
            yield t
        total = 0
        flipped = False
        for i in xrange(len(parts)):
            part = parts[i]
            height = int(args[i + 2])
            yield Literal(r'\pfpart{')
            yield Literal(str((height / 100.) * 10.3))
            yield Literal(r'in}{')
            for t in part:
                yield t
            yield Literal('}\n')
            total += height
            if total == 100:
                if not flipped:
                    yield Literal('\\clearpage\n')
                    flipped = True
                total = 0
            elif total > 100:
                raise WikiException("Pagefold side doesn't add up to 100!")
            else:
                yield Literal('\\hrule\n')
        yield Literal('\\end{pagefold}\n')
    else:
        # TODO(xavid): cleaner HTML version.
        for part in parts:
            for t in part:
                yield t
            yield Entity(HRULE)
            
macros.pagefold = pagefold
macros.upsidedown = environment('upsidedown', texmode='command')

#macros.header = divided('header','gap','left','right')
#macros.footer = divided('footer','gap','left','right')
#macros.gap = illegal('gap','header','footer')

def imageheader(arglist, content=None):
    args = parse_macro_args(arglist)
    if 1 not in args or 2 not in args or 3 not in args or len(args) != 3:
        raise WikiException(
            "##imageheader## takes exactly 3 parameters!")
    if content is None:
        raise NoContentException("##imageheader## needs content!")
    width = baz_eval(args[2]).strip()
    height = baz_eval(args[3]).strip()
    img = Entity(IMAGE, "%s^d%sx%s" % (args[1], width, height))

    if get_format() == FORMATS['html']:
        yield Literal('<table class="imageheader"><tr>\n')
        yield Literal('<td>')
        yield img
        yield Literal('</td>\n')
        yield Literal('<th>\n')
        for t in content:
            yield t
        yield Literal('</th>\n')
        yield Literal('</tr></table>\n')
    elif get_format() == FORMATS['tex']:
        yield Literal('\\imageheader{')
        yield img
        yield Literal('}{%spx}{%spx}{' % (width, height))
        for t in content:
            yield t
        yield Literal('}')
    else:
        yield Start(HEADER, 1)
        for t in content:
            yield t
        yield End(HEADER, 1)
macros.imageheader = imageheader

macros.secret = environment(
    'secret', doc="""Mark this content as secret, for printing.""")

def labels(arglist, content):
    args = parse_macro_args(arglist)
    h = baz_eval(args[1])
    w = baz_eval(args[2])
    v_margin = baz_eval(args[3])
    h_margin = baz_eval(args[4])
    if 5 in args:
        v_gap = baz_eval(args[5])
    else:
        v_gap = 0
    if 6 in args:
        h_gap = baz_eval(args[6])
    else:
        h_gap = 0
    
    if get_format() == FORMATS['tex']:
        rows = int(11. / (h + v_gap))
        cols = int(8.5 / (w + h_gap))
        yield Literal('\\LabelCols=%s\n' % cols)
        yield Literal('\\LabelRows=%s\n' % rows)
        yield Literal('\\LeftPageMargin=%sin\n' % h_margin)
        yield Literal('\\RightPageMargin=%sin\n' % h_margin)
        yield Literal('\\TopPageMargin=%sin\n' % v_margin)
        yield Literal('\\BottomPageMargin=%sin\n' % v_margin)
        yield Literal('\\InterLabelColumn=%sin\n' % h_gap)
        yield Literal('\\InterLabelRow=%sin\n' % v_gap)
        yield Literal('\\LabelSetup\n')
        for t in content:
            if t.style == UNORDERED_ITEM:
                if t.op == START:
                    yield Literal(r'\addresslabel{\centering ')
                elif t.op == END:
                    yield Literal('}\n')
                else:
                    assert False, t
            else:
                yield t
    else:
        for t in content:
            yield t

macros.labels = labels

def current_run():
    return '1'

def runtime(arglist):
    ename, propname = arglist[0].split('.', 1)
    element = get_element(ename)
    propname = get_propname(propname)
    propval = element.get_propval(propname)

    run = current_run()
    if False: #propval.has_overlay(run):
        yield Start(STRIKE)
        yield Entity(MACRO, ('%s.%s' % (ename, propname), None))
        yield End(STRIKE)
        yield Text(u' ')
        yield Start(BOLD)
        yield Text(propval.get_overlay(run))
        yield End(BOLD)
    else:
        for t in recurse(ename, propname): yield t
macros.runtime = runtime

def todo(arglist, content=None):
    makeRestricted()
    yield Start(BOLD)
    yield Start(ITALIC)
    yield Text(u"TODO: ")
    if arglist:
        yield Text(u' '.join(arglist))
    if content:
        for t in content:
            yield t
    yield Text(u" :TODO")
    yield End(ITALIC)
    yield End(BOLD)
macros.todo = todo
macros.TODO = todo

def quote(start, end=None):
    def env(arglist, content):
        parts = safesplit(content, ('break',))
        if len(parts) > 2:
            raise WikiException("Too many parts in a quote!")
        yield start
        yield Start(ITALIC)
        for t in parts[0]:
            yield t
        yield End(ITALIC)
        if end is not None:
            yield end
        if len(parts) > 1:
            yield Start(RIGHT)
            yield Start(ITALIC)
            for t in parts[1]:
                yield t
            yield End(ITALIC)
            yield End(RIGHT)
    return env
macros.cenquote = quote(Start(CENTER), End(CENTER))
macros.bigquote = quote(Entity(NOINDENT))

GENDER_CONV_MAP = {'?': 'A'}

def pronoun_helper(mfna):
    genderp = u'gender'
    #leaf = get_element(u'leaf')
    #assert leaf is not None
    #gen = render_allow_override(leaf, genderp)
    #if gen is None:
    e = get_current_element()
    assert e is not None
    gen_full = render_allow_override(e, genderp, '?')
    if ' ' in gen_full:
        raise WikiException("Unknown gender '%s'" % gen_full)
    gen = gen_full[0:1].upper()

    if 'A' not in mfna:
        mfna['A'] = mfna['M'] + [Text(u'/')] + mfna['F']
    for g in ('N', 'S'):
        if g not in mfna:
            mfna[g] = mfna['A']
    if gen in GENDER_CONV_MAP:
        gen = GENDER_CONV_MAP[gen]
    if gen in mfna:
        return mfna[gen]
    else:
        raise WikiException("Unknown gender: %s" % gen_full)

def mfn_generic(*order):
    def mfn(arglist, content):
        parts = safesplit(content, ('break',))
        mfna = {}
        for x in xrange(min(len(order), len(parts))):
            mfna[order[x]] = parts[x]
        return pronoun_helper(mfna)
    return mfn
macros.mf = macros.mfn = mfn_generic('M', 'F', 'N')
macros.fm = macros.fmn = mfn_generic('F', 'M', 'N')

def pronoun(**mfna):
    for k in mfna:
        mfna[k] = [Text(unicode(mfna[k]))]
    def pn(arglist, content=None):
        return pronoun_helper(mfna)
    return pn
# Helpers
def _pron(mac, M, F, **mfna):
    mfna['M'] = M
    mfna['F'] = F
    # Doing this explicitly causes the uppercasing to DTRT.
    if 'A' not in mfna:
        mfna['A'] = '%s/%s' % (F, M)
    if 'S' not in mfna and mac.startswith('th'):
        mfna['S'] = mac[2:]
    for a in [mac] + mfna.values():
        if not hasattr(macros, a):
            pn = pronoun(**mfna)
            if a == mac:
                pn.hidden = 'pronoun'
            else:
                pn.hidden = 'pronounvariation'
            setattr(macros, a, pn)
    upper = dict((k, mfna[k][0].upper() + mfna[k][1:]) for k in mfna)
    mac = mac[0].upper()+mac[1:]
    for a in [mac] + upper.values():
        if not hasattr(macros, a):
            pn = pronoun(**upper)
            pn.hidden = 'pronounupper'
            setattr(macros, a, pn)
# First in the list wins
_pron('they', 'he', 'she', N='it')
_pron('them', 'him', 'her', N='it')
_pron('their', 'his', 'her', N='its')
_pron('theirs', 'his', 'hers', N='its')
_pron('themself', 'himself', 'herself', N='itself')
_pron('spouse', 'husband', 'wife', N='spouse', A='spouse')
_pron('offspring', 'son', 'daughter', N='child', A='child')
_pron('kid', 'boy', 'girl', N='kid', A='kid')
_pron('sibling', 'brother', 'sister', N='sibling', A='sibling')
_pron('parent', 'father', 'mother', N='parent', A='parent')
_pron('uncle', 'uncle', 'aunt')
_pron('nephew', 'nephew', 'niece')
_pron('person', 'man', 'woman', N='person', A='person')
_pron('human', 'man', 'woman', N='human', A='human')
_pron('gender', 'male', 'female', N='neuter', A='uncertain', S='other')

# For sarcastic titles.
_pron('mister', 'mister', 'miss')

def youish(you):
    def y(arglist, content):
        current = get_element(u'current')
        if get_element(u'leaf') == current:
            yield Text(you)
        else:
            had_content = False
            for t in content:
                had_content = True
                yield t
            if not had_content:
                for t in recurse(current, u'name'):
                    yield t
    return y
macros.You = youish(u'You')
macros.you = youish(u'you')

PRE=u'<<<pre/>>>'
SUF=u'<<<suf/>>>'
def parsename(name):
    gd = dict((k, '') for k in ('first', 'middle', 'last',
                                'prefix', 'suffix'))
    gd['full'] = name
    if SUF in name:
        name, gd['suffix'] = name.split(SUF, 1)
        gd['full'] = gd['full'].replace(SUF, ' ')
    elif ", " in name:
        name, gd['suffix'] = name.split(", ", 1)

    if PRE in name:
        gd['prefix'], name = name.split(PRE, 1)
        gd['full'] = gd['full'].replace(PRE, ' ')
    
    bits = name.split()
    assert len(bits) >= 1
    if len(bits) == 1:
        gd['first'] = bits[0]
    elif not gd['prefix'] and bits[0].endswith('.'):
        gd['prefix'] = bits[0]
        if len(bits) == 2:
            gd['last'] = bits[1]
        elif len(bits) == 3:
            gd['first'], gd['last'] = bits[1:]
        else:
            gd['first'] = bits[1]
            gd['last'] = bits[-1]
            gd['middle'] = ' '.join(bits[2:-1])
    else:
        if len(bits) == 2:
            gd['first'], gd['last'] = bits
        else:
            gd['first'] = bits[0]
            gd['last'] = bits[-1]
            gd['middle'] = ' '.join(bits[1:-1])

    gd['informal'] = gd['first'] or gd['last']
    
    gd['formal'] = gd['last'] or gd['first']
    if gd['prefix']:
        gd['formal'] = "%s %s" % (gd['prefix'], gd['formal'])

    # TODO(xavid): handle Japanese/etc. name order
    if 'last' in gd:
        gd['surname'] = gd['last']
    if 'first' in gd:
        gd['given'] = gd['first']
    
    return gd
    
def namepart(part, nameprop=u'name'):
    def np(arglist, content=None):
        argstr = u' '.join(arglist)
        if argstr.strip():
            namep = argstr.strip()
        else:
            namep = nameprop
        e = get_current_element()
        name = render_allow_override(e, namep,
                                     moverrides=dict(pre=PRE, suf=SUF))
        if name is None:
            raise WikiException("%s.%s is not defined for name parsing!"
                                % (e.ename,namep))
        #print >>sys.stderr, repr(name)
        namedict = parsename(name)
        yield Text(namedict[part])
    np.hidden = 'namepart'
    return np

macros.pre = atom(
    " ",
    doc="Marks the end of a title or prefix for name parsing.")
macros.suf = atom(
    " ",
    doc="Marks the start of an end title or suffix for name parsing.")
macros.first = namepart('first')
macros.middle = namepart('middle')
macros.last = namepart('last')
macros.full = namepart('full')

macros.given = namepart('given')
macros.surname = namepart('surname')

macros.formal = namepart('formal')
macros.informal = namepart('informal')

macros.player = leafprop(u'player')
macros.contact = leafprop(u'contact')
