# (C) Copyright 2004-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines the HTML editor factory. HTML editors interpret and display
    HTML-formatted text, but do not modify it.
"""

from traits.api import Bool, Str

from traitsui.basic_editor_factory import BasicEditorFactory
from traitsui.toolkit import toolkit_object

# Callable that returns the editor to use in the UI.


def html_editor(*args, **traits):
    return toolkit_object("html_editor:SimpleEditor")(*args, **traits)


# Template used to create code blocks embedded in the module comment
block_template = """<center><table width="95%%"><tr><td bgcolor="#ECECEC"><tt>
%s</tt></td></tr></table></center>"""

# Template used to create lists embedded in the module comment
list_template = """<%s>
%s
</%s>"""

# ------------------------------------------------------------------------------
#  'ToolkitEditorFactory' class:
# ------------------------------------------------------------------------------


class ToolkitEditorFactory(BasicEditorFactory):
    """Editor factory for HTML editors."""

    # --------------------------------------------------------------------------
    #  Trait definitions:
    # --------------------------------------------------------------------------

    #: Should implicit text formatting be converted to HTML?
    format_text = Bool(False)

    #: External objects referenced in the HTML are relative to this url
    base_url = Str()

    #: The object trait containing the base URL
    base_url_name = Str()

    #: Should links be opened in an external browser?
    open_externally = Bool(False)

    def parse_text(self, text):
        """Parses the contents of a formatted text string into the
        corresponding HTML.
        """
        text = text.replace("\r\n", "\n")
        lines = [("." + line).strip()[1:] for line in text.split("\n")]
        ind = min(
            *(
                [self.indent(line) for line in lines if line != ""]
                + [1000, 1000]
            )
        )
        if ind >= 1000:
            ind = 0
        lines = [line[ind:] for line in lines]
        new_lines = []
        i = 0
        n = len(lines)
        while i < n:
            line = lines[i]
            m = self.indent(line)
            if m > 0:
                if line[m] in "-*":
                    i, line = self.parse_list(lines, i)
                else:
                    i, line = self.parse_block(lines, i)
                new_lines.append(line)
            else:
                new_lines.append(line)
                i += 1
        text = "\n".join(new_lines)
        paragraphs = [p.strip() for p in text.split("\n\n")]
        for i, paragraph in enumerate(paragraphs):
            if paragraph[:3].lower() != "<p>":
                paragraphs[i] = "<p>%s</p>" % paragraph
        return "\n".join(paragraphs)

    def parse_block(self, lines, i):
        """Parses a code block."""
        m = 1000
        n = len(lines)
        j = i
        while j < n:
            line = lines[j]
            if line != "":
                k = self.indent(line)
                if k == 0:
                    break
                m = min(m, k)
            j += 1
        j -= 1
        while (j > i) and (lines[j] == ""):
            j -= 1
        j += 1
        temp = [
            (("&nbsp;" * (self.indent(line) - m)) + line.strip())
            for line in lines[i:j]
        ]
        return (j, block_template % "\n<br>".join(temp))

    def parse_list(self, lines, i):
        """Parses a list."""
        line = lines[i]
        m = self.indent(line)
        kind = line[m]
        result = ["<li>" + line[m + 1 :].strip()]
        n = len(lines)
        j = i + 1
        while j < n:
            line = lines[j]
            k = self.indent(line)
            if k < m:
                break
            if k == m:
                if line[k] != kind:
                    break
                result.append("<li>" + line[k + 1 :].strip())
                j += 1
            elif line[k] in "-*":
                j, line = self.parse_list(lines, j)
                result.append(line)
            else:
                result.append(line.strip())
                j += 1
        style = ["ul", "ol"][kind == "*"]
        return (j, list_template % (style, "\n".join(result), style))

    def indent(self, line):
        """Calculates the amount of white space at the beginning of a line."""
        return len(line) - len((line + ".").strip()) + 1


HTMLEditor = ToolkitEditorFactory(klass=html_editor)
