#!/usr/bin/env python3
# XXX: Mit mehreren Leveln von Navigation auch etwas anfangen

import bottle
import sys
import markdown
import re
import urllib
import os
import getopt
import shutil


# Transform a filename into an HTML filename if it ends with .md
def gethtmlfilename(filename):
    ending = re.search("\.[^.]*$", filename)
    if ending is not None:
        filename = filename[:ending.span(0)[0]] + ".html"
    return filename



#
# 
# Remove the header section of the file.
# Input
#  * curdir - the directory of the current file
#  * filetext - the file text as a list, one item per line
#  * wsinfo - the website info structure
# Returns
#  * filetext - input filetext without the navigation part
#  * files - a list of files to be parsed
#  * fileheader - a dictionary of header key-value pairs
#
#
def getheader(curdir, filetext, wsinfo):
    filelines = filetext.split("\n")
    fileheader = {}
    fileheader["linkurls"] = {}
    fileheader["linklvls"] = {}
    fileheader["title"] = ""
    fileheader["header"] = ""
    fileheader["staticfiles"] = {}
    lnr = 0
    while lnr < len(filelines)-1:
        # Get first line
        if not re.search("[+*_-]{3,}", filelines[lnr].strip()):
            break
        # Get last line
        for endlnr in range(lnr+1,len(filelines)):
            if re.search("[+*_-]{3,}", filelines[endlnr].strip()):
                break
        # No last line: Content starts
        if not re.search("[+*_-]{3,}", filelines[endlnr].strip()):
            break
        # Check keyword
        keyword = re.search("bottlemd_([a-zA-Z0-9]*):", filelines[lnr+1].strip())
        if keyword is None:
            break
        fileheader[keyword[1].lower()] = filelines[lnr+2:endlnr]
#        print("Found %d-%d %s: %s" % (lnr, endlnr, keyword[1].lower(), fileheader[keyword[1].lower()]))
        lnr = endlnr

    if re.search("[+*_-]{3,}", filelines[lnr].strip()):
        lnr += 1

    # At this point, all header fields are a list of lines. Treat them accordingly.
    # Navigation: Collect the links
    files = []
    if "navigation" in fileheader:
        indentlevels = []
        # Collect the navigation levels
        for line in fileheader["navigation"]:
            mdlink = re.search("^([ \t]*)[+*-][ \t]*([!]*)\[(.*)\][ ]*\((.*)\)", line)
            if mdlink is None:
                continue
            if len(mdlink[2]) != 0:
                filepath = mdlink[4]
                if filepath.startswith("/"):
                    srcpath = os.path.join(wsinfo.rootdir, filepath)
                else:
                    srcpath = os.path.join(curdir, filepath)

                fileheader["staticfiles"][srcpath] = os.path.join(wsinfo.targetdir, filepath)
                print(srcpath + " -> " + fileheader["staticfiles"][srcpath])
                continue


            curindent = len(mdlink[1])
            linkname = mdlink[3]
            linkurl = mdlink[4]

            if len(indentlevels) == 0:
                indentlevels = [curindent]
            if curindent == indentlevels[-1]:
                pass
            elif curindent > indentlevels[-1]:
                indentlevels.append(curindent)
            else:
                curlevel = next(idx[0] for idx in enumerate(indentlevels) if idx[1] > curindent)
                indentlevels = indentlevels[:curlevel]

            linklvl = len(indentlevels)

            urlparser = urllib.parse.urlparse(linkurl)
            if len(urlparser.scheme) != 0:
                fileheader["linkurls"][linkname] = linkurl
                fileheader["linklvls"][linkname] = linklvl
                continue

            if linkurl.startswith("/"):
                filepath = os.path.join(wsinfo.rootdir, linkurl)
            else:
                filepath = os.path.join(curdir, linkurl)

            files.append(filepath)
            if len(linkname) != 0:
                filepath = gethtmlfilename(filepath)
                fileheader["linkurls"][linkname] = filepath
                fileheader["linklvls"][linkname] = linklvl
