#! /usr/bin/python3

from argparse import ArgumentParser, FileType
import re
import sys
import xml.etree.ElementTree as ET

def parse_args():
    parser = ArgumentParser()
    parser.add_argument("infile", nargs='?', type=FileType('r'),
                        default=sys.stdin)
    return parser.parse_args()

cfg = parse_args()

html = ET.Element('{http://www.w3.org/1999/xhtml}html')
head = ET.SubElement(html, '{http://www.w3.org/1999/xhtml}head')
style = ET.SubElement(head, '{http://www.w3.org/1999/xhtml}style')
style.text = """
@namespace "http://www.w3.org/1999/xhtml";

body {
  background-color: #111;
  color: #eee;
  max-width: 50em;
  margin: auto;
  position: relative;
}

dl {
  margin: 0;
}

details:not([open]) summary dl {
  display: flex;
  width: calc(100% - 10em);
}

details:not([open]) summary .field {
  display: none;
}

details:not([open]) summary .field.Mode,
details:not([open]) summary .field.Sequence,
details:not([open]) summary .field.Sequences {
  order: 0;
  flex: auto;
  display: block;
}

details:not([open]) summary .field.Mnemonic {
  width: 10em;
  position: absolute;
  right: 0;
  text-align: right;
  display: block;
}

details:not([open]) summary .field.Description {
  order: 1;
  flex: auto;
  text-align: right;
  display: block;
}

details:not([open]) summary dt {
  display: none;
}

.field.Mnemonic dd {
  font-weight: bold;
}

.field.Mode dd,
.field.Sequence dd,
.field.Sequences dd {
  font-family: monospace;
}

dt {
  display: inline;
}

dt::after {
  content: ": "
}

dd {
  display: inline;
  margin: 0;
}

summary {
  list-style-position: outside;
}

kbd {
  font-weight: bold;
  color: #fca;
}

var {
  color: #8f8;
}

.sep {
  color: white;
  background-color: black;
}

.ctrl {
  border: 1px solid;
  margin: -1px; /* compensate for border */
  border-radius: 0.3em;
}
"""
body = ET.SubElement(html, '{http://www.w3.org/1999/xhtml}body')

header_re = re.compile(
    r'((?:(?:Section|Sequences?|Mode|Mnemonic|Description|Source|Status):.*\n'
    r'(?:[ \t]+.*\n)*)+)')

def render_chunk(chunk):
    parts = header_re.split(chunk)
    hr = ET.Element('{http://www.w3.org/1999/xhtml}hr')
    elements = [render_body(part) if i % 2 == 0 else render_header(part)
            for i, part in enumerate(parts) if part != ""]
    if len(elements) > 0:
        summary = ET.Element('{http://www.w3.org/1999/xhtml}summary')
        details = ET.Element('{http://www.w3.org/1999/xhtml}details')
        details.append(summary)
        summary.append(elements[0])
        details.extend(elements[1:])
        return [details, hr]
    else:
        return elements + [hr]

fold_re = re.compile(r'\n[ \t]+')
field_re = re.compile(r'([\w-]*): *(.*)\n')

def render_header(text):
    text = fold_re.sub(' ', text)
    h = None
    dl = ET.Element('{http://www.w3.org/1999/xhtml}dl')
    for m in field_re.finditer(text):
        if m.group(1) == 'Section':
            h = ET.Element('{http://www.w3.org/1999/xhtml}h2')
            h.text = m.group(2).strip(' *')
        else:
            dl.extend(render_field(m.group(1), m.group(2)))
    if h == None:
        return dl
    if len(dl) == 0:
        return h
    div = ET.Element('{http://www.w3.org/1999/xhtml}div')
    div.extend([h, dl])
    return div
        
def render_field(name, value):
    # Because display: run-in is not widely supported, we put each
    # header field into its own <div>.
    div = ET.Element('{http://www.w3.org/1999/xhtml}div',
                     **{'class': 'field ' + name})
    dt = ET.SubElement(div,'{http://www.w3.org/1999/xhtml}dt')
    dt.text = name
    dd = ET.SubElement(div, '{http://www.w3.org/1999/xhtml}dd')
    if name in ('Mode', 'Sequence', 'Sequences'):
        dd.extend(render_sequence(value))
    else:
        dd.text = value
    return [div]

ctrl_re = re.compile(r'^[A-Z]{2,}[0-9]*$')
lit_re = re.compile(r'(?:^[!-~]|[0-?]+)$')

def render_sequence(text):
    elems = []
    for word in text.split(' '):
        elem = ET.Element('{http://www.w3.org/1999/xhtml}span')
        if ctrl_re.match(word):
            elem.set('class', 'ctrl')
        elif lit_re.match(word):
            elem.tag = '{http://www.w3.org/1999/xhtml}kbd'
        else:
            elem.tag = '{http://www.w3.org/1999/xhtml}var'
        elem.text = word
        elem.tail = ' '
        elems.append(elem)
    elems[-1].tail = None
    return elems

def render_body(text):
    pre = ET.Element('{http://www.w3.org/1999/xhtml}pre')
    pre.text = text
    return pre

acc = ""
for line in cfg.infile:
    if line == ('-' * 79 + "\n"):
        body.extend(render_chunk(acc))
        acc = ""
    else:
        acc += line

# I'd like to use default_namespace here, but it's broken if you use
# attributes: https://bugs.python.org/issue17088
ET.ElementTree(html).write(sys.stdout, encoding='unicode')
