import os
import re
import inspect
import html_util
from html_util import E


def black_text():
    return "\033[0;30m"

def blue_text(text):
    return f"\033[0;34m{text}\033[0m"

def red_text(text):
      return f"\033[31m{text}\033[0m"

def msg(text=""):
    frame = inspect.currentframe().f_back
    print(blue_text(f"[{os.path.basename(frame.f_code.co_filename)}:{frame.f_lineno}]"), text)


def escape(s):
    result = s
    # These were commented out -- has it been replaced?
    # It's only used in kutil/klammer_base.py and block/block.py
    # and does nothing now.
    #result = re.sub(r"\b", r"\b", result)
    #result = re.sub(r"\t", r"\t", result)
    #result = re.sub("\f", r"\\f", result)
    #result = re.sub("\v", r"\\v", result)
    #result = re.compile("\\(.)").sub(r"\\\1", result)
    return result

def klammertext_dir():
    var = 'KLAMMERTEXT_HOME'
    kdir = os.environ.get(var)
    if kdir is None:
        raise Exception(
            f"The environment variable {var} must be defined as the top-level Klammertext directory")
    return kdir

def cache_directory(relative_pathname, basename):
    directory = relative_pathname
    if not os.path.isdir(directory):
        os.makedirs(directory)
    result = directory  + "/_klammertext_cache/" + basename
    return result;

def sks_dirs():
    result = 'kutil book document block font link section list image table code color'.split()
    k = klammertext_dir()
    result = [[e, f"{k}/sks/{e}"] for e in result]
    return result

def sks_files_of_type(extension):
    result = []
    for base, sks_dir in sks_dirs():
        filename = f'{sks_dir}/{base}.{extension}'
        if os.path.exists(filename):
            result.append([base, filename])
    return result

def make_dir_if_necessary(d, delete_contents=False):
    if not os.path.exists(d):
        os.makedirs(d)
    if delete_contents:
        os.system(f'rm -rf {d}/*')

def protect_klammertext_special_characters(text):
    result = text
    result = re.compile(r"\^").sub("H̅", result)
    result = re.compile(r"@").sub("A̅", result)
    result = re.compile(r":").sub("̅C̅", result)
    result = re.compile(r"\|").sub("̅B̅", result)
    return result

# When did this ever make sense?  Before the new word patterns, probably.

def bar_delimiter():
    return "\3"

def double_bar_delimiter():
    return "\4"

def caption_delimiter():
    return "__CAPTION__"

def caption_marker(name, caption, delimiter=" - "):
    caption = re.sub("\n", " ", caption)
    d = caption_delimiter()
    if caption:
        caption = f"{delimiter}{caption}"
    return f"{d}{name}{d}{caption}{d}"

def rest_split(s, dimensions=1):
    """Split bar-delimited text into nested lists, one level per dimension.

    The delimiter for dimension n is a run of exactly n bar characters:
    | separates elements, || separates lists of elements, ||| lists of
    lists, and so on.  This is the cast behind the rest(N) argument type.
    One trailing top-level delimiter (the customary dangling separator
    before a closing @) is removed; all other empty elements are
    preserved, so a trailing | still makes an empty final cell.
    """
    s = s.strip()
    if not s:
        return [] if dimensions > 0 else s
    delimiter = "|" * dimensions
    if s.endswith(delimiter) and not s.endswith("|" + delimiter):
        s = s[:-len(delimiter)]
    return _rest_split_level(s, dimensions)

def _rest_split_level(s, dimensions):
    if dimensions <= 0:
        return s.strip()
    pattern = re.compile("(?<!\\|)" + "\\|" * dimensions + "(?!\\|)")
    return [_rest_split_level(part, dimensions - 1) for part in pattern.split(s)]

def rest_args(s, dimensions=1):
    return rest_split(s, dimensions)