#                print("Link: " + linkname + " -> " + filepath)

    # Condense the link levels
    linklvls = list(set(fileheader["linklvls"].values()))
    for linklvl in range(0,len(linklvls)):
        for key in fileheader["linklvls"].keys():
            if fileheader["linklvls"][key] == linklvls[linklvl]:
                fileheader["linklvls"][key] = linklvl

    # Title: Merge all into one line
    if "title" in fileheader:
        fileheader["title"] = " ".join(fileheader["title"])
    # Header: Merge all into one line
    if "header" in fileheader:
        fileheader["header"] = "\n".join(fileheader["header"])

    filetext = ""
    for line in filelines[lnr:]:
        filetext += line + "\n" 

    return(filetext, files, fileheader)


#
#
# Given a filename, create the website with the templates etc.
# Input
#  * curdir - the directory of the current file
#  * filetext - the file text as a list, one item per line
#  * wsinfo - the website info structure
#  * filename - the filename of the file
# Returns
#  * htmlsite - the parsed HTML content of the file
#  * files - a list of files (subsites) to be parsed
#  * staticfiles - a dict of files to be copied 1:1
#
#
def genwebsite_single(curdir, filetext, wsinfo, filename):
    (filetext, files, fileheader) = getheader(curdir, filetext, wsinfo)
    fileheader["filename"] = filename

    tpldict = {}
    maxlevel = 0
    tpldict["bottlemd_navigation"] = ""
    if len(fileheader["linklvls"].keys()) != 0:
        maxlevel = max(fileheader["linklvls"].values()) + 1
    for lvl in range(0, maxlevel):
        navdictkey = "bottlemd_navigation" + str(lvl)
        tpldict[navdictkey] = ""
        print("maxlevel: " + str(lvl))
        for key in fileheader["linkurls"].keys():
            print("Key: (" + str(lvl) + "/" + str(fileheader["linklvls"][key]) + ") " + key)
            if fileheader["linklvls"][key] != lvl:
                continue
            linkdict = {"bottlemd_name": key,
                "bottlemd_url": fileheader["linkurls"][key]}
            tpldict[navdictkey] += bottle.template(wsinfo.templates[navdictkey],
                **linkdict)
    if "bottlemd_navigation0" in tpldict.keys():
        tpldict["bottlemd_navigation"] = tpldict["bottlemd_navigation0"]

    # Parse the file
    if fileheader["filename"].endswith(".md"):
        htmltext = markdown.markdown(filetext)
        # Add header anchors
        headerres = []
        headerres.append(re.compile("(<h1[^>]*)(>)(.*?)(</h1>)", re.DOTALL))
        headerres.append(re.compile("(<h2[^>]*)(>)(.*?)(</h2>)", re.DOTALL))
        headerres.append(re.compile("(<h3[^>]*)(>)(.*?)(</h3>)", re.DOTALL))
        headerres.append(re.compile("(<h4[^>]*)(>)(.*?)(</h4>)", re.DOTALL))
        headerres.append(re.compile("(<h5[^>]*)(>)(.*?)(</h5>)", re.DOTALL))
        headerres.append(re.compile("(<h6[^>]*)(>)(.*?)(</h6>)", re.DOTALL))
        curpos = 0
        for headerre in headerres:
            fullmatch = headerre.search(htmltext, curpos)
            while fullmatch is not None:
                anchornamelist = re.findall("[a-zA-Z0-9]+", fullmatch[3])
                anchorname = ""
                for anc in anchornamelist:
                    anchorname += anc
                newcontent = fullmatch[1] + " id='" + anchorname.lower() + "'" + fullmatch[2] + fullmatch[3] + fullmatch[4]
                htmltext = htmltext[:fullmatch.start()] + newcontent + htmltext[fullmatch.end():]
                curpos = fullmatch.start() + len(newcontent)
                fullmatch = headerre.search(htmltext, curpos)

    else:
        htmltext = filetext



    # If there is no title given, find the first heading in the file.
    if fileheader["title"] == "":
        filelines = filetext.split("\n")
        for line in filelines:
            title = re.search("^#+ (.*)", line.strip())
            print(line.strip())
            if title is not None:
                print("TITLE" + title[1])
                tpldict["bottlemd_title"] = title[1]
                break
    else:
        tpldict["bottlemd_title"] = fileheader["title"]

    tpldict["bottlemd_header"] = fileheader["header"]
    tpldict["bottlemd_sitetext"] = htmltext
    print(tpldict.keys())
    htmlsite = bottle.template(wsinfo.templates["bottlemd_site"], tpldict)

    return (htmlsite, files, fileheader["staticfiles"])


