#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import time
import logging
import signal
import hashlib
import subprocess
from pathlib import Path
from threading import Thread
from time import sleep

# 配置X11环境变量
os.environ['DISPLAY'] = ':0'
# os.environ['XAUTHORITY'] = '/home/pi/.Xauthority'  # 根据实际情况调整路径

# 全局变量
processes = {}  # 存储进程对象
monitor_thread = None  # 监控线程
running = True  # 控制循环运行
isSettings = False
SettingFlagPath="/tmp/ktouch/setflag"
apppath="/opt/ktouch"
display_monitor_process = None  # 存储display_monitor进程对象


# 配置日志
def setup_logging():
    log_file = os.path.join(apppath, 'touchscreen.log')
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s daemon.py [%(levelname)s] %(message)s',
        handlers=[
            logging.FileHandler(log_file, encoding='utf-8'),
            logging.StreamHandler(sys.stdout)
        ]
    )
    return logging.getLogger(__name__)

logger = setup_logging()

# 信号处理函数
def signal_handler(signum, frame):
    global running
    logger.info("Received signal %d, shutting down...", signum)
    running = False
    cleanup()

def cleanup():
    """清理函数，停止所有子进程"""
    logger.info("清理子进程")
    for proc_name, proc_info in processes.items():
        try:
            proc = proc_info['process']
            if proc and proc.poll() is None:  # 进程还在运行
                proc.terminate()
                try:
                    proc.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    proc.kill()
                logger.info("关闭了进程: %s", proc_name)
        except Exception as e:
            logger.error("关闭进程失败 %s: %s", proc_name, e)
    processes.clear()


