if __name__ == "__main__":
    import sys
    sys.path.append("../kutil")
    sys.path.append("../target")

import re
import klammer_base
import kutil
import html_util
from html_util import E
import latex_util as L
import pprint
import phases

def escape_newlines(s):
    return re.sub("\n", " ___NL___ ", s)

# def literal_newline(s):
#     def replace(match):
#         before, after = match.groups()
#         return f"{before}\\n{after}"
#     backslash_pat = re.compile(r'(".*?)\n(.*?")', re.S)
#     return backslash_pat.sub(replace, s)    

def get_blocks(s):
    comment_pat = re.compile(r"(\s*)//(\d+)\s+(.*)", re.S)
    blocks = []
    lines = s.strip("\n").split("\n")
    i = 0
    uncommented = ""
    while i < len(lines):
        match = comment_pat.match(lines[i])
        if match:
            if uncommented:
                blocks.append([uncommented.rstrip(), None])
                uncommented = ""
            count = int(match.group(2))
            comment = match.group(3)
            code = ""
            j = 0
            i += 1
            while j < count:
                line = re.sub("\n", "\\n", lines[i])
                code += line + "\n"
                j += 1
                i += 1
            blocks.append([code.strip("\n"), comment])
        else:
            uncommented += lines[i] + "\n"
            i += 1
    if uncommented:
        blocks.append([uncommented.rstrip(), None])
    return blocks

def latex_spaces(s):
    def replace(match):
        s = match.group(0)
        if False and len(s) == 1:
            return "~"
        else:
            result = "~" * len(s)
            result = f"\\hphantom{{{result}}}"
            return result
    space_pat = re.compile(" +", re.S)
    return space_pat.sub(replace, s)

def latex_unquote(s):
    quoted = "asciicircum quotesingle asciigrave asciitilde asciitilde backslash".split()
    quoted = [f"{{}}\text{e}{{}}" for e in quoted]
    result = s
    for q in quoted:
        result = re.sub(q, "X", result)
    result = re.sub(" ", "Y", result)
    return result

def longest_line(s):
    result = ""
    for line in latex_unquote(s).split("\n"):
        if len(line) > len(result):
            result = line
    return result

def literal_newline(s):
    def replace(match):
        before, after = match.groups()
        return f"{before}\\n{after}"
    backslash_pat = re.compile(r'(".*?)\n(.*?")', re.S)
    return backslash_pat.sub(replace, s)

class Code(klammer_base.Klammer_base):
    id = 0
    def __init__(self, K):
        super().__init__(K)
        self.text = phases.expand_whitespace_markers(self.text)

    def html(self):
        if self.K_target == "html":
            self.text = literal_newline(self.text)
        # Escape Klammertext special characters so they survive
        # re-insertion into the katom stream after @eval
        self.text = self.text.replace("^", "^^")
        self.text = self.text.replace("#", "^#")
        self.text = self.text.replace("@", "^@")
        self.text = self.text.replace("|", "^|")
        self.blocks = get_blocks(self.text)
        result = ''
        for text, comment in self.blocks:
            border = "code_border" if comment else "code_no_border"
            body = E("div").body(text).cls(f"code_text {border}").str(None)
            if comment:
                body += "\n" + E("div").body(comment).cls("code_comment").str()
            result += E("div").body(body).cls("code_block").str()
        if self.number or self.caption:
            #result = html_util.add_caption(
            #    result, "Listing", self.number, self.caption, "i", "left", "top")
            caption = kutil.caption_marker("Listing", self.caption)
            result = f'<div class="plain_caption code_caption">{caption}</div>{result}\n'
        return result

    @staticmethod
    def escape_latex(s):
        """Escape LaTeX special characters in code text."""
        # Backslash must be first (before adding more backslashes)
        s = s.replace("\\", "\\textbackslash{}")
        s = s.replace("{", "\\{")
        s = s.replace("}", "\\}")
        s = s.replace("%", "\\%")
        s = s.replace("$", "\\$")
        s = s.replace("&", "\\&")
        s = s.replace("_", "\\_")
        s = s.replace("^", "\\textasciicircum{}")
        s = s.replace("~", "\\textasciitilde{}")
        s = s.replace("<", "\\textless{}")
        s = s.replace(">", "\\textgreater{}")
        return s

    def tex(self):
        # Escape Klammertext special characters
        self.text = self.text.replace("^", "^^")
        self.text = self.text.replace("#", "^#")
        self.text = self.text.replace("@", "^@")
        self.text = self.text.replace("|", "^|")
        # Escape LaTeX special characters in code text
        self.text = Code.escape_latex(self.text)
        self.blocks = get_blocks(self.text)
        strutvis = "0pt"
        start_strut = f"\\rule[0pt]{{{strutvis}}}{{12pt}}"
        end_strut = f"\\rule[-6pt]{{{strutvis}}}{{12pt}}"
        caption_strut = f"\\rule[-8pt]{{{strutvis}}}{{6pt}}"
        indent = "8pt"
        comment_sep = "10pt"
        i = 0
        result = ""
        count = len(self.blocks)
        for text, comment in self.blocks:
            text = " " + re.sub("\n", " \n ", text) + " "
            longest = longest_line(text)
            text = latex_spaces(text)
            text = re.sub("\n", r"\\\\", text)
            text = f"{start_strut}\\ttfamily {text}{end_strut}"
            width = f"\\widthof{{\\ttfamily {longest}}}"
            code = L.environment("minipage", text, width) + "\\\\\n"
            if comment:
                width = f"\\linewidth - {width} - {indent} - {comment_sep}"
                code =  f"\\fcolorbox{{Gray}}{{LightGray}}{{{code}}}"
                code += f"\\rule{{{comment_sep}}}{{{strutvis}}}" \
                    + L.environment("minipage", "\\sffamily\\small\\raggedright " + comment, width)
            result += f"\\rule{{{indent}}}{{{strutvis}}}{code}"
            if comment:
                if i < count - 1 and self.blocks[i+1][1]:
                    result += "\\\\[4pt]"
            i += 1
        if not self.blocks[count-1][1]:
            result = result[:-4]
        if self.number or self.caption:
            caption = kutil.caption_marker("Listing", self.caption)
            if self.blocks[0][1]:
                caption += caption_strut
            strut = f"\\rule{{{indent}}}{{{strutvis}}}"
            result = f"{strut}\\emph{{\\it {caption}}}\\newline\n" + result + "\n"
        result = f"\\hypertarget{{Reference-Listing-{Code.id}}}{{}}\n{result}"
        Code.id += 1
        return result

