import psutil
import pywinauto
from pywinauto_recorder import UIApplication
from pywinauto_recorder.player import UIPath, click, focus_on_application, exists, find, get_wrapper_path
from pywinauto.controls.uiawrapper import UIAWrapper
from pywinauto.application import WindowSpecification
from pywinauto import findbestmatch
import sys
import codecs
import os
import locale
import six


def connect_application(backend="uia", **kwargs):
	app = pywinauto.Application(backend=backend)
	app.connect(**kwargs)
	top_window = app.top_window().wrapper_object()
	native_window_handle = top_window.handle
	return UIApplication(app, native_window_handle)

def get_ui_path_with_window_specification(obj):
	return UIPath(get_wrapper_path(obj))

def get_process_pid_by_name(process_name: str, min_memory_mb: float = 0) -> tuple[bool, int]:
    """
    通过进程名称和最小内存要求获取进程PID
    
    Args:
        process_name: 进程名称
        min_memory_mb: 最小内存要求(MB)，默认为0表示不检查内存
        
    Returns:
        tuple[bool, int]: (是否找到进程, 进程PID)
    """
    process_found = False
    process_pid = None
    min_memory_bytes = min_memory_mb * 1024 * 1024  # 转换为字节
    
    try:
        for proc in psutil.process_iter(['name', 'pid', 'memory_info']):
            try:
                # 获取进程信息
                proc_info = proc.info
                if proc_info['name'] == process_name:
                    # 如果设置了内存限制，则检查内存
                    if min_memory_mb > 0:
                        memory_info = proc_info.get('memory_info')
                        if memory_info and memory_info.rss > min_memory_bytes:
                            process_found = True
                            process_pid = proc_info['pid']
                            break
                    else:
                        # 不检查内存，直接返回找到的进程
                        process_found = True
                        process_pid = proc_info['pid']
                        break
                        
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                continue
                
    except Exception as e:
        print(f"获取进程信息时发生错误: {str(e)}")
        
    return process_found, process_pid

def print_wrapper_identifiers(wrapper_object, depth=None, filename=None):
    """
    打印控件及其子控件的标识信息
    
    Args:
        wrapper_object: UIAWrapper对象
        depth: 打印的最大深度,None表示打印全部
        filename: 输出文件名,None表示打印到控制台
    """
    if depth is None:
        depth = sys.maxsize
        
    # 创建所有控件的列表(当前控件及其所有子代)
    all_ctrls = [wrapper_object, ] + wrapper_object.descendants()
    
    # 创建所有可见文本控件的列表
    txt_ctrls = [ctrl for ctrl in all_ctrls if ctrl.can_be_label and ctrl.is_visible() and ctrl.window_text()]
    
    # 构建唯一的控件名称字典
    name_ctrl_id_map = findbestmatch.UniqueDict()
    for index, ctrl in enumerate(all_ctrls):
        ctrl_names = findbestmatch.get_control_names(ctrl, all_ctrls, txt_ctrls)
        for name in ctrl_names:
            name_ctrl_id_map[name] = index
            
    # 反转映射关系(控件索引到名称列表)
    ctrl_id_name_map = {}
    for name, index in name_ctrl_id_map.items():
        ctrl_id_name_map.setdefault(index, []).append(name)
        
    def print_identifiers(ctrls, current_depth=1, log_func=print):
        """递归打印控件及其子代的标识信息"""
        if len(ctrls) == 0 or current_depth > depth:
            return
            
        indent = (current_depth - 1) * u"   | "
        for ctrl in ctrls:
            try:
                ctrl_id = all_ctrls.index(ctrl)
            except ValueError:
                continue
                
            ctrl_text = ctrl.window_text()
            if ctrl_text:
                # 将多行文本转换为单行
                ctrl_text = ctrl_text.replace('\n', r'\n').replace('\r', r'\r')
                
            output = indent + u'\n'
            output += indent + u"{class_name} - '{text}'    {rect}\n"\
                "".format(class_name=ctrl.friendly_class_name(),
                          text=ctrl_text,
                          rect=ctrl.rectangle())
            output += indent + u'{}'.format(ctrl_id_name_map[ctrl_id])
            
            title = ctrl_text
            class_name = ctrl.class_name()
            auto_id = None
            control_type = None
            if hasattr(ctrl.element_info, 'automation_id'):
                auto_id = ctrl.element_info.automation_id
            if hasattr(ctrl.element_info, 'control_type'):
                control_type = ctrl.element_info.control_type
                if control_type:
                    class_name = None  # 如果有control_type就不需要class_name
                else:
                    control_type = None # 如果control_type为空,仍使用class_name
                    
            criteria_texts = []
            recorder_texts = []
            if title:
                criteria_texts.append(u'title="{}"'.format(title))
                recorder_texts.append(f"{title}")
            if class_name:
                criteria_texts.append(u'class_name="{}"'.format(class_name))
            if auto_id:
                criteria_texts.append(u'auto_id="{}"'.format(auto_id))
            if control_type:
                criteria_texts.append(u'control_type="{}"'.format(control_type))
                recorder_texts.append(f"||{control_type}")
            if title or class_name or auto_id:
                output += u'\n' + indent + u'child_window(' + u', '.join(criteria_texts) + u')' + " / " + "".join(recorder_texts)
                
            if six.PY3:
                log_func(output)
            else:
                log_func(output.encode(locale.getpreferredencoding(), errors='backslashreplace'))
                
            print_identifiers(ctrl.children(), current_depth + 1, log_func)
            
    if filename is None:
        print("Control Identifiers:")
        print_identifiers([wrapper_object, ])
    else:
        log_file = codecs.open(filename, "w", locale.getpreferredencoding())
        def log_func(msg):
            log_file.write(str(msg) + os.linesep)
        log_func("Control Identifiers:")
        print_identifiers([wrapper_object, ], log_func=log_func)
        log_file.close()

