# coding=utf-8
import sys
import re
import logging
from lxml import etree
from .ocr import Ocr
from lxml.etree import tostring

short_keys = {'id': 'resource-id', 'class_': 'class', 'klass': 'class', 'desc': 'content-desc'}


class GetUI(object):
    def __init__(self, adb_ext):
        self.adb_ext = adb_ext
        self.keys = None
        self.xml = None
        self.ocr = None
        self.init_ocr()
        self.image = None

    def init_ocr(self, app_id=None, secret_id=None, secret_key=None, keys=[]):
        self.keys = keys
        if app_id is None and secret_id is None and secret_key is None:
            # 以下为测试账号,任何人可用,但是随时都会不可用,建议自行去腾讯优图申请专属账号
            app_id = '10126986'
            secret_id = 'AKIDT1Ws34B98MgtvmqRIC4oQr7CBzhEPvCL'
            secret_key = 'AAyb3KQL5d1DE4jIMF2f6PYWJvLaeXEk'
        self.keys.append({'app_id': app_id, 'secret_id': secret_id, 'secret_key': secret_key})
        self.ocr = Ocr(app_id, secret_id, secret_key)

    def get_ui_by_attr(self, is_contains=True, is_update=True, **kwargs):
        uis = self.get_uis_by_attr(is_contains=is_contains, is_update=is_update, **kwargs)
        return uis[0] if uis else None

    def get_uis_by_attr(self, is_contains=True, is_update=True, **kwargs):
        """
        通过节点的属性获取节点
        :param is_contains: 是否使用模糊查找
        :param is_update:
        :param kwargs:
        :return: 
        """
        for key in kwargs:
            if key in short_keys:
                kwargs[short_keys[key]] = kwargs.pop(key)
        if is_contains:
            s = list(map(lambda x: "contains(@{}, '{}')".format(x, kwargs[x]), kwargs))
            xpath = './/*[{}]'.format(' and '.join(s))
        else:
            s = list(map(lambda key: "[@{}='{}']".format(key, kwargs[key]), kwargs))
            xpath = './/*{}'.format(''.join(s))
        uis = self.get_uis_by_xpath(xpath, is_update=is_update)
        return uis

    def get_ui_by_xpath(self, xpath, is_update=True):
        uis = self.get_uis_by_xpath(xpath, is_update)
        return uis[0] if uis else None

    def get_uis_by_xpath(self, xpath, is_update=True):
        """
        通过xpath查找节点
        :param xpath: 
        :param is_update: 
        :return: 
        """
        if is_update:
            xml_str = None
            for _ in range(5):
                try:
                    xml_str = self.adb_ext.dump()  # 获取xml文件
                    self.__init_xml(xml_str)
                    break
                except etree.XMLSyntaxError:
                    logging.error('etree.XMLSyntaxError:\n')
                    if xml_str:
                        logging.error('xml str:{}'.format(xml_str))
        xpath = xpath.decode('utf-8') if sys.version_info[0] < 3 else xpath
        elements = self.xml.xpath(xpath)
        uis = []
        for element in elements:
            uis.append(self.get_ui_by_element(element))
        return uis

    def get_ui_by_element(self, element):
        bounds = element.get('bounds')
        x1, y1, x2, y2 = re.compile(r"-?\d+").findall(bounds)
        ui = UI(self.adb_ext, x1, y1, x2, y2)
        ui.element = element
        text = element.get('text')
        if not text:
            text = element.get('content-desc')
        ui.text = text.encode('utf-8') if self.adb_ext.util.is_py2 and not isinstance(text, str) else text
        return ui

    def get_ui_by_ocr(self, text, is_contains=True, is_update=True):
        uis = self.get_uis_by_ocr(text, is_contains, is_update)
        return uis[0] if uis else None

    def get_uis_by_ocr(self, text, is_contains=True, is_update=True):
        """
        通过ocr识别获取节点
        :param is_contains:
        :param text: 查找的文本
        :param is_update: 是否重新获取截图
        :return: 
        """
        if self.ocr is None:
            raise NameError('ocr 功能没有初始化.请到 adbui 页面查看如何使用。\nhttps://github.com/hao1032/adbui')
        if is_update:
            self.image = self.adb_ext.screenshot(is_jpg=True)  # 获取截图

        ocr_result = self.__get_ocr_result()
        text = text.decode('utf-8') if self.adb_ext.util.is_py2 and isinstance(text, str) else text
        uis = []
        for item in ocr_result['items']:
            item_string = item['itemstring']
            item_string = item_string.decode('utf-8') if self.adb_ext.util.is_py2 and isinstance(item_string, str) else item_string

            if (is_contains and text in item_string) or (not is_contains and text == item_string):
                item_coord = item['itemcoord']
                ui = UI(self.adb_ext, item_coord['x'], item_coord['y'],
                        item_coord['x'] + item_coord['width'], item_coord['y'] + item_coord['height'])
                ui.text = item_string
                uis.append(ui)
        return uis

    def __get_ocr_result(self):
        for key in self.keys:
            ocr_result = self.ocr.get_result_image(self.image)
            if 'httpcode' in ocr_result and ocr_result['httpcode'] == 510:  # 如果频率限制,换一个
                self.ocr = Ocr(app_id=key['app_id'], secret_id=key['secret_id'], secret_key=key['secret_key'])
            else:
                return ocr_result

        if 'httpcode' in ocr_result and ocr_result['httpcode'] == 510:  # 如果依然频率限制,报错
            raise NameError('OCR 服务调用频率限制或者连接数限制,请使用自己申请的账号。')

    def __init_xml(self, xml_str):
        self.xml = etree.fromstring(xml_str.encode('utf-8'))
        for element in self.xml.findall('.//node'):
            element.tag = element.get('class').split('.')[-1].replace('$', '')  # 将每个node的name替换为class值,和uiautomator里显示的一致

        try:
            self.original_xml = etree.tostring(self.xml, pretty_print=True, encoding='utf-8').decode()  # 原始 xml
            self.replace_xml = etree.tostring(self.xml, pretty_print=True, encoding='utf-8').decode()  # 替换后的 xml
        except:
            self.replace_xml = etree.tostring(self.xml, pretty_print=True).decode()  # 替换后的 xml
            self.original_xml = etree.tostring(self.xml, pretty_print=True).decode()  # 原始 xml


class UI:
    def __init__(self, adb_ext, x1, y1, x2, y2):
        self.__adb_ext = adb_ext
        self.x1 = int(x1)  # 左上角 x
        self.y1 = int(y1)  # 左上角 y
        self.x2 = int(x2)  # 右下角 x
        self.y2 = int(y2)  # 右下角 y
        self.width = self.x2 - self.x1  # 元素宽
        self.height = self.y2 - self.y1  # 元素高
        self.x = self.x1 + int(self.width / 2)
        self.y = self.y1 + int(self.height / 2)
        self.text = None  # 元素文本
        self.element = None  # 元素对应的 lxml element,ocr无效

    def get_element_str(self):
        return tostring(self.element)

    def get_value(self, key):
        # 返回 lxml element 属性对应的值
        if key in short_keys:
            key = short_keys[key]
        return self.element.get(key)

    def click(self):
        # 点击元素的中心点
        self.__adb_ext.click(self.x, self.y)