#!/usr/bin/python

# Part of Latex-Suite
#
# Copyright: Srinath Avadhanula
# Description:
#   This file implements a simple outline creation for latex documents.

import re
import os
import sys
import StringIO

# getFileContents {{{
def getFileContents(argin, ext=''):
    if type(argin) is str:
        fname = argin + ext
    else:
            fname = argin.group(3) + ext

    # This longish thing is to make sure that all files are converted into
    # \n seperated lines.
    contents = '\n'.join(open(fname).read().splitlines())

    # TODO what are all the ways in which a tex file can include another?
    pat = re.compile(r'^\\(@?)(include|input){(.*?)}', re.M)
    contents = re.sub(pat, lambda input: getFileContents(input, ext), contents)

    return ('%%==== FILENAME: %s' % fname) + '\n' + contents

# }}}
# stripComments {{{
def stripComments(contents):
    # remove all comments except those of the form
    # %%==== FILENAME: <filename.tex>
    uncomm = [re.sub('%(?!==== FILENAME: ).*', '', line) for line in contents.splitlines()]
    # also remove all only-whitespace lines.
    nonempty = [line for line in uncomm if line.strip()]

    return nonempty
# }}}
# addFileNameAndNumber {{{
def addFileNameAndNumber(lines):
    filename = ''
    retval = ''
    for line in lines:
        if re.match('%==== FILENAME: ', line):
            filename = line.split('%==== FILENAME: ')[1]
        else:
            retval += '<%s>%s\n' % (filename, line)

    return retval
# }}}
# getSectionLabels_Root {{{
def getSectionLabels_Root(lineinfo, section_prefix, label_prefix):
    prev_txt = ''
    inside_env = 0
    prev_env = ''
    outstr = StringIO.StringIO('')
    pres_depth = len(section_prefix)

    #print '+getSectionLabels_Root: lineinfo = [%s]' % lineinfo
    for line in lineinfo.splitlines():
        if not line:
            continue

        # throw away leading white-space
        m = re.search('<(.*?)>(.*)', line)

        fname = m.group(1)
        line = m.group(2).lstrip()

        # we found a label!
        m = re.search(r'\\label{(%s.*?)}' % label_prefix, line)
        if m:
            # add the current line (except the \label command) to the text
            # which will be displayed below this label
            prev_txt += re.search(r'(^.*?)\\label{', line).group(1)

            # for the figure environment however, just display the caption.
            # instead of everything since the \begin command.
            if prev_env == 'figure':
                cm = re.search(r'\caption(\[.*?\]\s*)?{(.*?)}', prev_txt)
                if cm:
                    prev_txt = cm.group(2)

            # print a nice formatted text entry like so
            #
            # >        eqn:label
            # :          e^{i\pi} + 1 = 0
            #
            # Use the current "section depth" for the leading indentation.
            print >>outstr, '>%s%s\t\t<%s>' % (' '*(2*pres_depth+2),
                    m.group(1), fname)
            print >>outstr, ':%s%s' % (' '*(2*pres_depth+4), prev_txt)
            prev_txt = ''

        # If we just encoutered the start or end of an environment or a
        # label, then do not remember this line. 
        # NOTE: This assumes that there is no equation text on the same
        # line as the \begin or \end command. The text on the same line as
        # the \label was already handled.
        if re.search(r'\\begin{(equation|eqnarray|align|figure)', line):
            prev_txt = ''
            prev_env = re.search(r'\\begin{(.*?)}', line).group(1)
            inside_env = 1

        elif re.search(r'\\label', line):
            prev_txt = ''

        elif re.search(r'\\end{(equation|eqnarray|align|figure)', line):
            inside_env = 0
            prev_env = ''

        else:
            # If we are inside an environment, then the text displayed with
            # the label is the complete text within the environment,
            # otherwise its just the previous line.
            if inside_env:
                prev_txt += line
            else:
                prev_txt = line

    return outstr.getvalue()
    
# }}}
# getSectionLabels {{{
def getSectionLabels(lineinfo, 
        sectypes=['chapter', 'section', 'subsection', 'subsubsection'], 
        section_prefix='', label_prefix=''):

    if not sectypes:
        return getSectionLabels_Root(lineinfo, section_prefix, label_prefix)

    ##print 'sectypes[0] = %s, section_prefix = [%s], lineinfo = [%s]' % (
    ##        sectypes[0], section_prefix, lineinfo)

    sections = re.split(r'(<.*?>\\%s{.*})' % sectypes[0], lineinfo)
    
    # there will 1+2n sections, the first containing the "preamble" and the
    # others containing the child sections as paris of [section_name,
    # section_text]

    rettext = getSectionLabels(sections[0], sectypes[1:], section_prefix, label_prefix)
    
    for i in range(1,len(sections),2):
        sec_num = (i+1)/2
        section_name = re.search(r'\\%s{(.*?)}' % sectypes[0], sections[i]).group(1)
        section_label_text = getSectionLabels(sections[i] + sections[i+1], sectypes[1:], 
                                    section_prefix+('%d.' % sec_num), label_prefix)

        if section_label_text:
            sec_heading = 2*' '*len(section_prefix) + section_prefix
            sec_heading += '%d. %s' % (sec_num, section_name)
            sec_heading += '<<<%d\n' % (len(section_prefix)/2+1)

            rettext += sec_heading + section_label_text

    return rettext
    
# }}}

# main {{{
def main(fname, label_prefix):
    [head, tail] = os.path.split(fname)
    if head:
        os.chdir(head)

    [root, ext] = os.path.splitext(tail)
    contents = getFileContents(root, ext)
    nonempty = stripComments(contents)
    lineinfo = addFileNameAndNumber(nonempty)

    return getSectionLabels(lineinfo, label_prefix=label_prefix)
# }}}

if __name__ == "__main__":
    if len(sys.argv) > 2:
        prefix = sys.argv[2]
    else:
        prefix = ''

    print main(sys.argv[1], prefix)


# vim: fdm=marker
