import os
import re
import sys
import textwrap
sys.path.append(f'{os.environ.get("KLAMMERTEXT_HOME")}/sks/kutil')
import kutil

_novalue = '__no_value__'

class E:
    void_elements = "area base br col embed hr img input link meta source track wbr".split()
    def __init__(self, tag):
        self._tag = tag
        self._sty = []
        self._cls = []
        self._body = []
        self._attr = []
        self.no_value = '__novalue__'


    def sty(self, name, value=_novalue):
        if name and value is _novalue:
            self._sty.append(name.strip(';'))
        elif value and value is not _novalue:
            if type(value) is list:
                value = " ".join(value)
            self._sty.append('{}:{}'.format(name, value))
        return self

    def cls(self, c):
        if c:
            self._cls.append(c)
        return self

    def body(self, b, newline=True):
        b = str(b)
        #print("B:", b)
        if newline and (b and b[0] == '<' or '\n' in b or len(b) > 72):
            b = '\n' + b + '\n'
        self._body.append(b)
        return self

    def attr(self, name, value):
        self._attr.append('{}="{}"'.format(name, value))
        return self

    def data(self, name, value="true"):
        attr = 'data-{}'.format(name)
        attr = '{}="{}"'.format(attr, value)
        self._attr.append(attr)
        return self


    def __str__(self):
        def label(name, s):
            return ' {}="{}"'.format(name, s) if s else ''
        c = label('class', " ".join(self._cls))
        s = "; ".join(self._sty).strip(' ;')
        s = label('style', s + ';' if s else '')
        a = " ".join(self._attr)
        a = " " + a.strip() if a.strip() else ""
        b = "\n".join(self._body)
        tag = '{}{}{}{}'.format(self._tag, a, c, s).strip()
        end_tag = '</{}>'.format(self._tag) if self._tag not in E.void_elements else ""
        no_body = self._tag in E.void_elements
        if b and no_body:
            raise Exception(f"Tag {self._tag} incorrectly defines a body.")
        if no_body:
            result = f'<{tag}/>'
        else:
            result = f'<{tag}>{b}{end_tag}\n' # .format(tag, b, end_tag)
        if no_body:
            print(result)
        return result

    def __repr__(self):
        return self.__str__()

    def __add__(self, e):
        return str(self) + '\n' + str(e)

    def str(self, newline=True):
        result = str(self)
        #print(f"begin: |{result}|")
        if newline is None:
            #result = result.strip("\n")
            result = re.sub(">\n", ">", result)
            #print("newline: None")
            #print(f"str: |{result}|\n")
        elif newline:
            result = result.rstrip() + "\n"
        return result

def color(rgb):
    return "rgb(" + ",".join([f"{float(e)*100}%" for e in rgb.split(",")]) + ")"

def html_indent(filename):
    command = "(progn (setq make-backup-files nil) (mark-whole-buffer) "
    command += "(indent-region (point-min) (point-max) nil) (save-buffer))"
    os.system(f'emacs -nw -q --batch {filename} --eval "{command}" --kill 2> /dev/null')

