import re, glob
import kutil
import font

def tex_style():
    result = ""
    for basename, filename in kutil.sks_files_of_type("sty"):
        with open(filename) as fp:
            src = fp.read()
        result += f"% {basename}.sty\n{src.strip()}\n\n"
    return result

# \usepackage{quoting}

def block(material):
    r"""Return tex material as a block: vertical mode on both sides.

    A block element -- a table, an image, a code listing -- is box
    material.  Appended to a non-empty horizontal list (an @image written
    after text on the same line, with no blank line between) a box is
    typeset BESIDE the text rather than below it, and text following it
    flows to its right.  A \par on each side ends the paragraph in
    progress and starts a new one after.  \par in vertical mode is a
    no-op, so this is safe wherever it is applied: a block klammer needs
    no knowledge of what preceded it.

    Every SKS klammer whose tex output is box material returns it through
    this function.  LaTeX ENVIRONMENTS -- center, flushright, quote,
    itemize, verbatim -- already break the paragraph themselves and do
    not need it.

    The paragraph policy this serves: in an @document, paragraphs are
    made from blank-line-separated text (@par is the explicit form for a
    fragment rendered without @document), and the author is never
    required to know that a target distinguishes horizontal from
    vertical mode.  Asserted by sks/tst/paragraph_test.sh.
    """
    return "^'\\par'^\n" + material.strip("\n") + "\n^'\\par'^\n"

def environment(name, body, required=None, optional=None):
    req = f"{{{required}}}" if required else ""
    opt = f"[{optional}]" if optional else ""
    result = f"""\\begin{{{name}}}{opt}{req}
{body}
\\end{{{name}}}
"""
    return result

def page(title, subtitle, leading, pointsize, ragged_right, body):
    title = f"\\textsf{{\\Large {title}}}" if title else ""
    subtitle = f"\\vspace*{{4pt}}\\textsf{{\\large {subtitle}}}" if subtitle else ""
    #leading = f"\\renewcommand{{\\baselinestretch}}{{{leading}}}"
    leading = f"\\setstretch{{{leading}}}"
    raggedright = "\\raggedright" if ragged_right else ""
    if title or subtitle:
        title = f"\\begin{{center}}{title} \\\\ {subtitle}\\end{{center}}\n\n"
    result = f"""\\documentclass[a4paper,{pointsize}]{{article}}
{tex_style()}
{leading}
{raggedright}
\\begin{{document}}
\\thispagestyle{{empty}}
"""
    result += title + body + "\n\\end{document}\n"
    return result

TARGET_ID = 0

#def add_caption(element, label, numbered, caption_text, center, side, width, side_center, vmargin, caption_margin):

def minipage(content, width="\\textwidth", vertical="c", center=True, vmargin="", outline=False):
    vertical = f"[{vertical}]" if vertical else ""
    center = "\n\\centering" if center else ""
    vmargin = f"\n\\vspace*{{{vmargin}}}" if vmargin else ""
    result = f"""\\begin{{minipage}}{vertical}{{{width}}}{center}{vmargin}
{content}{vmargin}
\\end{{minipage}}"""
    if outline:
        result = f"\\fbox{{{result}}}"
    return result

def offset_length(offset):
    r"""An :offset as a LaTeX length, or "0pt" when there is none."""
    if not offset or offset in ("0", "0pt"):
        return "0pt"
    return kutil.parse_length("tex", offset, 1)[0]

def offset_space(offset):
    r"""An :offset as an \hspace*, or "" when there is none."""
    length = offset_length(offset)
    return "" if length == "0pt" else f"\\hspace*{{{length}}}"

