from __future__ import absolute_import, unicode_literals

from draftjs_exporter.engines.base import DOMEngine

try:
    # Python 3.2 and above.
    from html import escape
except ImportError:
    import cgi

    def escape(s):
        # Force quote escaping in Python 2.7.
        return cgi.escape(s, quote=True).replace('\'', '&#x27;')

# http://w3c.github.io/html/single-page.html#void-elements
# https://github.com/html5lib/html5lib-python/blob/0cae52b2073e3f2220db93a7650901f2200f2a13/html5lib/constants.py#L560
VOID_ELEMENTS = {
    'area',
    'base',
    'br',
    'col',
    'embed',
    'hr',
    'img',
    'input',
    'link',
    'meta',
    'param',
    'source',
    'track',
    'wbr',
}


class DOMString(DOMEngine):
    """
    String concatenation implementation of the DOM API.
    """

    @staticmethod
    def create_tag(type_, attr=None):
        return {
            'type': type_,
            'attr': attr,
            'children': [],
        }

    @staticmethod
    def parse_html(markup):
        """
        Allows inserting arbitrary HTML into the exporter output.
        Treats the HTML as if it had been escaped and was safe already.
        """
        return {
            'type': 'escaped_html',
            'attr': None,
            'children': None,
            'markup': markup,
        }

    @staticmethod
    def append_child(elt, child):
        # This check is necessary because the current wrapper_state implementation
        # has an issue where it inserts elements multiple times.
        # This must be skipped for text, which can be duplicated.
        is_existing_ref = isinstance(child, dict) and child in elt['children']
        if not is_existing_ref:
            elt['children'].append(child)

    @staticmethod
    def render_attrs(attr):
        return ''.join(sorted([' %s="%s"' % (k, escape(v)) for k, v in attr.items()]))

    @staticmethod
    def render_children(children):
        return ''.join([DOMString.render(c) if isinstance(c, dict) else escape(c) for c in children])

    @staticmethod
    def render(elt):
        type_ = elt['type']
        attr = DOMString.render_attrs(elt['attr']) if elt['attr'] else ''
        children = DOMString.render_children(elt['children']) if elt['children'] else ''

        if type_ == 'fragment':
            return children

        if type_ in VOID_ELEMENTS:
            return '<%s%s/>' % (type_, attr)

        if type_ == 'escaped_html':
            return elt['markup']

        return '<%s%s>%s</%s>' % (type_, attr, children, type_)

    @staticmethod
    def render_debug(elt):
        type_ = elt['type']
        attr = DOMString.render_attrs(elt['attr']) if elt['attr'] else ''
        children = DOMString.render_children(elt['children']) if elt['children'] else ''

        if type_ in VOID_ELEMENTS:
            return '<%s%s/>' % (type_, attr)

        if type_ == 'escaped_html':
            return elt['markup']

        return '<%s%s>%s</%s>' % (type_, attr, children, type_)

@@ -699,16 +699,205 @@
 r',%0A%7D%0A%0A%0A
+class Elt(object):%0A    def __init__(self, type_, attr, children, markup):%0A        self.type = type_%0A        self.attr = attr%0A        self.children = children%0A        self.markup = markup%0A%0A%0A
 class DO
@@ -1064,100 +1064,34 @@
 urn 
-%7B%0A            'type': type_,%0A            'attr': attr,%0A            'children': %5B%5D,%0A        %7D
+Elt(type_, attr, %5B%5D, None)
 %0A%0A  
@@ -1315,141 +1315,47 @@
 urn 
-%7B%0A            'type': 'escaped_html',%0A            'attr': None,%0A            'children': None,%0A            'markup': markup,%0A        %7D
+Elt('escaped_html', None, None, markup)
 %0A%0A  
@@ -1662,19 +1662,18 @@
 (child, 
-dic
+El
 t) and c
@@ -1683,26 +1683,25 @@
 d in elt
-%5B'
+.
 children
 '%5D%0A     
@@ -1692,18 +1692,16 @@
 children
-'%5D
 %0A       
@@ -1740,26 +1740,25 @@
      elt
-%5B'
+.
 children
 '%5D.appen
@@ -1749,18 +1749,16 @@
 children
-'%5D
 .append(
@@ -2017,11 +2017,10 @@
 (c, 
-dic
+El
 t) e
@@ -2108,32 +2108,29 @@
  type_ = elt
-%5B'
+.
 type
-'%5D
 %0A        att
@@ -2159,32 +2159,29 @@
 ttrs(elt
-%5B'
+.
 attr
-'%5D
 ) if elt
 %5B'attr'%5D
@@ -2168,32 +2168,29 @@
 attr) if elt
-%5B'
+.
 attr
-'%5D
  else ''%0A   
@@ -2230,34 +2230,33 @@
 children(elt
-%5B'
+.
 children
 '%5D) if elt%5B'
@@ -2239,34 +2239,32 @@
 elt.children
-'%5D
 ) if elt
 %5B'children'%5D
@@ -2247,34 +2247,33 @@
 dren) if elt
-%5B'
+.
 children
 '%5D else ''%0A%0A
@@ -2252,34 +2252,32 @@
  if elt.children
-'%5D
  else ''%0A%0A      
@@ -2466,34 +2466,31 @@
   return elt
-%5B'
+.
 markup
-'%5D
 %0A%0A        re
@@ -2612,16 +2612,13 @@
  elt
-%5B'
+.
 type
-'%5D
 %0A   
@@ -2659,24 +2659,21 @@
 (elt
-%5B'
+.
 attr
-'%5D
 ) if elt
 %5B'at
@@ -2672,16 +2672,13 @@
  elt
-%5B'
+.
 attr
-'%5D
  els
@@ -2730,26 +2730,25 @@
 dren(elt
-%5B'
+.
 children
 '%5D) if e
@@ -2743,18 +2743,16 @@
 dren
-'%5D
 ) if elt
 %5B'ch
@@ -2751,18 +2751,17 @@
  elt
-%5B'
+.
 children
 '%5D e
@@ -2756,18 +2756,16 @@
 children
-'%5D
  else ''
@@ -2909,18 +2909,15 @@
  elt
-%5B'
+.
 markup
-'%5D
 %0A%0A  

Fix string engine incorrectly skipping identical elements at the same level of the DOM