def undash(s):
    result = s
    result = re.sub("__MDASH__", "---", result)
    result = re.sub("__NDASH__", "--", result)
    return result

class Code_fragment(klammer_base.Klammer_base):
    def __init__(self, K):
        super().__init__(K)

    def html(self):
        #print(f"code: |{self.code_text}|")
        result = self.code_text.strip()
        result = undash(result)
        #result = re.escape(result)
        result = re.sub("<", "&lt;", result)
        result = re.sub(" ", "&nbsp;", result)
        #print(f"code: |{self.code_text}| -> |{result}|")
        return f'<span class="code">{result}</span>'
    
    def tex(self):
        return f"{{\\tt {self.code_text.strip()}}}"

def show(s):
    print("-"*80)
    print(s)
    print("-"*80)

class Source(klammer_base.Klammer_base):
    def __init__(self, K):
        super().__init__(K)
        with open(self.filename) as fp:
            self.src = fp.read()

    def tex(self):
        result = self.src
        # result = re.sub("#", "^#", result)
        # result = re.sub("\\^", "\\^", result)
        result = f"\\begin{{verbatim}}\n{result}\n\\end{{verbatim}}\n"
        return result

    def html(self):
        result = escape_newlines(self.src.strip()) + "\n"
        result = re.sub("@", "^@", result)
        result = E("div").body(result).cls("code_text").str()
        return result

# --------------------------------------------------------------------------------

if __name__ == "__main__":
    s = """
int main(int argc, char* argv[])
{
    //1 One line commented
    int count = 12;
    //3 Two lines commented
    for (int i = 0; i <  count; i++) {
        std::cout << "Counter: " << i << "\n";
    }
    //1 A really long comment for one line. A really long comment for one line. A really long comment for one line.
    std::cout << "End\n";
}
"""
    get_blocks(s);


# class Code(klammer_base.Klammer_base):
#     def __init__(self, K):
#         super().__init__(K)
#         #self.show("Code")
#         if self.filename and self.text:
#             raise Exception("Both :text and :filename cannot be defined")
#         if K.filename:
#             with open(K.filename) as fp:
#                 self.src = fp.read()
#             if K.pattern:
#                 rgx = re.compile(f".*?({K.pattern}).*", re.S)
#                 match = rgx.match(self.src)
#                 if match is None:
#                     raise Exception(f"Match fails for @source_code: {K.pattern}")
#                 self.src = match.group(1)
#             self.src = kutil.protect_klammertext_special_characters(self.src)
#         else:
#             self.src = self.text

#     def html(self):
#         #src = re.sub("\n", "<!-- -->", self.src) ?
#         #result = f'<pre class="code">\n{self.src}\n</pre>\n'
#         result = self.src
#         result = undash(result)
#         result = f'<pre>\n{result}\n</pre>\n'
#         return result

#     def tex(self):
#         src = self.src
#         src = re.sub(r"\\{", "{", src)
#         src = re.sub(r"\\}", "}", src)
#         result = f"\\begin{{lstlisting}}\n{src}\n\\end{{lstlisting}}\n"
#         return result

#     def txt(self):
#         return "x~   " + self.src

# class Pathname(klammer_base.Klammer_base):
#     def __init__(self, K):
#         super().__init__(K)
    
#     def html(self):
#         return f'<span class="monospace">{self.s}</span>'

#     def tex(self):
#         result = self.s
#         def replace(match):
#             return '\\{}'.format(match.group(1))
#         result = re.sub(r'\\', 'XXXBACKSLASHXXX', result)
#         result = re.compile('\s*__UNSPACE__\s*', re.S).sub('', result)
#         result = re.compile(r'([&${}%#_])').sub(replace, result)
#         result = re.sub('\^', r'\\^{}', result)
#         result = re.sub('~', r'\\~{}', result)
#         result = re.sub(r'XXXBACKSLASHXXX', r'{\\textbackslash}', result)
#         result = re.sub('\n', r'~\\\\\n', result.strip())
#         result = re.sub(' ', '$~$', result)
#         result = re.sub("'", r"{\\textquotesingle}", result)
#         result = re.sub('"', r'{\\textquotedbl}', result)
#         result = re.sub('--', '{-}{-}', result)
#         result = r'{{\normalfont\texttt{{{}}}}}'.format(result.strip())
#         result = re.sub(r'\{\\textbackslash\}\\#', '\\#', result)
#         if self.small:
#             result = '{{\\footnotesize{}}}'.format(result)
#         return result
    
#     def txt(self):
#         return f"'{self.s}'"

