#!/usr/bin/env python3

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

import re
import os
import sys
if sys.version_info <= (3, 0):
    from StringIO import StringIO
else:
    from io import StringIO


def getFileContents(fname):
    if type(fname) is not str:
        fname = fname.group(3)

    # If neither the file or file.tex exists, then we just give up.
    if not os.path.isfile(fname):
        if os.path.isfile(fname + '.tex'):
            fname += '.tex'
        else:
            return ''

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

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

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


def stripComments(contents):
    # remove all comments except those of the form
    # %%==== FILENAME: <filename.tex>
    # BUG: This comment right after a new paragraph is not recognized:
    # foo\\%comment
    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


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


def getSectionLabels_Root(lineinfo, section_prefix, label_prefix):
    prev_txt = ''
    inside_env = 0
    prev_env = ''
    outstr = StringIO('')
    pres_depth = len(section_prefix)
    indent = ' ' * (2*pres_depth + 2)

    #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'\\(?:nl)?label{(%s.*?)}' % label_prefix, line)
        if m:
            # Get the corresponding label
            label = m.group(1)

            # add the current line (except the \label command) to the text
            # which will be displayed below this label
            prev_txt += re.search(r'(^.*?)\\(?:nl)?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.
            outstr.write('>%s%s\t\t<%s>\n' % (indent, label, fname))
            outstr.write(':%s  %s\n'       % (indent, 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|align|figure)', line):
            prev_txt = ''
            prev_env = re.search(r'\\begin{(.*?)}', line).group(1)
            inside_env = 1

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

        elif re.search(r'\\end{(equation|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()


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


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

    contents = getFileContents(fname)
    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))
