import json
import os
import sys
import time
import ctypes
import pyautogui
import pygetwindow as gw
from PIL import Image

# 解决 Windows 高 DPI 缩放问题
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception:
    pass

# 默认布局参数（与 LogicBeacon：锚点 + 9 个数据块 对齐，需盖住第 9、10 格）
_DEFAULTS = {
    "pixel_size": 17,
    "block_start_x": 30,
    "scan_region_width": 190,
    "scan_region_height": 15,
    "offset_left": 20,
    "offset_top": 45,
    # 巡逻上马（coordinate_patrol，与 GUI「参数配置」一致）
    "enable_mount": True,
    "mount_key": "x",
    "mount_hold_sec": 1.6,
    "mount_retry_after_sec": 2.0,
    # 炉石回城
    "hearthstone_key": "b",
    "bag_full_hearthstone": False,
}

SCREENSHOT_DIR = 'screenshot'
CONFIG_FILE = 'game_state_config.json'


def _config_base():
    if getattr(sys, 'frozen', False):
        return os.path.dirname(sys.executable)
    return os.path.dirname(os.path.abspath(__file__))


def _get_config_path():
    base = _config_base()
    p = os.path.join(base, CONFIG_FILE)
    if os.path.exists(p):
        return p
    if getattr(sys, 'frozen', False) and getattr(sys, '_MEIPASS', ''):
        p2 = os.path.join(sys._MEIPASS, CONFIG_FILE)
        if os.path.exists(p2):
            return p2
    return p


def load_layout_config():
    """加载 game_state_config.json，返回布局参数字典"""
    path = _get_config_path()
    cfg = dict(_DEFAULTS)
    if os.path.exists(path):
        try:
            with open(path, 'r', encoding='utf-8') as f:
                loaded = json.load(f)
                for k in _DEFAULTS:
                    if k in loaded:
                        cfg[k] = loaded[k]
        except Exception:
            pass
    cfg['center_offset'] = cfg['pixel_size'] // 2
    return cfg


def _tri_state_from_r(channel_byte):
    """Lua SetColorTexture 的 R 通道 0 / 0.5 / 1.0 → 与最近档位对齐（抗截图量化误差）"""
    v = channel_byte / 255.0
    return min((0.0, 0.5, 1.0), key=lambda t: abs(t - v))


def _tri_state_label(value, low_mid_high):
    """将 0 / 0.5 / 1.0 三档转为对应中文说明（抗浮点误差）"""
    v = float(value) if value is not None else 0.0
    if v < 0.25:
        return low_mid_high[0]
    if v < 0.75:
        return low_mid_high[1]
    return low_mid_high[2]


def format_game_state_line(state):
    """单行状态文字（中文），供 CLI 与 GUI 共用。"""
    death_txt = ('存活', '尸体', '灵魂')[state.get('death_state', 0)]
    target_hp = state.get('target_hp')
    if target_hp is not None:
        hp_part = f"血:{state['hp']}% 目标血:{target_hp}% 法:{state['mp']}%"
    else:
        hp_part = f"血:{state['hp']}% 法:{state['mp']}%"
    combat_txt = '战斗中' if state['combat'] else '未战斗'
    target_txt = '有敌对目标' if state['target'] else '无有效目标'
    flight_txt = _tri_state_label(
        state.get('flight', 0),
        ('地面', '待起飞', '飞行中'),
    )
    mounted_txt = '已上马' if state.get('mounted') else '未上马'
    flyable_txt = '区域可飞' if state.get('flyable') else '区域不可飞'
    follow_txt = _tri_state_label(
        state.get('follow', 0),
        ('未跟随', '需补跟随', '跟随中'),
    )
    return (
        f"{hp_part} | "
        f"{combat_txt} {target_txt} | "
        f"空格:{state.get('free_slots', 0)} 耐久:{state.get('durability', 0):.0%} {death_txt} | "
        f"x:{state['x']} y:{state['y']} 朝向:{state['facing']:.1f}° | "
        f"{flight_txt} {mounted_txt} {flyable_txt} {follow_txt}"
    )