def caption_wrapper(element, hpos, bottom_margin=0, offset=None):
    # hpos is left, center, right, none (no wrapper), or a length, which
    # becomes the left margin.  The element is a box on a line inside a
    # full-width minipage; \hfill on the empty side pushes it into place.
    #
    # offset insets the element from the margin hpos names -- from the left
    # for "left", from the right for "right".  A centered element has no
    # such margin, so the offset is ignored there rather than being an
    # error: the two arguments are independent, and :hpos is what decides
    # whether the offset has anything to measure from.  (The html
    # counterpart is html_util.hpos_container.)
    vmargin = f"{bottom_margin}\\baselineskip"
    inset = offset_space(offset)
    if hpos == "center":
        return minipage(element, vmargin=vmargin)
    if hpos == "left":
        return minipage(inset + element + "\\hfill", vmargin=vmargin, center=False)
    if hpos == "right":
        return minipage("\\hfill" + element + inset, vmargin=vmargin, center=False)
    if hpos == "none":
        return element
    length, _, _ = kutil.parse_length("tex", hpos, 1)
    return minipage(f"\\hspace*{{{length}}}" + element + "\\hfill",
                    vmargin=vmargin, center=False)

def make_caption_text(number, label, text, font_symbol, font_size):
    caption = None
    if number:
        caption = kutil.caption_marker(label, text)
    elif text:
        # caption = text   # f"{{\\small \\it \\par {caption_text}}}"
        caption = text

    if caption is None:
        return ""
    caption = font.tex_fontify(caption, font_symbol, font_size)
    return caption

def add_caption(element, caption_label, number, caption_text, latex_width,
                hpos="center", side="bottom", font_symbol="i", font_size=.9,
                offset=None):
    top_margin = .75 if "includegraphics" in element else .5
    caption = make_caption_text(number, caption_label, caption_text, font_symbol, font_size)
    if caption:
        if side in {"left", "right"}:
            caption_margin_size = "12pt"
            caption_margin = f"\\hspace{{{caption_margin_size}}}"
            caption_width = f"\\textwidth - {latex_width} - {caption_margin_size}"
        else:
            #caption_margin = "\\vspace*{8pt}"
            # caption_margin = "\\vstrut{.25\\baselineskip}\n"
            pass

        if side == "left":
            element = minipage(minipage(caption, caption_width, center=False) + #, vmargin=top_margin) +
                               caption_margin +
                               minipage(element, latex_width))
        elif side == "right":
            element = minipage(minipage(element, latex_width) + 
                               caption_margin +
                               minipage(caption, caption_width, center=False))
        elif side == "bottom":
            element = minipage(element + "\n\\vstrut{1.33\\baselineskip}" + caption,
                               # minipage(element, vertical="t") +
                               # caption_margin + "\n" +
                               # minipage(caption),
                               latex_width, vertical="t")
        elif side == "top":
            # A depth strut: the bottom-caption case uses \vstrut (height)
            # above the caption; a top caption needs the mirror image,
            # space below its line.
            element = minipage(caption + "\\rule[-0.75\\baselineskip]{0pt}{0pt}\n" + element,
                               latex_width, vertical="t")
    return caption_wrapper(element, hpos, offset=offset)

def define_color(name, rgb_or_hex):
    if "," in rgb_or_hex:
        r, g, b = [float(e) for e in rgb_or_hex.split(",")]
    else:
        def c(h):
            return float(eval('0x' + h)) / 255.0
        r = c(rgb_or_hex[:2])
        g = c(rgb_or_hex[2:4])
        b = c(rgb_or_hex[4:])
    return r'\\definecolor{{{}}}{{rgb}}{{{:.3f},{:.3f},{:.3f}}}'.format(name, r, g, b)

COLOR_BOX_TEMPLATE = r'''
^'\hfill\begingroup
COLOR
\setlength{\fboxsep}{8pt}
\fcolorbox{bordercolor}{localcolor}{
  \parbox{WIDTH\linewidth}{\raggedright\setlength{\parskip}{8pt}'^
    TEXT
^'}}\endgroup'^
'''.strip()

def color_box(text, width=1, box_color="1,1,1", border_color="0,0,0"):
    color_definitions = define_color("localcolor", box_color) + "\n" \
        +  define_color("bordercolor", border_color)
    result = COLOR_BOX_TEMPLATE
    for old, new in [['TEXT', re.sub(r'\\', r'\\\\', text)],
                     ['COLOR', color_definitions],
                     ['WIDTH', str(width)]]:
        result = re.sub(old, new, result)
    result = block(result)
    return result
