from __future__ import unicode_literals


class HtmlGenerator(object):
    def __init__(self, create_writer):
        self._stack = []
        self._create_writer = create_writer
        self._writer = create_writer()
    
    def text(self, text):
        if text:
            self.write_all()
            self._writer.text(text)
    
    def start(self, name, attributes=None, always_write=False):
        self._stack.append(_Element(name, attributes))
        
        if always_write:
            self.write_all()

    def end(self):
        element = self._stack.pop()
        if element.written:
            self._writer.end(element.name)
    
    def end_all(self):
        while self._stack:
            self.end()
    
    def self_closing(self, name, attributes=None):
        self._writer.self_closing(name, attributes)
    
    def write_all(self):
        for element in self._stack:
            if not element.written:
                element.written = True
                self._write_element(element)
    
    def _write_element(self, element):
        self._writer.start(element.name, element.attributes)
    
    def append(self, other):
        other_string = other.as_string()
        if other_string:
            self.write_all()
            self._writer.append(other_string)
    
    def as_string(self):
        return self._writer.as_string()
    
    def child(self):
        return HtmlGenerator(self._create_writer)


class _Element(object):
    def __init__(self, name, attributes):
        if attributes is None:
            attributes = {}
        
        self.name = name
        self.attributes = attributes
        self.written = False


def satisfy_html_path(generator, path):
    first_unsatisfied_index = _find_first_unsatisfied_index(generator, path)
    while len(generator._stack) > first_unsatisfied_index:
        generator.end()
    
    for element in path.elements[first_unsatisfied_index:]:
        _append_element(generator, element)


def append_html_path(generator, path):
    for element in path.elements:
        _append_element(generator, element)


def _append_element(generator, element):
    attributes = {}
    if element.class_names:
        attributes["class"] = _generate_class_attribute(element)
    generator.start(element.names[0], attributes=attributes)
    


def _find_first_unsatisfied_index(generator, path):
    for index, (generated_element, path_element) in enumerate(zip(generator._stack, path.elements)):
        if not _is_element_match(generated_element, path_element):
            return index
    
    return len(generator._stack)


def _is_element_match(generated_element, path_element):
    return (
        not path_element.fresh and
        generated_element.name in path_element.names and
        generated_element.attributes.get("class", "") == _generate_class_attribute(path_element)
    )


def _generate_class_attribute(path_element):
    return " ".join(path_element.class_names)
