#!/usr/bin/env python
# -*- coding: utf-8 -*-

from lxml import etree
import re

def getbytag(e, tagname):
    l = []
    if e.tag == tagname:
        l.append(e)
    else:
        for c in e:
            l += getbytag(c, tagname)
    return l

def getbyid(e, id):
    if e.get('id') == id:
        return e
    else:
        for c in e:
            i = getbyid(c, id)
            if i is not None:
                return i
    return None

def inline(e):
    s = e.text or ''
    for c in e:
        if c.tag == 'i':
            s += '\\fI'
        elif c.tag == 'span':
            s += '\\fB'
        elif c.tag == 'br':
            s += '\n\n'
        s += inline(c)
        if c.tag == 'i':
            s += '\\fP'
        elif c.tag == 'span':
            s += '\\fP'
        s += c.tail or ""
    return '\n'.join(l.strip()
                     for l in re.sub('([^a-zA-Z\\\\]|^)-', '\\1\-', s).split('\n'))

captions = {
    'Description:': 'DESCRIPTION',
    'Syntax:': 'SYNOPSIS',
    'Example:': 'EXAMPLES',
    'Examples:': 'EXAMPLES',
    'Examples': 'EXAMPLES',
    'Results:': 'RESULT',
    'Return Codes:': 'MESSAGES',
    'Switches:': 'OPTIONS',
    'Arguments:': 'ARGUMENTS',
    'Arguments (grayscale mode):': 'ARGUMENTS',
    'Arguments (color mode):': 'ARGUMENTS',
    'Column Names:': 'OUTPUT COLUMNS',
}

copyright = '''2001-2015 California Institute of Technology, Pasadena, California
.PP
If your research uses Montage, please include the following
acknowledgement: "This research made use of Montage. It is funded by
the National Science Foundation under Grant Number ACI-1440620, and
was previously funded by the National Aeronautics and Space
Administration\'s Earth Science Technology Office, Computation
Technologies Project, under Cooperative Agreement Number NCC5-626
between NASA and the California Institute of Technology."
.PP
The Montage distribution includes an adaptation of the MOPEX algorithm
developed at the Spitzer Science Center.'''

bugs = '''The drizzle algorithm has been implemented but has not been tested
in this release.
.PP
If a header template contains carriage returns (i.e., created/modified
on a Windows machine), the cfitsio library will be unable to read it
properly, resulting in the error: [struct stat="ERROR", status=207,
msg="illegal character in keyword"]
.PP
It is best for the background correction algorithms if the area
described in the header template completely encloses all of the input
images in their entirety. If parts of input images are "chopped off"
by the header template, the background correction will be affected. We
recommend you use an expanded header for the reprojection and
background modeling steps, returning to the originally desired header
size for the final coaddition. The default background matching assumes
that there are no non-linear background variations in the individual
images (and therefore in the overlap differences). If there is any
uncertainty in this regard, it is safer to turn on the "level only"
background matching (the "\\-l" flag in mBgModel.
'''

sbugs = {
    'mImgtbl': '''.PP
We recommend use of the "\\-c" option when running mImgtbl to include
the locations of the four corners of each image in the output metadata
table. Some other modules may require the corner locations to function
correctly.''',
    'mProjectPP': '''.PP
mProjectPP is only suitable for use on projections which can be
approximated by tangent-plane projections (TAN, SIN, ZEA, STG, ARC),
and is therefore not suited for images covering large portions of the
sky. Also note that it does not directly support changes in coordinate
system (i.e. equatorial to galactic coordinates), though these changes
can be facilitated by the use of an alternate header.''',
    'mTanHdr': '''.PP
mProjectPP is only suitable for use on projections which can be
approximated by tangent-plane projections (TAN, SIN, ZEA, STG, ARC),
and is therefore not suited for images covering large portions of the
sky. Also note that it does not directly support changes in coordinate
system (i.e. equatorial to galactic coordinates), though these changes
can be facilitated by the use of an alternate header.''',
    'mAdd': '''.PP
Although the memory limitation for output images has been overcome in
versions 2.x and above of Montage, it is still possible (though
unlikely) to create an out-of-memory situation due to the size and
number of input images. mAdd builds the output image one row at a
time, and stores every pixel from any input image that contributes to
that row in memory.
.PP
If you have a large enough mosaic, it is almost always more efficient
(and often easier on the user) to tile it. There are tools in Montage
to help with this and these have been brought together under
mAddExec. In fact, even if you want a single output image, it may be
faster to do it in two steps: mAddExec to create a set of tiles, and
then mAdd to make a final mosaic from these tiles. There is absolutely
no loss of information in doing this.''',
    'mOverlaps': '''.PP
mOverlaps generates a list of images whose outer boundaries
overlap. This does not guarantee that any image pixels within those
images actually overlap; the overlap regions may only contain blank
pixels, especially in images that have been rotated a significant
amount.
.PP
This eventually will result in a number of images showing up as
"failed" when running subsequent programs like mDiffExec, but this
will not have any effect on the final mosaic.''',
    'mMakeHdr': '''.PP
Due to some numerical approximation, the "North Up" bounding box
calculated when the \\-n switch is on can be skewed a fraction of a
degree.''',
}

