import ntpath
import re

from bs4 import BeautifulSoup
from slugify import slugify

'''
    The resulted toc tree is in a recursive dictionary, for example

    ===============================================================
    {
        "title": "section 1",
        "slugified": "section-1",
        "link": "sample/part1.htm"
        "children": [
            {
                "title": "section 1.1",
                "slugified": "section-1-1",
                "link": "sample/part2.htm"
                "children": []
            }
        ]
    }
    ===============================================================

    title:      the title as in the table of the content
    slugified:  filename-friendly version of the title
    link:       the relative file name of the html page
    children:   a list of immediate children of the current heading
    '''

# keys in dict
TITLE = 'title'
SLUGIFIED = 'slugified'
LINK = 'link'
CHILDREN = 'children'


def construct_toc_tree(root_title, toc_html_path):
    # specific to the HTML format generated by Adobe Acrobat 2015 DC
    # get a list of heading tags in the table of content HTML
    file = open(toc_html_path, 'r')
    html_content = file.read()
    soup = BeautifulSoup(html_content, 'html.parser')
    all_tags = soup.find_all(attrs={
        'class': re.compile('toc\d+')
    })

    # construct the root node
    result = dict()
    result[TITLE] = root_title
    result[SLUGIFIED] = slugify(root_title)
    result[LINK] = ntpath.basename(toc_html_path)
    result[CHILDREN] = list()

    # parse the tag list
    while len(all_tags) > 0:
        recursive_parse(all_tags, result, -1)

    # clean up and return
    file.close()
    return result


def recursive_parse(tags, node, level):
    # terminal condition: when there is no tag to be processed
    if len(tags) == 0:
        return tags

    else:

        # get the first tag from the queue and get the level number
        # level numbers start from 0 (Heading 1) to whatever it takes
        tag = tags[0]
        next_level = int(tag['class'][0].replace('toc', ''))

        '''
        The next node (tag) has two possible cases
        1)  the immediate child of the current node, or
        2)  a node that is the grandchild of the current node
        '''

        # CASE ONE: process all following nodes that are immediate children of the current node
        while level + 1 == next_level:

            # append to the children list of the current node
            node[CHILDREN].append(
                {
                    TITLE: tag.text,
                    SLUGIFIED: slugify(tag.text),
                    LINK: tag['href'],
                    CHILDREN: list()
                }
            )

            # remove the first tag from the queue after processing it
            tags.pop(0)

            # if no tag remains, terminate
            if len(tags) == 0:
                break

            # re-get the first tag and its level from the queue
            tag = tags[0]
            next_level = int(tag['class'][0].replace('toc', ''))

        # CASE TWO: leave the grandchild to be processed by the last child node of the current node
        if next_level > level + 1:
            recursive_parse(tags, node[CHILDREN][-1], level + 1)