def parse_length(target, s, rel_fraction):
    def choose(html_value, tex_value):
        return html_value if target == "html" else tex_value
    pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:"([^"]+)")')
    #pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:{([^}]+)})')


    #pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(f)')
    match = pat.match(s)
    if match is None: # But already checked by the klammer
        raise Exception(f'The argument "{s}" is not a length')

    #print(match.groups())
    num, units, fit, text = match.groups()
    num = float(num) if num else ""

    num *= rel_fraction

    if fit:
        result = "f" # choose("100vw", "\\textwidth")
    elif text:
        text = re.compile(r"\{\}\\textbackslash\{\}").sub(r"\\", text)
        if text[0] == "-":
            result = choose("", f"\\textwidth - \\widthof{{  {text[1:]}}}")
        else:
            result = choose("", f"\\widthof{{  {text}}}")
    elif units == "w":
        result = choose(f"{100 * num}vw", f"{num}\\textwidth")
    elif units == "h":
        result = choose(f"{100 * num}vh", f"{num}\\textheight")
    elif units == "pw":
        result = choose(f"{100 * num}vw", f"{num}\\paperwidth")
    elif units == "ph":
        result = choose(f"{100 * num}vh", f"{num}\\paperheight")
    elif units == "px":
        result = choose(f"{round(num)}", f"{num}px")
    else:
        result = f"{num}{units}"
    result = re.sub("\t", "\\t", result)
    return result, num, units


def old_parse_length(target, s):
    pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|("([^"]+)")')
    match = pat.match(s)
    if match is None:
        raise Exception(f'The argument "{s}" is not a length')
    if match.group(3) in {"none", "f"}:
        return "f"
    if s[0] == '"':
        if target in {"tex", "pdf"}:
            return f"\\widthof{{{match.group(5)} }}"
        else:
            return ""
    num, units = match.groups()[:2]
    if float(num) == 0:
        return None
    result = f"{num}{units}"
    if target in {"tex", "pdf"}:
        if units == "w":
            result = fr"{num}\textwidth"
        elif units == "h":
            result = fr"{numb}\textheight"
    elif target in {"html"}:
        scale_x = None
        scale_y = None
        if units == "w":
            #result = fr"{int(float(num)*100)}vw"
            result = fr"calc({int(float(num)*100)}vw - 2rem)"
            #result = 0
            scale_x = num
            result = "none"
        elif units == "h":
            result = fr"{int(float(num)*100)}vw"
            #result = 0
            scale_y = num
            result = "none"
        result = [result, scale_x, scale_y]
    else:
        raise Exception(f"Uknown length: '{s}'")
    return result

def parse_lengths(target, s):
    # Fill strings with space character for the split:
    #print("parse_lengths:", s)
    def replace(match):
        return re.sub(r"\s", "~", match.group(1))
    result = re.compile(r'("[^"]+")').sub(replace, s)
    #print(result, result.split())
    result = [parse_length(target, e)[0] for e in result.split()]
    return result

paragraph_separator_re = re.compile(r'\n *\n', re.S)

def format_for_paragraphs(s):
    result = s
    if len(paragraph_separator_re.findall(result)) > 0:
        result = '\n\n{}\n\n'.format(result)
    return result



def filename_list(text):
    """Split a filename list; the Python twin of resolve_filename_list()
    in mac/file.cpp (keep the two in sync).  A standalone "/" (whitespace
    on both sides) separates names, whose inner spacing is preserved;
    without a separator, whitespace-separated tokens that do not name
    existing files are rejoined with their neighbors into names that do.
    A leading ~ expands to the home directory."""
    text = text.strip()
    if not text:
        return []
    parts = re.split(r'(?:^|(?<=\s))/(?:\s|$)', text)
    if len(parts) > 1:
        return [os.path.expanduser(p.strip()) for p in parts if p.strip()]
    tokens = text.split()
    result, i = [], 0
    while i < len(tokens):
        if os.path.isfile(os.path.expanduser(tokens[i])):
            result.append(tokens[i])
            i += 1
            continue
        acc, j, found = tokens[i], i + 1, False
        while j < len(tokens):
            acc += " " + tokens[j]
            j += 1
            if os.path.isfile(os.path.expanduser(acc)):
                result.append(acc)
                i = j
                found = True
                break
        if not found:
            result.append(tokens[i])
            i += 1
    return [os.path.expanduser(p) for p in result]
