import copy
from io import BytesIO
from itertools import *

import benchbase
from benchbase import (with_attributes, with_text, onlylib,
                       serialized, children, nochange)

TEXT  = "some ASCII text"
UTEXT = u"some klingon: \uF8D2"

############################################################
# Benchmarks
############################################################

class BenchMark(benchbase.TreeBenchMark):
    @nochange
    def bench_iter_children(self, root):
        for child in root:
            pass

    @nochange
    def bench_iter_children_reversed(self, root):
        for child in reversed(root):
            pass

    @nochange
    def bench_first_child(self, root):
        for i in self.repeat1000:
            child = root[0]

    @nochange
    def bench_last_child(self, root):
        for i in self.repeat1000:
            child = root[-1]

    @nochange
    def bench_middle_child(self, root):
        pos = len(root) // 2
        for i in self.repeat1000:
            child = root[pos]

    @nochange
    @with_attributes(False)
    @with_text(text=True)
    def bench_tostring_text_ascii(self, root):
        self.etree.tostring(root, method="text")

    @nochange
    @with_attributes(False)
    @with_text(text=True, utext=True)
    def bench_tostring_text_unicode(self, root):
        self.etree.tostring(root, method="text", encoding='unicode')

    @nochange
    @with_attributes(False)
    @with_text(text=True, utext=True)
    def bench_tostring_text_utf16(self, root):
        self.etree.tostring(root, method="text", encoding='UTF-16')

    @nochange
    @with_attributes(False)
    @with_text(text=True, utext=True)
    @onlylib('lxe')
    @children
    def bench_tostring_text_utf8_with_tail(self, children):
        for child in children:
            self.etree.tostring(child, method="text",
                                encoding='UTF-8', with_tail=True)

    @nochange
    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    def bench_tostring_utf8(self, root):
        self.etree.tostring(root, encoding='UTF-8')

    @nochange
    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    def bench_tostring_utf16(self, root):
        self.etree.tostring(root, encoding='UTF-16')

    @nochange
    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    def bench_tostring_utf8_unicode_XML(self, root):
        xml = self.etree.tostring(root, encoding='UTF-8').decode('UTF-8')
        self.etree.XML(xml)

    @nochange
    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    def bench_write_utf8_parse_bytesIO(self, root):
        f = BytesIO()
        self.etree.ElementTree(root).write(f, encoding='UTF-8')
        f.seek(0)
        self.etree.parse(f)

    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    @serialized
    def bench_parse_bytesIO(self, root_xml):
        f = BytesIO(root_xml)
        self.etree.parse(f)

    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    @serialized
    def bench_XML(self, root_xml):
        self.etree.XML(root_xml)

    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    @serialized
    def bench_iterparse_bytesIO(self, root_xml):
        f = BytesIO(root_xml)
        for event, element in self.etree.iterparse(f):
            pass

    @with_attributes(True, False)
    @with_text(text=True, utext=True)
    @serialized
    def bench_iterparse_bytesIO_clear(self, root_xml):
        f = BytesIO(root_xml)
        for event, element in self.etree.iterparse(f):
            element.clear()

    def bench_append_from_document(self, root1, root2):
        # == "1,2 2,3 1,3 3,1 3,2 2,1" # trees 1 and 2, or 2 and 3, or ...
        for el in root2:
            root1.append(el)

    def bench_insert_from_document(self, root1, root2):
        pos = len(root1)//2
        for el in root2:
            root1.insert(pos, el)
            pos = pos + 1

    def bench_rotate_children(self, root):
        # == "1 2 3" # runs on any single tree independently
        for i in range(100):
            el = root[0]
            del root[0]
            root.append(el)

    def bench_reorder(self, root):
        for i in range(1,len(root)//2):
            el = root[0]
            del root[0]
            root[-i:-i] = [ el ]

    def bench_reorder_slice(self, root):
        for i in range(1,len(root)//2):
            els = root[0:1]
            del root[0]
            root[-i:-i] = els

    def bench_clear(self, root):
        root.clear()

    @nochange
    @children
    def bench_has_children(self, children):
        for child in children:
            if child and child and child and child and child:
                pass

    @nochange
    @children
    def bench_len(self, children):
        for child in children:
            map(len, repeat(child, 20))

    @children
    def bench_create_subelements(self, children):
        SubElement = self.etree.SubElement
        for child in children:
            SubElement(child, '{test}test')

    def bench_append_elements(self, root):
        Element = self.etree.Element
        for child in root:
            el = Element('{test}test')
            child.append(el)

    @nochange
    @children
    def bench_makeelement(self, children):
        empty_attrib = {}
        for child in children:
            child.makeelement('{test}test', empty_attrib)

    @nochange
    @children
    def bench_create_elements(self, children):
        Element = self.etree.Element
        for child in children:
            Element('{test}test')

    @children
    def bench_replace_children_element(self, children):
        Element = self.etree.Element
        for child in children:
            el = Element('{test}test')
            child[:] = [el]

    @children
    def bench_replace_children(self, children):
        els = [ self.etree.Element("newchild") ]
        for child in children:
            child[:] = els

    def bench_remove_children(self, root):
        for child in root:
            root.remove(child)

    def bench_remove_children_reversed(self, root):
        for child in reversed(root):
            root.remove(child)

    @children
    def bench_set_attributes(self, children):
        for child in children:
            child.set('a', 'bla')

    @with_attributes(True)
    @children
    @nochange
    def bench_get_attributes(self, children):
        for child in children:
            child.get('bla1')
            child.get('{attr}test1')

    @children
    def bench_setget_attributes(self, children):
        for child in children:
            child.set('a', 'bla')
        for child in children:
            child.get('a')

    @nochange
    def bench_root_getchildren(self, root):
        root.getchildren()

    @nochange
    def bench_root_list_children(self, root):
        list(root)

    @nochange
    @children
    def bench_getchildren(self, children):
        for child in children:
            child.getchildren()

    @nochange
    @children
    def bench_get_children_slice(self, children):
        for child in children:
            child[:]

    @nochange
    @children
    def bench_get_children_slice_2x(self, children):
        for child in children:
            child[:]
            child[:]

    @nochange
    @children
    @with_attributes(True, False)
    @with_text(utext=True, text=True, no_text=True)
    def bench_deepcopy(self, children):
        for child in children:
            copy.deepcopy(child)

    @nochange
    @with_attributes(True, False)
    @with_text(utext=True, text=True, no_text=True)
    def bench_deepcopy_all(self, root):
        copy.deepcopy(root)

    @nochange
    @children
    def bench_tag(self, children):
        for child in children:
            child.tag

    @nochange
    @children
    def bench_tag_repeat(self, children):
        for child in children:
            for i in self.repeat100:
                child.tag

    @nochange
    @with_text(utext=True, text=True, no_text=True)
    @children
    def bench_text(self, children):
        for child in children:
            child.text

    @nochange
    @with_text(utext=True, text=True, no_text=True)
    @children
    def bench_text_repeat(self, children):
        for child in children:
            for i in self.repeat500:
                child.text

    @children
    def bench_set_text(self, children):
        text = TEXT
        for child in children:
            child.text = text

    @children
    def bench_set_utext(self, children):
        text = UTEXT
        for child in children:
            child.text = text

    @nochange
    @onlylib('lxe')
    def bench_index(self, root):
        for child in root:
            root.index(child)

    @nochange
    @onlylib('lxe')
    def bench_index_slice(self, root):
        for child in root[5:100]:
            root.index(child, 5, 100)

    @nochange
    @onlylib('lxe')
    def bench_index_slice_neg(self, root):
        for child in root[-100:-5]:
            root.index(child, start=-100, stop=-5)

    @nochange
    def bench_iter_all(self, root):
        list(root.iter())

    @nochange
    def bench_iter_one_at_a_time(self, root):
        list(islice(root.iter(), 2**30, None))

    @nochange
    def bench_iter_islice(self, root):
        list(islice(root.iter(), 10, 110))

    @nochange
    def bench_iter_tag(self, root):
        list(islice(root.iter(self.SEARCH_TAG), 3, 10))

    @nochange
    def bench_iter_tag_all(self, root):
        list(root.iter(self.SEARCH_TAG))

    @nochange
    def bench_iter_tag_one_at_a_time(self, root):
        list(islice(root.iter(self.SEARCH_TAG), 2**30, None))

    @nochange
    def bench_iter_tag_none(self, root):
        list(root.iter("{ThisShould}NeverExist"))

    @nochange
    def bench_iter_tag_text(self, root):
        [ e.text for e in root.iter(self.SEARCH_TAG) ]

    @nochange
    def bench_findall(self, root):
        root.findall(".//*")

    @nochange
    def bench_findall_child(self, root):
        root.findall(".//*/" + self.SEARCH_TAG)

    @nochange
    def bench_findall_tag(self, root):
        root.findall(".//" + self.SEARCH_TAG)

    @nochange
    def bench_findall_path(self, root):
        root.findall(".//*[%s]/./%s/./*" % (self.SEARCH_TAG, self.SEARCH_TAG))

    @nochange
    @onlylib('lxe')
    def bench_xpath_path(self, root):
        ns, tag = self.SEARCH_TAG[1:].split('}')
        root.xpath(".//*[p:%s]/./p:%s/./*" % (tag,tag),
                   namespaces = {'p':ns})

    @nochange
    def bench_iterfind(self, root):
        list(root.iterfind(".//*"))

    @nochange
    def bench_iterfind_tag(self, root):
        list(root.iterfind(".//" + self.SEARCH_TAG))

    @nochange
    def bench_iterfind_islice(self, root):
        list(islice(root.iterfind(".//*"), 10, 110))

    _bench_xpath_single_xpath = None

    @nochange
    @onlylib('lxe')
    def bench_xpath_single(self, root):
        xpath = self._bench_xpath_single_xpath
        if xpath is None:
            ns, tag = self.SEARCH_TAG[1:].split('}')
            xpath = self._bench_xpath_single_xpath = self.etree.XPath(
                './/p:%s[1]' % tag, namespaces={'p': ns})
        xpath(root)

    @nochange
    def bench_find_single(self, root):
        root.find(".//%s" % self.SEARCH_TAG)

    @nochange
    def bench_iter_single(self, root):
        next(root.iter(self.SEARCH_TAG))

    _bench_xpath_two_xpath = None

    @nochange
    @onlylib('lxe')
    def bench_xpath_two(self, root):
        xpath = self._bench_xpath_two_xpath
        if xpath is None:
            ns, tag = self.SEARCH_TAG[1:].split('}')
            xpath = self._bench_xpath_two_xpath = self.etree.XPath(
                './/p:%s[position() < 3]' % tag, namespaces={'p': ns})
        xpath(root)

    @nochange
    def bench_iterfind_two(self, root):
        it = root.iterfind(".//%s" % self.SEARCH_TAG)
        next(it)
        next(it)

    @nochange
    def bench_iter_two(self, root):
        it = root.iter(self.SEARCH_TAG)
        next(it)
        next(it)


if __name__ == '__main__':
    benchbase.main(BenchMark)
