import lxml.etree


def has_class(klass):
    return "[contains(concat(' ',normalize-space(@class),' '),' " + klass + " ')]"


def add_class(elem, klasses):
    if not isinstance(klasses, list):
        klasses = [klasses]
    existing = elem.attrib.get("class", "").split()
    to_add = [klass for klass in klasses if klass not in existing]
    if to_add:
        elem.attrib["class"] = " ".join(existing + to_add)


def remove_class(elem, klasses):
    if not isinstance(klasses, list):
        klasses = [klasses]

    elem.attrib["class"] = " ".join(
        [x for x in elem.attrib.get("class", "").split() if x not in klasses]
    )


def get_icon(element, clear=False):
    icon = None
    for sub in element.xpath('*[contains(@class, "icon")]'):
        for klass in sub.attrib.get("class", "").split(" "):
            if klass.startswith("icon-"):
                icon = klass
                break

        if icon and clear:
            remove_class(sub, [icon, "icon"])
    return icon


def transform_buttons(tree):
    # btn -> button
    for elm in tree.find_class("btn"):
        primary = "btn-primary" in elm.attrib["class"]
        remove_class(elm, ["btn-primary", "btn-lg", "btn"])
        add_class(elm, "button")

        # icon
        icon = get_icon(elm, clear=True)
        if icon:
            to_add = ["icon", icon]
            if primary:
                to_add += ["bicolor"]
            add_class(elm, to_add)


def transform_menu_items(tree):
    # grab active main menuitem and insert its icon in the header
    for menuitem in tree.xpath("//nav//li" + has_class("menu-active")):
        icon = get_icon(menuitem)
        if icon:
            for h1 in tree.xpath("//header//h1"):
                add_class(h1, ["icon", icon])


def transform_listings(tree):
    # listings
    to_remove = "table table-striped table-bordered table-hover".split()
    for table in tree.xpath("//table" + has_class("table")):
        remove_class(table, to_remove)
        table.attrib["class"] = "listing " + table.attrib["class"]

        for caption in table.xpath("caption"):
            add_class(caption, "table-headers")

        for button in table.xpath("caption//a" + has_class("button")):
            remove_class(button, "button")


def transform_icons(tree):
    # icons
    for elem in tree.xpath("//*[contains(@class, 'icon-')]"):
        add_class(elem, "icon")


def transform_forms(tree):
    # forms
    for form in tree.xpath("//form"):
        # Disable all html5 validation in the backend.
        form.attrib["novalidate"] = ""

        remove_class(form, "well")

        for elm in form.xpath("*" + has_class("form-group")):
            remove_class(elm, "form-group")
            elm.tag = "li"  # this should be an li
            if elm.xpath("label" + has_class("required")):
                add_class(elm, "required")

            for div in elm.iterchildren("div"):
                div.attrib["class"] = "field-content"

                for form_control in div.xpath("*" + has_class("form-control")):
                    remove_class(form_control, "form-control")

            # wrap in a div[class='field']
            field_div = lxml.etree.Element("div")
            field_div.attrib["class"] = "field"
            for child in reversed(elm.getchildren()):
                child.addprevious(field_div)
                field_div.insert(0, child)

        # remove the "*" marking required fields. Wagtail uses css for that.
        for span in form.xpath('//span[text()="*"]'):
            span.getparent().remove(span)

        # Now wrap all the li's of the form in a ul
        ul = lxml.etree.Element("ul")
        ul.attrib["class"] = "fields"
        for li in reversed(form.xpath("li")):
            li.addprevious(ul)
            ul.insert(0, li)


def transform_pagination(tree):
    # Pagination
    for elem in tree.xpath("//ul" + has_class("pager")):
        replacement = lxml.etree.Element("div")
        replacement.attrib["class"] = "pagination col12"

        # current page text
        current = elem.xpath("//li" + has_class("current"))[0].text.strip()
        p_el = lxml.etree.Element("p", text=current)
        p_el.text = current
        replacement.append(p_el)

        # prev/next
        prev_el = elem.xpath("//li" + has_class("previous"))
        next_el = elem.xpath("//li" + has_class("next"))
        if prev_el or next_el:
            wrapper = lxml.etree.Element("ul")
            if prev_el:
                prev_el = prev_el[0]
                prev_el.attrib["class"] = "prev"
                prev_a = prev_el.xpath("//a")[0]
                prev_a.attrib["class"] = "icon icon-arrow-left"
                wrapper.append(prev_el)
            if next_el:
                next_el = next_el[0]
                next_a = next_el.xpath("//a")[0]
                next_a.attrib["class"] = "icon icon-arrow-right"
                wrapper.append(next_el)
            replacement.append(wrapper)
        elem.getparent().replace(elem, replacement)