# 检查display_monitor.py是否在运行
def check_display_monitor_running():
    """检查display_monitor.py是否已经在运行"""
    try:
        # 使用pgrep检查进程
        result = subprocess.run(['pgrep', '-f', 'display_monitor.py'], 
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return result.returncode == 0 and len(result.stdout.strip()) > 0
    except Exception as e:
        logger.error("检查display_monitor.py进程状态失败: %s", e)
        return False

# 启动display_monitor.py
def start_display_monitor():
    """启动display_monitor.py脚本"""
    global display_monitor_process
    
    # 检查脚本是否存在
    display_monitor_script = "/opt/ktouch/display_monitor.py"
    if not os.path.exists(display_monitor_script):
        logger.warning("display_monitor.py脚本不存在: %s", display_monitor_script)
        return False
    
    # 检查是否已经在运行
    if check_display_monitor_running():
        logger.info("display_monitor.py已经在运行")
        return True
    
    try:
        # 设置X11环境
        env = os.environ.copy()
        env['DISPLAY'] = ':0'
        
        # 启动display_monitor.py，使用nohup使其在后台独立运行
        display_monitor_process = subprocess.Popen(
            ['nohup', 'python3', display_monitor_script, '&'],
            env=env,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            preexec_fn=os.setpgrp  # 创建新的进程组，使子进程独立
        )
        
        # 等待一下让进程启动
        time.sleep(2)
        
        # 检查是否成功启动
        if check_display_monitor_running():
            logger.info("成功启动display_monitor.py (PID: %d)", display_monitor_process.pid)
            return True
        else:
            logger.error("启动display_monitor.py失败")
            return False
            
    except Exception as e:
        logger.error("启动display_monitor.py时发生错误: %s", e)
        return False

# 1. 日志文件轮转函数
def rotate_logs():
    log_files = [os.path.join(apppath,'sub_modules.log'), os.path.join(apppath,'touchscreen.log')]
    max_size = 10 * 1024 * 1024  # 10MB
    max_backups = 7
    
    for log_file in log_files:
        if os.path.exists(log_file) and os.path.getsize(log_file) > max_size:
            #print(f'文件{log_file}的大小是{os.path.getsize(log_file)}')
            # 删除最旧的备份文件
            oldest_backup = "%s.%d" % (log_file, max_backups)
            if os.path.exists(oldest_backup):
                os.remove(oldest_backup)
                logger.info("清理过早的日志: %s", oldest_backup)
            
            # 重命名现有备份文件
            for i in range(max_backups-1, 0, -1):
                old_name = "%s.%d" % (log_file, i)
                new_name = "%s.%d" % (log_file, i+1)
                if os.path.exists(old_name):
                    os.rename(old_name, new_name)
                    # logger.info("Renamed %s to %s", old_name, new_name)
            
            # 重命名当前日志文件
            os.rename(log_file, "%s.1" % log_file)
            # logger.info("Rotated log file: %s -> %s.1", log_file, log_file)

# 2. 检查并创建所需文件
def initialize_files():
    ktouch_dir = Path("/tmp/ktouch")
    files_to_create = [
        "screen.txt",
        "touch.txt",
        "touch_need_update.txt",
        "usbadd.txt",
        "setflag"
    ]
    
    # 创建目录
    if not ktouch_dir.exists():
        ktouch_dir.mkdir()
    logger.info("创建状态缓存目录 /tmp/ktouch ")
    
    # 创建或初始化文件
    for file_name in files_to_create:
        file_path = ktouch_dir / file_name
        with open(str(file_path), 'w') as f:
            f.write('1')
        logger.info("初始化缓存文件 %s: to 1", file_path)


# 启动单个后台程序
def start_process(proc_name, proc_args):
    """启动单个进程并添加到监控列表"""
    # 设置X11环境
    env = os.environ.copy()
    env['DISPLAY'] = ':0'
    
    try:
        proc = subprocess.Popen(proc_args, env=env)
        processes[proc_name] = {
            'process': proc,
            'args': proc_args,
            'start_time': time.time(),
            'restart_count': 0
        }
        logger.info("拉起子进程 %s (PID: %d)", proc_name, proc.pid)
        return True
    except Exception as e:
        logger.error("进程启动失败 %s: %s", proc_name, e)
        return False


# 3. 启动后台程序（添加X11环境支持）
def start_background_processes():
    process_configs = {
        'screen_ds': ["/opt/ktouch/screen_ds", "/tmp/ktouch/screen.txt"],
        'touch_ds': ["/opt/ktouch/touch_ds", "/tmp/ktouch/touch.txt", "/tmp/ktouch/touch_need_update.txt"],
        'usb_ds': ["/opt/ktouch/usb_ds", "/tmp/ktouch/usbadd.txt"]
    }

    for proc_name, proc_args in process_configs.items():
        start_process(proc_name, proc_args)


# 停止所有子进程
def stop_all_processes():
    """停止所有监控的子进程"""
    logger.info("停止所有子进程")
    for proc_name, proc_info in list(processes.items()):
        try:
            proc = proc_info['process']
            if proc and proc.poll() is None:  # 进程还在运行
                proc.terminate()
                try:
                    proc.wait(timeout=3)
                    logger.info("成功停止进程: %s", proc_name)
                except subprocess.TimeoutExpired:
                    proc.kill()
                    logger.warning("强制停止进程: %s", proc_name)
            # 从监控列表中移除
            del processes[proc_name]
        except Exception as e:
            logger.error("停止进程失败 %s: %s", proc_name, e)


# 检查暂停标志文件
def check_pause_flag():
    """检查setflag文件，返回是否需要暂停"""
    
    if not os.path.exists(SettingFlagPath):
        return False
    
    try:
        with open(SettingFlagPath, 'r') as f:
            content = f.read().strip()
            return content == '1'
    except Exception as e:
        # logger.error("读取setflag文件失败: %s", e)
        return False


# 进入设置轮空的状态
def handle_pause_state():
    """处理暂停和恢复逻辑"""
    global isSettings
    
    if should_pause and not paused:
        # 进入暂停状态
        logger.info("检测到setflag=1，进入暂停状态")
        paused = True
        stop_all_processes()
        logger.info("已暂停所有监控和子进程")
        
    elif not should_pause and paused:
        # 恢复运行状态
        logger.info("检测到setflag=0或文件不存在，恢复运行状态")
        paused = False
        # 重新启动后台进程
        start_background_processes()
        logger.info("已恢复所有监控和子进程")
    
    return paused



# 监控和重启进程
def monitor_processes():
    """监控进程状态，如果进程退出则重启"""
    global running, isSettings

    buff_isSettings = isSettings
    
    while running:
        try:
            if not isSettings == buff_isSettings:
                # 值变化，开始操作
                if isSettings:
                    stop_all_processes()
                else:
                    start_background_processes()

            buff_isSettings = isSettings

            if isSettings:
                time.sleep(5)
                continue


            for proc_name, proc_info in list(processes.items()):
                proc = proc_info['process']
                returncode = proc.poll()
                
                if returncode is not None:  # 进程已退出
                    logger.warning("进程 %s (PID: %d) 已退出，代码 %d", proc_name, proc.pid, returncode)
                    
                    # 限制重启次数，避免无限重启
                    proc_info['restart_count'] += 1
                    if proc_info['restart_count'] > 128:  # 最多重启128次
                        logger.error("启动 %s 进程被结束超过128次，决定不再重启。请检查日志文件。", proc_name)
                        del processes[proc_name]
                        continue
                    
                    # 等待一段时间再重启
                    time.sleep(2)
                    
                    # 重新启动进程
                    if start_process(proc_name, proc_info['args']):
                        logger.info("重启进程: %s", proc_name)
                    else:
                        logger.error("重启进程失败: %s", proc_name)
            
            # 检查是否有进程需要启动（初始启动失败的情况）
            expected_processes = ['screen_ds', 'touch_ds', 'usb_ds']
            for proc_name in expected_processes:
                if proc_name not in processes:
                    logger.warning("发现 %s 消失, 准备重新启动", proc_name)
                    # 这里需要根据进程名重新构建参数
                    if proc_name == 'screen_ds':
                        start_process(proc_name, ["/opt/ktouch/screen_ds", "/tmp/ktouch/screen.txt"])
                    elif proc_name == 'touch_ds':
                        start_process(proc_name, ["/opt/ktouch/touch_ds", "/tmp/ktouch/touchmap.txt", "/tmp/ktouch/touch_need_update.txt"])
                    elif proc_name == 'usb_ds':
                        start_process(proc_name, ["/opt/ktouch/usb_ds", "/tmp/ktouch/usbadd.txt"])
            
            time.sleep(5)  # 每2秒检查一次进程状态
            
        except Exception as e:
            logger.error("进程监控出现问题: %s", e)
            time.sleep(5)


# 4. 执行touch_remap并重置文件（添加X11环境支持）
def run_touch_remap():
    # 设置X11环境
    env = os.environ.copy()
    env['DISPLAY'] = ':0'
    
    try:
        # 执行touch_remap程序
        result = subprocess.run(["/opt/ktouch/touch_set", "/tmp/ktouch/touch.txt"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, env=env)
        if result.returncode == 0:
            logger.info("执行映射(touch_set)成功")
        else:
            logger.error("执行映射(touch_set)失败，错误信息为 %d: %s", result.returncode, result.stderr)
    except Exception as e:
        logger.error("无法执行touch_set映射程序！！！！！: %s", e)
    
    # 重置文件为0
    ktouch_dir = Path("/tmp/ktouch")
    files_to_reset = [
        "touch_need_update.txt",
        "usbadd.txt",
        "setflag"
    ]
    
    for file_name in files_to_reset:
        file_path = ktouch_dir / file_name
        try:
            with open(str(file_path), 'w') as f:
                f.write('0')
            # logger.info("Reset %s to 0", file_path)
        except Exception as e:
            # logger.error("Failed to reset %s: %s", file_path, e)
            pass

# 5. 监控文件变化
def monitor_files():
    global isSettings
    # 记录screen.txt的初始状态
    screen_file = "/tmp/ktouch/screen.txt"
    last_screen_content = ""
    
    # 启动时读取以一下屏幕文件
    if os.path.exists(screen_file):
        with open(screen_file, 'r') as f:
            last_screen_content = f.read().strip()
    
    logger.info("开始主程序监控")
    buff_mainloop_isSettings = check_pause_flag()
    
    while running:
        # 检查是否在设置过程中，是的话则暂停循环，并关闭三个子进程
        isSettings = check_pause_flag()
        if not buff_mainloop_isSettings == isSettings:
            logger.info("设置标志变化：%d", isSettings)
            # 变化，如果设置结束则触发一起映射
            if not isSettings:
                run_touch_remap()

        buff_mainloop_isSettings = isSettings

        if isSettings:
            sleep(2)
            continue
        

        # 检查是否需要执行touch_remap
        need_update = False
        
        # 检查usbadd.txt和touch_need_update.txt
        for file_name in ["/tmp/ktouch/usbadd.txt", "/tmp/ktouch/touch_need_update.txt"]:
            try:
                with open(file_name, 'r') as f:
                    content = f.read().strip()
                    if content == '1':
                        logger.info("文件 %s 内容变为1，触发重映射", file_name)
                        need_update = True
            except Exception as e:
                logger.error("读取文件失败 %s: %s", file_name, e)
        
        # 检查screen.txt是否发生变化
        try:
            with open(screen_file, 'r') as f:
                current_content = f.read().strip()
                if current_content != last_screen_content:
                    logger.info("文件 screen.txt 内容变化，触发重映射")
                    need_update = True
                    last_screen_content = current_content
        except Exception as e:
            logger.error("读取文件失败 %s: %s", screen_file, e)
        
        # 如果需要更新，执行touch_remap
        if need_update:
            run_touch_remap()
        
        # 等待一段时间再次检查
        time.sleep(1)

# 检查X11环境是否可用
def check_x11_environment():
    try:
        # 尝试运行一个简单的X11命令来检查环境
        result = subprocess.run(['xset', '-q'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, timeout=5)
        if result.returncode == 0:
            logger.info("X11 environment is available")
            return True
        else:
            logger.warning("X11 environment check failed")
            return False
    except Exception as e:
        logger.error("Error checking X11 environment: %s", e)
        return False

# 主函数
def main():
    global monitor_thread, running
    has_config_file=True
    try:
        # 注册信号处理
        signal.signal(signal.SIGTERM, signal_handler)
        signal.signal(signal.SIGINT, signal_handler)
        logger.info("==============kscreen_remap_daemon 程序启动====================")
        
        # 检查X11环境
        x11_try=3
        while x11_try:
            if not check_x11_environment():
                logger.warning("主程序：X11 environment may not be available, 程序将继续运行但无效果")
                x11_try -= 1
                sleep(1)
            else:
                x11_try = 0
        
        # 1. 日志轮转
        rotate_logs()
        logger.info("主程序：处理日志 OK")
        
        # 2. 初始化文件
        initialize_files()
        logger.info("主程序：初始化和检查文件 OK")


        # 2.5 启动屏幕设置的后台监控进程
        # 检查并启动display_monitor.py(UOS中)
        if os.path.exists('/etc/os-version'):
            logger.info("检查并启动display_monitor.py...")
            if start_display_monitor():
                logger.info("display_monitor.py启动成功或已在运行")
            else:
                logger.warning("display_monitor.py启动失败，但主程序将继续运行")

        # ========如果没有配置文件，则不用运行监控===========
        if not os.path.exists("/opt/ktouch/config"):
            logger.info("配置文件不存在, 进入等待模式")
            has_config_file = False
            while True:
                if not running:
                    break
                time.sleep(5)
                if os.path.exists("/opt/ktouch/config"):
                    logger.info("配置文件出现, 进行一次映射，然后开始监控")
                    run_touch_remap()
                    break

        
        
        # 3. 启动后台进程
        start_background_processes()
        logger.info("主程序：启动子进程 OK")

        # 启动进程监控线程
        monitor_thread = Thread(target=monitor_processes)
        monitor_thread.daemon = True
        monitor_thread.start()
        logger.info("主程序：启动进程监控 OK")
        
        sleep(5)
        # 4. 首次执行touch_remap
        while True:
            if os.path.exists("/tmp/ktouch/screen.txt"):
                break
                # 如果screen.txt不存在可能引起故障
                
        run_touch_remap()
        logger.info("主程序：执行一次映射 OK")
        
        sleep(2)
        # 5. 开始监控文件变化
        logger.info("主程序：开始监控loop")
        monitor_files()
        
        
    except Exception as e:
        logger.error("Unexpected error in main: %s", e)
        running = False
        cleanup()
        sys.exit(1)
    finally:
        cleanup()
        logger.info("Daemon script stopped")

if __name__ == "__main__":
    main()