def save_layout_config(cfg):
    """保存布局配置到 game_state_config.json"""
    path = _get_config_path()
    out = {k: cfg[k] for k in _DEFAULTS if k in cfg}
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(out, f, indent=2, ensure_ascii=False)
    return path


def get_wow_info():
    windows = gw.getWindowsWithTitle('魔兽世界')
    if not windows:
        return None
    wow = windows[0]
    cfg = load_layout_config()
    return {
        "left": wow.left + cfg['offset_left'],
        "top": wow.top + cfg['offset_top'],
        "layout": cfg,
    }


def parse_game_state():
    pos = get_wow_info()
    if not pos:
        return None

    cfg = pos['layout']
    w = cfg['scan_region_width']
    h = cfg['scan_region_height']
    pixel_size = cfg['pixel_size']
    block_start_x = cfg['block_start_x']
    center_offset = cfg['center_offset']

    # 截图并保存到 screenshot 文件夹
    screenshot = pyautogui.screenshot(region=(pos['left'], pos['top'], w, h))
    os.makedirs(SCREENSHOT_DIR, exist_ok=True)
    screenshot.save(os.path.join(SCREENSHOT_DIR, 'game_state.png'))

    # 每 pixel_size 一格 → … x, y, 飞行块(pixels[9]), 跟随(pixels[10])
    CHANNEL_NAMES = (
        'hp', 'mp', 'combat', 'target', 'logistics_death', 'x', 'y',
        'flight_block', 'follow',
    )
    for idx, name in enumerate(CHANNEL_NAMES):
        left = block_start_x + idx * pixel_size
        box = (left, 0, left + pixel_size, pixel_size)
        crop = screenshot.crop(box)
        crop.save(os.path.join(SCREENSHOT_DIR, f'game_state_{name}.png'))

    # 每格取中心像素，避免裁到边缘混合色
    def get_val(idx):
        sample_x = block_start_x + (idx * pixel_size) + center_offset
        sample_y = center_offset
        return screenshot.getpixel((sample_x, sample_y))

    state = {}
    # idx 0=血(红), 1=法(蓝), 2=战斗(绿), 3=目标(红)
    hp_px = get_val(0)
    state['hp'] = round(hp_px[0] / 255 * 100)
    state['target_hp'] = round(hp_px[1] / 255 * 100)

    mp_px = get_val(1)
    state['mp'] = round(mp_px[2] / 255 * 100)

    cb_px = get_val(2)
    state['combat'] = cb_px[1] > 150  # 绿色通道

    tg_px = get_val(3)
    state['target'] = tg_px[0] > 150  # 红色通道

    # 第 5 像素：后勤与死亡 (R=空格数, G=耐久比例, B=死亡状态)
    p5 = get_val(4)
    state['free_slots'] = p5[0]
    state['durability'] = round(p5[1] / 255.0, 3)
    state['death_state'] = 0 if p5[2] < 50 else (1 if p5[2] < 200 else 2)  # 0存活 1尸体 2灵魂

    # 第 6 像素：x 坐标
    p6 = get_val(5)
    state['x'] = round((p6[0] + p6[1]/100), 2)
    raw_deg =  (p6[2] / 255) * 360.0
    state['facing'] = (360.0 - raw_deg) % 360.0

    # 第 7 像素：y 坐标
    p7 = get_val(6)
    state['y'] = round((p7[0] + p7[1]/100), 2)

    # 第 9 像素（Lua pixels[9]）：R=飞行信号，G=上马，B=可飞区域
    p9 = get_val(7)
    state['flight'] = _tri_state_from_r(p9[0])
    state['mounted'] = p9[1] > 127
    state['flyable'] = p9[2] > 127

    # 第 10 像素（Lua pixels[10]）：R=跟随信号
    p10 = get_val(8)
    state['follow'] = _tri_state_from_r(p10[0])

    return state

if __name__ == "__main__":
    print("开始监控魔兽状态... (Ctrl+C 退出)")
    try:
        while True:
            state = parse_game_state()
            if state:
                print(f"\r[状态] {format_game_state_line(state)}", end="")
            time.sleep(0.5)
    except KeyboardInterrupt:
        print("\n已停止。")