# Copyright (C) 2007-2026 Ahmet Öztürk (aoz_2@yahoo.com)
#
# This file is part of Lifeograph.
#
# Lifeograph is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Lifeograph is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Lifeograph.  If not, see <http://www.gnu.org/licenses/>.

from LoG import *
import re
import importer_common as ic

VERSION="0.1"

def import_markdown(entry, raw_lines):
    _ORDERED_RE = re.compile(r"^(\d+)\.\s+")
    in_code_block = False
    code_lang = None

    LIST_MAP = {
        "- ": VT.PLS.BULLET,
        "+ ": VT.PLS.BULLET,
        "* ": VT.PLS.BULLET,
        "A. ": VT.PLS.LETTER,
        "a. ": VT.PLS.LETTER_SMALL,
        "I. ": VT.PLS.ROMAN,
        "i. ": VT.PLS.ROMAN_SMALL,
    }

    for raw_line in raw_lines.splitlines():
        line = raw_line.strip()
        if not line:
            continue

        # indentation:
        indent_spaces = len(raw_line) - len(raw_line.lstrip(" "))
        indent_level = indent_spaces // 2

        # indented code block:
        if raw_line.startswith("    ") and not in_code_block:
            p = entry.append_paragraph(line)
            p.set_quot_type(VT.QT.GENERIC)
            p.set_indent_level(1)
            continue
        # fenced code block:
        elif line.startswith("```"):
            if not in_code_block:
                code_lang = ic.get_code_from_str(line[3:])
                in_code_block = True
            else:
                in_code_block = False
                code_lang = None
            continue

        if in_code_block:
            p = entry.append_paragraph(raw_line)
            p.set_quot_type(code_lang)
            p.set_indent_level(1)
            continue

        # block quote:
        quote_depth, line = _consume_block_quote_prefix(line)
        is_quote = quote_depth > 0
        if is_quote:
            indent_level += (quote_depth-1)

        # horizontal rule
        if _is_horizontal_rule(line):
            p = entry.append_paragraph("")
            p.set_hrule(True)
            #p.set_indent_level(indent_level) TODO: not supported in LoG
            continue

        # heading
        h_level = len(line) - len(line.lstrip("#"))
        if h_level > 0:
            line = line[h_level:].lstrip()

        # to-do
        ps = None
        if line.startswith("- ["):
            mark = line[3]
            ps = {
                " ": VT.PLS.TODO,
                "-": VT.PLS.PROGRESSED,
                "x": VT.PLS.DONE,
                "~": VT.PLS.CANCELED,
            }.get(mark)
            line = line[6:].lstrip()

        # ordered list
        m = _ORDERED_RE.match(line)
        if m:
            ps = VT.PLS.NUMBER
            line = line[m.end():]

        # other lists
        if ps is None:
            for k, v in LIST_MAP.items():
                if line.startswith(k):
                    ps = v
                    line = line[len(k):]
                    break

        if ps:
            indent_level += 1

        # paragraph
        p = entry.append_paragraph("")
        p.set_indent_level(indent_level)

        if is_quote:
            p.set_quote(True)
        if ps:
            p.set_list_type(ps)

        match h_level:
            case 1:
                p.set_heading_level(VT.PHS.LARGE)
            case 2:
                p.set_heading_level(VT.PHS.MEDIUM)
            # case 3:
            #     p.set_heading_level(VT.PHS.SMALL) TODO...
            case _:
                p.add_format(VT.FMT.BOLD)

        _parse_inline_formats(p, line)


# inline parsing (overlap-safe, code-isolated)
def _parse_inline_formats(p, text):
    i = 0
    out = []
    stack = []

    def open_fmt(fmt):
        stack.append((fmt, len(out)))

    def close_fmt(fmt):
        for j in range(len(stack) - 1, -1, -1):
            if stack[j][0] == fmt:
                _, start = stack.pop(j)
                p.add_format(fmt, start, len(out))
                return

    while i < len(text):
        # link
        if text[i] == "[":
            label_start = len(out)
            i += 1
            while i < len(text) and text[i] != "]":
                out.append(text[i])
                i += 1
            label_end = len(out)

            if i + 1 < len(text) and text[i + 1] == "(":
                i += 2
                uri = ""
                while i < len(text) and text[i] != ")":
                    uri += text[i]
                    i += 1
                p.add_format(VT.FMT.LINK_URI, label_start, label_end).uri = uri
                i += 1
            continue

        # underline
        if text.startswith("<u>", i):
            open_fmt(VT.FMT.UNDERLINE)
            i += 3
            continue
        if text.startswith("</u>", i):
            close_fmt(VT.FMT.UNDERLINE)
            i += 4
            continue

        # bold / italic / strikethru / highlight
        TOKENS = [
            ("**", VT.FMT.BOLD),
            ("__", VT.FMT.BOLD),
            ("~~", VT.FMT.STRIKETHRU),
            ("==", VT.FMT.HIGHLIGHT),
            ("`", VT.FMT.ITALIC), # no actual support for inline code
            ("*", VT.FMT.ITALIC),
            ("_", VT.FMT.ITALIC),
        ]

        matched = False
        for tok, fmt in TOKENS:
            if not text.startswith(tok, i):
                continue

            if stack and stack[-1][0] == fmt:
                if tok.startswith("_"):
                    if ic.is_boundary_alnum_after(text, i, len(tok)):
                        continue
                close_fmt(fmt)
            else:
                if tok.startswith("_"):
                    if ic.is_boundary_alnum_before(text, i):
                        continue
                open_fmt(fmt)

            i += len(tok)
            matched = True
            break

        if matched:
            continue

        out.append(text[i])
        i += 1

    p.set_text("".join(out))

def _is_horizontal_rule(line: str) -> bool:
    s = line.strip()
    if len(s) < 3:
        return False
    return all(c == s[0] for c in s) and s[0] in "-*_"

def _consume_block_quote_prefix(line: str):
    # returns (depth, rest_of_line)
    # supports: >text, > text, >    > text, >> text, etc.
    i = 0
    depth = 0
    n = len(line)

    while i < n:
        if line[i] == ">":
            depth += 1
            i += 1
            # skip optional spaces after each '>'
            while i < n and line[i] == " ":
                i += 1
        else:
            break

    return depth, line[i:]