def create_manpage(name, synopsis):
    print name

    tree = etree.parse(open('%s.html' % name), etree.HTMLParser())
    root = tree.getroot()
    usagediv = getbyid(root, 'usage')
    indent = False
    sections = {
        'NAME': '%s \\- %s\n' % (name, synopsis),
        'DESCRIPTION': '',
        'SYNOPSIS': '',
        'EXAMPLES': '',
        'RESULT': '',
        'MESSAGES': '',
        'OPTIONS': '',
        'ARGUMENTS': '',
        'OUTPUT COLUMNS': '',
        'BUGS': bugs + sbugs.get(name, ''),
        'COPYRIGHT': copyright,
    }
    current = None
    for e in usagediv:
        if e.tag == 'dl':
            for ee in e:
                if ee.tag == 'dt':
                    if e.get('class') == 'args':
                        sections[current] += '.TP\n%s\n' % inline(ee)
                    elif e.get('class') == 'example':
                        sections[current] += '.TP\n%s\n' % inline(ee)
                    else:
                        current = captions[ee.text]
                elif ee.tag == 'dd':
                    if (ee.getprevious() is not None
                        and ee.getprevious().tag == 'dd'):
                        sections[current] += '.PP\n'
                    sections[current] += '%s\n' % inline(ee)
        elif e.tag == 'p' and e.get('class') == 'dochead':
            if e.text is not None and len(e.text.strip()) > 0:
                current = captions[e.text]
        elif e.tag == 'p':
            sections[current] += '.PP\n%s\n' % inline(e)
        elif e.tag == 'ul':
            for ee in e:
                txt = inline(ee)
                if current == 'MESSAGES':
                    stat = re.sub('.*stat *= *"([^"]*)".*', '\\1', inline(ee))
                    msg = re.sub('.*msg *= *"([^"]*)"?.*', '\\1', inline(ee))
                    if not msg.startswith("Usage:"):
                        sections[current] += '.TP\n%s\n%s\n' %  (stat, msg)
                else:
                    sections[current] += '.IP\n%s\n' % txt
        elif e.tag in ('a', 'li', 'hr'):
            pass
        else:
            print etree.tostring(e, pretty_print = True)
            raise Exception('%s' % name)

    s = '.TH %s 1 "Dec 2016" "Montage 5" "Montage"\n' % name.upper()

    for current in ['NAME', 'SYNOPSIS', 'DESCRIPTION', 'OPTIONS', 'ARGUMENTS',
                    'RESULT', 'OUTPUT COLUMNS', 'MESSAGES', 'EXAMPLES', 'BUGS',
                    'COPYRIGHT']:
        txt = sections[current]
        if len(txt) > 0:
            s += '.SH %s\n%s\n' % (current, txt)
    return s

tree = etree.parse(open('how.html'), etree.HTMLParser())
root = tree.getroot()
body = root[1]
content = body[0][2]

syn_dict = {
    'mBackground': 'Remove a background plane from a FITS image',
    'mBgExec': 'Run mBackground on all the images in a metadata table',
    'mDAG': 'Create a Directed Acyclic Graph (DAG) for Pegasus',
    'mDAGTbls': 'Construct tables of projected and background corrected images',
    'mDiff': 'Calculates the difference between a pair of overlapping images',
    'mExamine': 'Information about the sky coverage of a FITS image',
    'mShrinkHdr': 'Make a FITS header from a template with different pixel scale',
    'mFitplane': 'Use least squares to fit a plane to an image',
    'mDiffFitExec': 'Run mDiff and mFitplane for each record found by mOverlaps',
    'mMakeImg': 'Generate synthetic FITS images',
    'mTANHdr': 'Determine if there would be an equivalent distorted TAN projection',
    'mTblSort': 'Sorts a table on numeric values',
    'mArchiveList': 'Retrieve a list of archive images from the IRSA server',
    'mArchiveGet': 'Retrieve a single FITS image from a remote archive',
    'mArchiveExec': 'Call mArchiveGet on each image in sequence',
    'mBestImage': 'Determine which image covers a location "best"',
    'mCatMap': 'Point-source imaging program',
    'mHdr': 'Connect to the IRSA service to create a header template',
    'mPix2Coord': 'Output the sky location corresponding to a pixel coordinate',
    'mRotate': 'Rotate a FITS image by an arbitrary angle',
    'mTranspose': 'Re-order axes of multi-dimensional data sets',
    'mProjectCube': 'Reproject a single cube to a user specified scale and coordinate system',
    'mSubCube': 'Create a subimage of a cube',
    'mShrinkCube': 'Reduce the size of a FITS cube',
    'mAddCube': 'Co-add the reprojected cubes to form the output mosaic',
    'mViewer': 'Render multi-dimensional images and large-scale images',
    'mHdrWWT': 'Creates the header template for a specific WWT/TOAST tile',
    'mHdrWWTExec': 'Runs mHdrWWT to create all the header template file needed for WWT TOAST tiles down to a specified level.',
    'mPNGWWTExec': 'Runs mViewer for all the tiles needed for a WWT TOAST set.',
    'mProjWWTExec': 'Runs mProjectQL for all the tiles needed for a WWT TOAST set',
}


for i in (7,9,11):
    tbl = content[i]
    for tr in tbl:
        synopsis = inline(tr[0])
        for a in tr[1]:
            if a.tag == 'a':
                name = a.get('href')[:-5]
                syn_dict[name] = synopsis

tree = etree.parse(open('index2.html'), etree.HTMLParser())
root = tree.getroot()
body = root[1]
content = body[0][2]
for a in getbytag(content, 'a'):
    if a.get('href','').startswith('m'):
        name = a.get('href')[:-5]
        synopsis = syn_dict.get(name)
        s = create_manpage(name, synopsis)
        s = s.replace('ouput', 'output')\
             .replace('succesfully', 'successfully')\
             .replace('extention', 'extension')\
             .replace(u'\xa0', ' ')
        f = open('man/%s.1' % name, 'w')
        f.write(s)
        f.close()