#
#
# Given a filename, create the website, its subsites and write them as files.
# Input
#  * filename - the relative filename of the site to be generated
#  * wsinfo - the website info structure
# Returns
#  * donefiles - the files which are already done (to be passed on as wsinfo.donefiles)
#
#
def genwebsite_file(filename, wsinfo):
    ofilename = os.path.join(wsinfo.sourcedir, filename)
    with open(ofilename, "r") as fp:
        filetext = fp.read()

    curdir = os.path.split(filename)[0]
    (htmltext, files, staticfiles) = genwebsite_single(curdir, filetext, wsinfo, filename)

    targetfile = os.path.join(wsinfo.targetdir, filename)
    with open(targetfile, "w") as fp:
        fp.write(filetext)

    targetfile = gethtmlfilename(targetfile)
    with open(targetfile, "w") as fp:
        fp.write(htmltext)

    for file in staticfiles.keys():
        if file in wsinfo.staticdonefiles:
            continue
        pathfile = os.path.split(staticfiles[file])
        if not os.path.exists(pathfile[0]):
            os.makedirs(pathfile[0])
        shutil.copy(file, staticfiles[file])
        wsinfo.staticdonefiles = wsinfo.staticdonefiles.union([file])

    wsinfo.donefiles = wsinfo.donefiles.union([filename])
    print(files)
    for file in files:
        if file in wsinfo.donefiles:
            continue
        tdonefiles = genwebsite_file(file, wsinfo)
        if tdonefiles is not None:
            wsinfo.donefiles = wsinfo.donefiles.union(tdonefiles)


#
#
# Class to hold the information relevant to building the website so there are not a thousand arguments to every function.
#
#
class WsInfo:
    def __init__(self):
        self.donefiles = set()
        self.staticdonefiles = set()
        self.templates = {}
        with open("bottlemd_site.tpl", "r") as fp:
            self.templates["bottlemd_site"] = fp.read()
        print("Loaded template bottlemd_site.tpl")

        navlvl = 1
        while True:
            navkey = "bottlemd_navigation" + str(navlvl)
            if not os.path.exists(navkey + ".tpl"):
                break
            with open(navkey + ".tpl", "r") as fp:
                self.templates[navkey] = fp.read()
            print("Loaded template " + navkey + ".tpl")
            navlvl += 1

        if os.path.exists("bottlemd_navigation.tpl"):
            with open("bottlemd_navigation.tpl", "r") as fp:
                self.templates["bottlemd_navigation"] = fp.read()
                self.templates["bottlemd_navigation0"] = self.templates["bottlemd_navigation"]
            print("Loaded template bottlemd_navigation.tpl")
        elif self.templates.haskey("bottlemd_navigation0"):
            self.templates["bottlemd_navigation"] = self.templates["bottlemd_navigation0"]

        self.targetdir = "website"
        self.sourcedir = os.getcwd()
        self.rootdir = "/"


wsinfo = WsInfo()

startfile = "index.md"
opts, args = getopt.getopt(sys.argv[1:], "s:t:i:")
for opt,arg in opts:
    if opt == "-s":
        wsinfo.sourcedir = arg
    elif opt == "-t":
        wsinfo.targetdir = arg
    elif opt == "-i":
        startfile = arg

genwebsite_file(startfile, wsinfo)

