# coding=utf-8
import re
import sys
import subprocess
import logging
import platform
from func_timeout import func_timeout, FunctionTimedOut


class Util(object):
    def __init__(self, sn):
        self.is_win = 'window' in platform.system().lower()
        self.is_wsl = 'linux' in platform.system().lower() and 'microsoft' in platform.release().lower()  # 判断当前是不是WSL环境
        self.is_py2 = sys.version_info < (3, 0)
        self.sn = sn
        self.adb_path = None
        self.connected = False
        self.debug = False

    def get_sn_list(self):
        out = self.cmd('adb devices').strip()
        out = re.split(r'[\r\n]+', out)
        sn_list = []
        for line in out[1:]:
            if not line.strip():
                continue
            sn, _ = re.split(r'\s+', line, maxsplit=1)
            if 'offline' in line:
                logging.warning('离线设备:{}'.format(line))
                if ':' in sn:
                    self.cmd('adb disconnect {}'.format(sn))  # 断掉离线的网络设备
                continue
            sn_list.append(sn)
        return sn_list

    @staticmethod
    def __get_cmd_process(arg):
        logging.debug(arg)
        p = subprocess.Popen(arg, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  # 将错误信息也使用stdout输出
        return p

    @staticmethod
    def __get_cmd_out(process):
        out, err = process.communicate()
        if err.strip():
            logging.error('命令 {} 有错误输出:\n{}'.format(process.args, err))

        return out

    @staticmethod
    def __run_cmd(arg, is_wait=True, encoding='utf-8', check_return_code=False):
        p = Util.__get_cmd_process(arg)
        if is_wait:
            out, err = p.communicate()
        else:
            return p  # 如果不等待，直接返回

        if encoding:
            out = out.decode(encoding)

        if check_return_code:
            p.communicate()  # 验证 return code，必须要结束
            if p.returncode != 0:  # 如果 returncode 非 0，引发异常
                raise NameError('{} 命令 return code {} 非 0\nout:\n{}'.format(arg, p.returncode, out))

        return out

    @staticmethod
    def cmd(arg, timeout=30, is_wait=True, encoding='utf-8', check_return_code=False):
        """
        执行命令，并返回命令的输出,有超时可以设置
        :param check_return_code:
        :param encoding:
        :param is_wait:
        :param arg:
        :param timeout:
        :return:
        """
        try:
            return func_timeout(timeout, Util.__run_cmd, args=(arg, is_wait, encoding, check_return_code))
        except FunctionTimedOut:
            print('执行命令超时 {}s: {}'.format(timeout, arg))

    def adb(self, arg, timeout=30, encoding='utf-8'):
        self.adb_path = self.adb_path if self.adb_path and self.adb_path != 'adb' else 'adb'

        if not self.connected:
            connected_sns = self.get_sn_list()
            # 没有传入设备 sn，并且也没有检测到设备
            if not self.sn and not connected_sns:
                raise NameError('没有可以使用的设备')

            # 检查传入的 sn 是否已经连接
            if self.sn and self.sn in connected_sns:
                self.connected = True

            # 没有传入设备 sn，并且检查有设备连接，使用第一个设备
            if not self.connected and self.sn is None and connected_sns:
                self.sn = connected_sns[0]
                self.connected = True

            # 传入的是网络 sn，并且前面没有找到
            if not self.connected and ':' in self.sn:
                self.cmd('adb disconnect {}'.format(self.sn))  # 连接前先断掉，否则可能会无法连接
                out = self.cmd('adb connect {}'.format(self.sn))
                self.connected = 'connected' in out  # 设置连接状态

        # 如果没有连接的设备，报错
        if not self.connected:
            raise NameError('没有可以使用的设备: {}'.format(self.sn))

        if self.sn:
            arg = '{} -s {} {}'.format(self.adb_path, self.sn, arg)
        else:
            arg = '{} {}'.format(self.adb_path, arg)
        return self.cmd(arg, timeout, encoding=encoding)

    def shell(self, arg, timeout=30, encoding='utf-8'):
        arg = 'shell {}'.format(arg)
        return self.adb(arg, timeout, encoding=encoding)
