from xml.dom import minidom
import copy
from uuid import uuid4

from .utils import *
from lottie.parsers.sif.sif.core import ObjectRegistry


class XmlDescriptor:
    def __init__(self, name):
        self.name = name
        self.att_name = name.replace("-", "_").replace("[", "_").replace("]", "").replace(".", "_")

    def to_xml(self, obj, parent: minidom.Element, dom: minidom.Document):
        raise NotImplementedError

    def from_xml(self, obj, parent: minidom.Element, registry: ObjectRegistry):
        raise NotImplementedError

    def from_python(self, value):
        raise NotImplementedError

    def initialize_object(self, dict, obj):
        if self.att_name in dict:
            setattr(obj, self.att_name, self.from_python(dict[self.att_name]))
        else:
            setattr(obj, self.att_name, self.default())

    def clean(self, value):
        return self.from_python(value)

    def default(self):
        return None

    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, self.name)


class TypedXmlDescriptor(XmlDescriptor):
    def __init__(self, name, type=str, default_value=None, att_name=None):
        super().__init__(name)
        self.type = type
        self.default_value = default_value
        if att_name is not None:
            self.att_name = att_name

    def from_python(self, value):
        if value is None and self.default_value is None:
            return None
        if not value_isinstance(value, self.type):
            return self.type(value)
        return value

    def default(self):
        return copy.deepcopy(self.default_value)


class ValueReference:
    def __init__(self, id, value=None):
        self.value = value
        self.id = id

    @classmethod
    def from_registry(cls, id, registry: ObjectRegistry):
        return cls(id, registry.get_object(id))


class XmlAttribute(TypedXmlDescriptor):
    def from_xml(self, obj, parent: minidom.Element, registry: ObjectRegistry):
        xml_str = parent.getAttribute(self.name)
        if xml_str:
            if xml_str.startswith(":") and xml_str[1:] in registry.registry:
                value = ValueReference.from_registry(xml_str[1:], registry)
            else:
                value = value_from_xml_string(xml_str, self.type, registry)
            setattr(obj, self.att_name, value)

    def to_xml(self, obj, parent: minidom.Element, dom: minidom.Document):
        value = getattr(obj, self.att_name)
        if value is not None:
            if isinstance(value, ValueReference):
                xml_str = ":" + value.id
            else:
                xml_str = value_to_xml_string(value, self.type)
            parent.setAttribute(self.name, xml_str)


class XmlFixedAttribute(XmlDescriptor):
    def __init__(self, name, value, type=str):
        super().__init__(name)
        self.value = value
        self.type = type

    def to_xml(self, obj, parent: minidom.Element, dom: minidom.Document):
        parent.setAttribute(self.name, value_to_xml_string(self.value, self.type))

    def from_python(self, value):
        if value != self.value:
            raise ValueError("Value of %s should be %s, got %s" % (self.name, self.value, value))
        return value

    def from_xml(self, obj, parent: minidom.Element, registry: ObjectRegistry):
        xml_str = parent.getAttribute(self.name)
        setattr(obj, self.att_name, value_from_xml_string(xml_str, self.type, registry))

    def default(self):
        return self.value


class XmlSimpleElement(TypedXmlDescriptor):
    def from_xml(self, obj, parent: minidom.Element, registry: ObjectRegistry):
        cn = xml_first_element_child(parent, self.name, allow_none=True)
        if cn:
            value = value_from_xml_string(xml_text(cn), self.type, registry)
        else:
            value = self.default_value

        setattr(obj, self.att_name, value)

    def to_xml(self, obj, parent: minidom.Element, dom: minidom.Document):
        value = getattr(obj, self.att_name)
        if value is not None:
            parent.appendChild(xml_make_text(dom, self.name, value_to_xml_string(value, self.type)))


class XmlMeta(TypedXmlDescriptor):
    def from_xml(self, obj, parent: minidom.Element, registry: ObjectRegistry):
        for cn in xml_child_elements(parent, "meta"):
            if cn.getAttribute("name") == self.name:
                value = value_from_xml_string(cn.getAttribute("content"), self.type, registry)
                break
        else:
            value = self.default_value

        setattr(obj, self.att_name, value)

    def to_xml(self, obj, parent: minidom.Element, dom: minidom.Document):
        value = getattr(obj, self.att_name)
        if value is not None:
            meta = parent.appendChild(dom.createElement("meta"))
            meta.setAttribute("name", self.name)
            meta.setAttribute("content", value_to_xml_string(value, self.type))
