from markdown.extensions import Extension
from markdown.inlinepatterns import LinkInlineProcessor
import xml.etree.ElementTree as etree
import urllib
import re

class MediaExtension(Extension):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def extendMarkdown(self, md):
        self.md = md

        # Override image detection
        MEDIA_RE = r'\!\['
        media_processor = MediaInlineProcessor(MEDIA_RE)
        media_processor.md = md
        md.inlinePatterns.register(media_processor, 'media_link', 155)

class AttrDict:
    def __init__(self, attrs):
        self.attrs = attrs

    def has(self, name):
        return name in self.attrs

    def getString(self, name):
        for attr in self.attrs:
            if attr.startswith(name + "="):
                return attr[len(name)+1:]

    def getInt(self, name):
        try:
            s = self.getString(name)
            return int(s) if s is not None else None
        except ValueError:
            return None

    def getSize(self, name):
        s = self.getString(name)
        if s is None:
            return None, None
        dims = s.split("x", 1)
        try:
            if len(dims) == 1:
                return int(dims[0]), None
            else:
                w = int(dims[0]) if dims[0] else None
                h = int(dims[1]) if dims[1] else None
                return w, h
        except ValueError:
            return None, None

    def getAlignmentClass(self):
        if self.has("left"):
            return "align-left"
        if self.has("center"):
            return "align-center"
        elif self.has("right"):
            return "align-right"
        elif self.has("float-left"):
            return "float-left"
        elif self.has("float-right"):
            return "float-right"
        return ""

class AttributeLinkInlineProcessor(LinkInlineProcessor):
    """
    A LinkInlineProcessor which additionally supports attributes after links,
    with the bracket syntax `{ <item>, <item>, ... }` where each item is a
    key/value pair with either single or double quotes: `key='value'` or
    `key="value"`.
    """

    def getAttributes(self, data, index):
        current_quote = ""
        has_closing_brace = False
        attrs = []
        current_attr_text = ""

        if index >= len(data) or data[index] != '{':
            return AttrDict([]), index, True
        index += 1

        for pos in range(index, len(data)):
            c = data[pos]
            index += 1

            # Close quote
            if current_quote != "" and c == current_quote:
                current_quote = ""
                continue
            # Open new quote
            if current_quote == "" and c in ["'", '"']:
                current_quote = c
                continue
            # Close brace
            if current_quote == "" and c == "}":
                has_closing_brace = True
                break

            if current_quote == "" and c == " ":
                if current_attr_text:
                    attrs.append(current_attr_text)
                current_attr_text = ""
            else:
                current_attr_text += c

        if current_attr_text:
            attrs.append(current_attr_text)

        return AttrDict(attrs), index, has_closing_brace

class MediaInlineProcessor(AttributeLinkInlineProcessor):
    """ Return a media element from the given match. """

    def isVideo(self, url):
        if url.endswith(".mp4") or url.endswith(".webm"):
            return True
        url = urllib.parse.urlparse(url)
        # TODO: Better detect YouTube URLs
        return url.hostname in ["youtu.be", "www.youtube.com"]

    def isAudio(self, url):
        return url.endswith(".mp3") or url.endswith(".ogg")

    def handleMatch(self, m, data):
        text, index, handled = self.getText(data, m.end(0))
        if not handled:
            return None, None, None

        src, title, index, handled = self.getLink(data, index)
        if not handled:
            return None, None, None

        attrs, index, handled = self.getAttributes(data, index)
        if not handled:
            return None, None, None

        kind = "image"
        if attrs.has("image"):
            kind = "image"
        elif attrs.has("audio") or self.isAudio(src):
            kind = "audio"
        elif attrs.has("video") or self.isVideo(src):
            kind = "video"

        if kind == "image":
            w, h = attrs.getSize("size")
            class_ = ""
            # TODO: Media converter: Find a way to clear atfer a float
            if attrs.has("pixelated"):
                class_ += " pixelated"
            class_ += " " + attrs.getAlignmentClass()

            el = etree.Element("img")
            el.set("src", src)

            if title is not None:
                el.set("title", title)

            if class_ != "":
                el.set("class", class_)
            if w is not None:
                el.set("width", str(w))
            if h is not None:
                el.set("height", str(h))

            el.set('alt', self.unescape(text))
            return el, m.start(0), index

        elif kind == "audio":
            # TODO: Media converter: support audio files
            pass

        elif kind == "video":
            w, h = attrs.getSize("size")
            class_ = attrs.getAlignmentClass()
            url = urllib.parse.urlparse(src)
            args = urllib.parse.parse_qs(url.query)
            youtube_source = None

            if url.hostname == "youtu.be" and \
                    re.fullmatch(r'\/[a-zA-Z0-9_-]+', url.path):
                youtube_source = url.path[1:]
            elif url.hostname == "www.youtube.com" and "v" in args and \
                    re.fullmatch(r'[a-zA-Z0-9_-]+', args["v"][0]):
                youtube_source = args["v"][0]

            if youtube_source:
                if w is None and h is None:
                    w, h = (470, 300) if attrs.has("tiny") else (560, 340)

                el = etree.Element("iframe")
                el.set("src",f"https://www.youtube.com/embed/{youtube_source}")
                el.set("frameborder", "0")
                el.set("allowfullscreen", "")
                # <iframe
                #    src="https://www.youtube.com/embed/{url.path}"
                #    frameborder="0" allowfullscreen></iframe>
                pass
            else:
                el = etree.Element("video")
                el.set("controls", "")
                source = etree.Element("source")
                source.set("src", src)
                el.append(source)
                # <video controls
                #  <source src="{url}">
                # </video>

            el.set("class", class_)
            if w is not None:
                el.set("width", str(min(w, 560)))
            if h is not None:
                el.set("height", str(min(h, 340)))

            return el, m.start(0), index

        return None, None, None