def page(head_elt, body, js_files=[], load_jquery=True):
    if True or js_files and load_jquery:
        body += E("script").attr(
            "src", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js").str()
    for j in js_files:
        body += E("script").attr("src", j).str()
    #body_elt = E("body").attr("id", "body").body(f"\n\n{body}\n\n").str().strip()
    body_elt = E("body").body(f"\n\n{body}\n\n").str().strip()

    result = "<!DOCTYPE html>\n" + E("html").attr("lang", "en").body(head_elt + body_elt).str()
    return result

def head(title, js_files=[], js_code="", css_files=[], css_code="",
         include_fonts=True, google_font=[], favicon=None, load_jquery=True):
    result = '<meta name="viewport" content="width=device-width, initial-scale=1"/>\n'
    result += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\n'
    if favicon:
        result += f'<link rel="icon" type="image/x-icon" href="{favicon}"/>\n'
    result += "".join([E("link").attr("href", e).attr("rel", "stylesheet").str() for e in css_files])
    fonts = None
    css = ''
    if not css_files and not css_code and include_fonts:
        fonts, css = default_fonts()
        fonts = google_font + fonts

    if fonts:
        result += E("link").attr("rel", "preconnect").attr("href", "https://fonts.gstatic.com").str()
        #font = "|".join(google_font)
        for font in fonts:
            result += E("link").attr("href", f"https://fonts.googleapis.com/css?family={font}&display=swap") \
                               .attr("rel", "stylesheet").str()
        #.attr("type", "text/css").str()
    if css + css_code:
        result += E("style").body(css + css_code).str()

    if js_files and load_jquery:
        result += E("script").attr(
            "src", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js").str()
    for j in js_files:
        result += E("script").attr("src", j).str()

    result += E("title").body(title).str().strip()
    result = E("head").body(result).str()
    return result

def default_fonts():
    def font_name(spec):
        return re.compile('[&|:]').split(re.sub(r'\+', ' ', spec))[0]
    #serif = "PT+Serif:ital,wght@0,400;0,700;1,400"
    #serif = "Manuale:ital,wght^@0,400;0,600;1,400"
    serif = "Gentium+Book+Basic:ital,wght^@0,400;0,700;1,400"
    monospace = "Roboto+Mono:ital,wght^@0,400;0,600;1,400"
    sans = "PT+Sans:ital,wght^@0,400;0,700;1,400"
    fonts = [serif, sans, monospace]
    css = f'body {{ font-family: "{font_name(serif)}", serif; margin: 3rem; font-size: 14px; }}\n'
    css += f'tt {{ font-family: "{font_name(monospace)}", sans-serif; font-size: 13px; }}\n'
    css += f'.sans {{ font-family: "{font_name(sans)}", monospace; font-size: 14px; }}\n'
    css += f'h1, h2, h3, h4 {{ font-family: "{font_name(sans)}", monospace; font-size: 20px; }}'
    return fonts, css

def css_reldir():
    return "css.ktdir"

"""
def css_basenames():
    kdir = kutil.klammertext_dir()
    result = []
    for base, sks_dir in kutil.sks_dirs():
        css_filename = f'{sks_dir}/{base}.css'
        if os.path.exists(css_filename):
            result.append(d)
    return result

def css_source_files():
    result = []
    kdir = kutil.klammertext_dir()
    for basename in css_basenames():
        result.append([basename, f"{kdir}/sks/{basename}/{basename}.css"])
    return result
"""

def css(styles, use_sks=False):
    code = f'<style>\n{styles}\n</style>' if styles else ''
    files = []
    if use_sks:
        for basename, filename in kutil.sks_files_of_type("css"):
            files.append(f"{css_reldir()}/{basename}.css")
        """
        kdir = kutil.klammertext_dir()
        css_files = []
        for basename in css_basenames():
            files.append(f'{css_reldir()}/{basename}.css')
        """
    return code, files

def element_tag(element):
    tag_match = re.compile(r"<(\w+).*", re.S).match(str(element))
    if not tag_match:
        raise Exception(f"No tag found in {element}")
    else:
        return tag_match.group(1)

# 1. Relationship of element to caption
# 2. Relationship of element with/without caption to page

def font_class(font):
    return {"r" : "", "i" : "ritalic", "t" : "monospace", "s" : "sanserif"}[font]

def hpos_container(element, hpos, offset=None):
    # Wrap element in its horizontal-position container.  hpos is left,
    # center, right, none, or a length, which becomes the left margin
    # (the tex counterparts are the \LTleft glue for tables and the
    # \hspace* in latex_util.caption_wrapper).
    #
    # offset insets the element from the margin hpos names, as padding on
    # that side of the flex container: the left for "left", the right for
    # "right".  A centered element has no such margin, so the offset is
    # ignored there rather than being an error -- same rule as
    # latex_util.caption_wrapper, which is the tex counterpart.  (It
    # replaces the fixed 1em that the hpos_margin class used to add to
    # every left- and right-placed element: that was this offset, hard
    # coded, and only in html.)
    styles = []
    if hpos not in ("left", "center", "right", "none"):
        length, _, _ = kutil.parse_length("html", hpos, 1)
        styles.append(f"margin-left: {length}")
        hpos = "indent"
    if offset and offset not in ("0", "0pt") and hpos in ("left", "right", "indent"):
        length, _, _ = kutil.parse_length("html", offset, 1)
        side = "right" if hpos == "right" else "left"
        styles.append(f"padding-{side}: {length}")
    result = E("div").cls("hpos_" + hpos).body(element)
    if styles:
        result.attr("style", "; ".join(styles))
    return result


def add_caption(element, caption_label, number, caption_text,
                font_symbol="i", hpos="center", side="bottom", as_string=True,
                font_size=.9, width=None, max_width=None, offset=None):
    tag = element_tag(element)
    # Caption
    caption = ""
    if number:
        caption = kutil.caption_marker(caption_label, caption_text)
    elif caption_text:
        caption = caption_text
    if caption:
        # caption = kutil.protect_klammertext_special_characters(caption)
        import font
        caption = font.html_fontify(caption, font_symbol, font_size)
        caption = E("div").cls("caption").body(caption, newline=False)
        if caption_text and side == "bottom" or side == "top":
            caption.cls("caption_text")

    if caption:
        if side == "left":
            element.cls("lgap")
            caption.cls("rgap")
        elif side == "right":
            element.cls("rgap")
            caption.cls("lgap")
        elif side == "bottom":
            element.cls("bgap")
        elif side == "top":
            element.cls("tgap")            

        if side in {"left", "top"}: 
            element = str(caption) + "\n" + str(element)
        else:
            #element += "\n" + "\n".join(textwrap.wrap(str(caption), 80))
            element += "\n" + re.sub("\n", " ", str(caption))
        #print("-"*80)
        #print(element)
        #print("-"*80)

        element = E("div").cls("caption_" + side).attr("data-label", caption_label).body(element)
        if width:
            # The element's width (e.g. a table's :column_width total) lives
            # on this wrapper: the element fills it, and the caption tracks
            # the element.
            element.attr("style", f"width: {width}")
        elif max_width:
            # A content-sized element (a 'fill' table): the wrapper shrinks
            # to it but never past the text column, so the element wraps at
            # narrow windows instead of overflowing.
            element.attr("style", f"max-width: {max_width}")


    result = hpos_container(element, hpos, offset)

    if number:
        result.cls("element_container")

    if caption and hpos != "none":
        if side == "bottom":
            result.cls("mbot")
        elif side == "top":
            result.cls("mtop")

    if as_string:
        result = str(result) + "\n"
    return result


if __name__ == '__main__':
    elt = E("img").attr("src", "tree.jpg")

    print(elt)
    sys.exit(0)

    import re
    print(css("", True))
    sys.exit(0)

    def make_pars(s):
        s = re.compile(r';(.*?)\.', re.S).sub(r';<tt>\1</tt>.', s)
        s = re.sub('--', '&#8212;', s)
        s = re.compile(r'"(\s)', re.S).sub(r'&#8221;\1', s)
        s = re.compile('"', re.S).sub('&#8220;', s)
        s = re.compile("'", re.S).sub('&#8217;', s)
        s = re.sub("whale", "<i>whale</i>", s)
        s = re.sub("Queequeg", "<b>Queegueg</b>", s)
        s = re.compile(r"(CHAPTER.*?Rope)\.", re.S).sub(r'<span class="sans">\1</span>', s)
        s = re.sub(" and ", ' <span class="sans"><b>and</b></span> ', s)
        pars = re.compile('\n\n+').split(s)
        result = "\n\n".join(["<p>{}</p>".format(e) for e in pars])
        return result

    google_font, css = default_fonts()

    with open("../../../moby/moby-072.txt") as fp:
        txt = make_pars(fp.read())

    hd = head("Moby Dick - Chapter 72", css_code=css, google_font=google_font)
    pg = page(hd, txt)
    filename = "chapter_72.html"
    with open(filename, "w") as fp:
        fp.write(pg)
    #html_indent(filename)
