# -*- coding: utf-8 -*-
from .common import ADDON_PATH, get_setting, notification, log
from . import t9_helper
import xbmc
import xbmcgui
import xbmcvfs
import time
import threading
import queue
import json
import base64

# T9 多字符映射：数字 -> [数字, 字母...]
_MULTITAP_MAP = {
    '2': ['2', 'A', 'B', 'C'],
    '3': ['3', 'D', 'E', 'F'],
    '4': ['4', 'G', 'H', 'I'],
    '5': ['5', 'J', 'K', 'L'],
    '6': ['6', 'M', 'N', 'O'],
    '7': ['7', 'P', 'Q', 'R', 'S'],
    '8': ['8', 'T', 'U', 'V'],
    '9': ['9', 'W', 'X', 'Y', 'Z'],
}

class CharSelectorDialog(xbmcgui.WindowXMLDialog):
    """T9 多字符选择弹窗：连续按同一数字键循环选择字符，超时自动确认。"""

    CONFIRM_DELAY = 1.0  # 秒

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.digit = None
        self.chars = []
        self.current_index = 0
        self.selected_char = None
        self._timer = None
        self._closed = False

    def setup(self, digit):
        """在 doModal 之前调用，设置要显示的数字键。"""
        self.digit = digit
        self.chars = _MULTITAP_MAP.get(digit, [digit])
        self.current_index = 0
        self.selected_char = None

    def onInit(self):
        self._update_display()
        self._reset_timer()
        # 将焦点设到 T9 面板对应数字键上
        idx = 10 if self.digit == '0' else int(self.digit) - 1
        xbmc.executebuiltin(f'SetFocus(6050,{idx})')

    def _update_display(self):
        """根据当前字符数和选中索引，更新 Home Window 属性。"""
        home = xbmcgui.Window(10000)
        # 布局槽位: [0]=上, [1]=左, [2]=中(数字), [3]=右, [4]=下
        # 规则: 第1个字母=左, 第2个字母=上, 第3个字母=右, 第4个字母(如有)=下
        # 4 chars (3 letters, e.g. 8=TUV): 左=T, 上=U, 中=8, 右=V
        # 5 chars (4 letters, e.g. 9=WXYZ): 左=W, 上=X, 中=9, 右=Y, 下=Z

        if len(self.chars) == 5:
            slots = [self.chars[2], self.chars[1], self.chars[0], self.chars[3], self.chars[4]]
        elif len(self.chars) == 4:
            slots = [self.chars[2], self.chars[1], self.chars[0], self.chars[3], ""]
        else:
            slots = [self.chars[0], "", "", "", ""]

        self._slots = slots

        # 写入属性
        for i, ch in enumerate(slots):
            home.setProperty(f"MFG.CharSel.{i}", ch)

        # 活跃索引：current_index 对应 self.chars 中的位置，需映射到 slot 索引
        char = self.chars[self.current_index]
        active_slot = "2"  # default center
        for si, sc in enumerate(slots):
            if sc == char:
                active_slot = str(si)
                break
        home.setProperty("MFG.CharSel.Active", active_slot)

    def _reset_timer(self):
        if self._timer:
            self._timer.cancel()
        self._timer = threading.Timer(self.CONFIRM_DELAY, self._on_timeout)
        self._timer.daemon = True
        self._timer.start()

    def _on_timeout(self):
        if not self._closed:
            if self.selected_char is None:
                self.selected_char = self.chars[self.current_index]
            self.close()

    def onAction(self, action):
        action_id = action.getId()

        if action_id == 10 or action_id == 92:  # ESC / NavBack
            self._cancel_timer()
            self.selected_char = None
            self.close()
            return

        # 方向键直接选择对应字符: 上=slots[0], 左=slots[1], 右=slots[3], 下=slots[4]
        _DIR_SLOT = {3: 0, 1: 1, 2: 3, 4: 4}  # up=3, left=1, right=2, down=4
        if action_id in _DIR_SLOT:
            slot_idx = _DIR_SLOT[action_id]
            slots = getattr(self, '_slots', [])
            if slots and slot_idx < len(slots) and slots[slot_idx]:
                self._cancel_timer()
                # 显示选中效果
                xbmcgui.Window(10000).setProperty("MFG.CharSel.Active", str(slot_idx))
                self.selected_char = slots[slot_idx]
                # 短暂延迟后关闭，让用户看到选中高亮
                self._timer = threading.Timer(0.15, self._on_timeout)
                self._timer.daemon = True
                self._timer.start()
            return

        # 同一数字键 → 循环
        if 58 <= action_id <= 67:
            pressed_digit = str(action_id - 58)
            if pressed_digit == self.digit:
                self.current_index = (self.current_index + 1) % len(self.chars)
                self._update_display()
                self._reset_timer()
            else:
                # 不同数字键 → 确认当前，让父窗口处理新按键
                self._cancel_timer()
                self.selected_char = self.chars[self.current_index]
                self._pending_digit = pressed_digit
                self.close()
            return

    def _cancel_timer(self):
        if self._timer:
            self._timer.cancel()
            self._timer = None

    def close(self):
        self._closed = True
        self._cancel_timer()
        # 清理属性
        home = xbmcgui.Window(10000)
        for i in range(5):
            home.clearProperty(f"MFG.CharSel.{i}")
        home.clearProperty("MFG.CharSel.Active")
        super().close()


# 定义 Skin String 值到按钮 ID 的映射
# 格式: { 'SkinStringName': { 'Value': ButtonID, 'Default': ButtonID } }
FILTER_MAP = {
    'filter.sort': {
        'mapping': {
            '最新入库': 1013,
            '最近观看': 1015,
            '随机': 1014,
            '最新上线': 1011,
            '影片评分': 1012
        },
        'default': 1011 # 最新上线
    },
    'filter.mediatype': {
        'mapping': {
            '电影': 6002,
            '系列电影': 6003,
            '剧集': 6004,
            '演唱会': 6005,
            '纪录片': 6006
        },
        'default': 6001 # 全部
    },
    'filter.genre': {
        'mapping': {
            '动作': 2002,
            '喜剧': 2003,
            '爱情': 2004,
            '科幻': 2005,
            '犯罪': 2006,
            '冒险': 2007,
            '剧情': 2014,
            '恐怖': 2008,
            '动画': 2009,
            '战争': 2010,
            '悬疑': 2011,
            '历史': 2012,
            '音乐': 2013,
            '其他': 2015
        },
        'default': 2001 # 类型
    },
    'filter.region': {
        'mapping': {
            '内地': 3002,
            '中国香港': 3003,
            '中国台湾': 3004,
            '美国': 3005,
            '日本': 3006,
            '韩国': 3007,
            '泰国': 3008,
            '印度': 3009,
            '英国': 3010,
            '法国': 3011,
            '德国': 3012,
            '俄罗斯': 3014,
            '加拿大': 3015,
            '其他': 3013
        },
        'default': 3001 # 地区
    },
    'filter.year': {
        'mapping': {
            '今年': 4002,
            '2020年代': 4003,
            '2010年代': 4004,
            '2000年代': 4005,
            '90年代': 4006,
            '80年代': 4007,
            '70年代': 4008,
            '60年代': 4009,
            '更早': 4010
        },
        'default': 4001 # 年份
    },
    'filter.rating': {
        'mapping': {
            '10-9': 5002,
            '9-8': 5003,
            '8-7': 5004,
            '7-6': 5005,
            '6分以下': 5006
        },
        'default': 5001 # 评分
    }
}

# 构建 ID -> (组, 值) 的反向查找映射
FILTER_ID_TO_INFO_MAP = {}
for group, data in FILTER_MAP.items():
    # 默认按钮
    FILTER_ID_TO_INFO_MAP[data['default']] = (group, '')
    # 映射按钮
    for val, btn_id in data['mapping'].items():
        FILTER_ID_TO_INFO_MAP[btn_id] = (group, val)

class FilterWindow(xbmcgui.WindowXML):
    def _set_button_state(self, btn_id, is_selected):
        color_val = 'FFEB9E17' if is_selected else 'FFFFFFFF'
        xbmcgui.Window(10000).setProperty(f'MFG.FilterColor.{btn_id}', color_val)

    def _load_state_from_skin(self):

        self.filter_state = {}
        
        # 尝试先从单个 blob 加载
        blob = xbmc.getInfoLabel('Skin.String(MFG.State)')
        if blob:
            try:
                decoded = base64.b64decode(blob).decode('utf-8')
                loaded_state = json.loads(decoded)
                
                self.filter_state = loaded_state
                return
            except Exception as e:
                log(f"Error loading state blob: {e}", xbmc.LOGERROR)
        
        # 使用默认值初始化 (ID + 值)
        for group, data in FILTER_MAP.items():
            default_id = data['default']
            _, default_val = FILTER_ID_TO_INFO_MAP[default_id]
            default_obj = {'id': default_id, 'value': default_val}
            
            if group == 'filter.rating':
                self.filter_state[group] = [default_obj]
            else:
                self.filter_state[group] = default_obj

    def _save_state_to_skin(self):
        try:
            # 序列化为 JSON 和 Base64
            json_str = json.dumps(self.filter_state)
            encoded = base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
            
            # 保存到单个 Skin String
            xbmc.executebuiltin(f'Skin.SetString(MFG.State,{encoded})')
            log("Saved state to Skin.String(MFG.State)")
        except Exception as e:
            log(f"Error saving state: {e}", xbmc.LOGERROR)

    def update_highlights(self, target_group=None):
        """
        根据 self.filter_state 设置窗口属性以高亮显示活动按钮。
        如果提供了 target_group，则仅更新该组。
        """
        groups = [target_group] if target_group else self.filter_state.keys()

        for group in groups:
            state = self.filter_state.get(group)
            if state is None: continue

            data = FILTER_MAP.get(group)
            if not data: continue
            
            # 收集该组的所有 ID 以便先重置它们（或仅设置正确的状态）
            all_ids = list(data['mapping'].values())
            all_ids.append(data['default'])
            
            if isinstance(state, list): # 多选 (评分)
                active_ids = [x['id'] for x in state]
                for btn_id in all_ids:
                    self._set_button_state(btn_id, btn_id in active_ids)
            else: # 单选
                active_id = state['id']
                for btn_id in all_ids:
                    self._set_button_state(btn_id, btn_id == active_id)

    def _fav_from_custom_keymaps(self):
        """解析用户的 keymap XML，提取绑定了 toggle_favourite 的自定义按键代码"""
        import os
        import json
        import xml.etree.ElementTree as ET
        
        # 记录全局的键盘映射 button_code -> action_str
        global_keyboard_map = {}
        
        # 加载键盘映射 JSON
        mapping_file = os.path.join(os.path.dirname(__file__), '..', 'resources', 'keyboard_mapping.json')
        name_to_code = {}
        try:
            with open(mapping_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                name_to_code = data.get("name_to_code", {})
        except Exception as e:
            log(f"Failed to load keyboard mapping json: {e}", xbmc.LOGERROR)

        folder = xbmcvfs.translatePath('special://profile/keymaps/')
        if os.path.exists(folder):
            # 获取所有xml并按字母顺序排序，以正确处理Kodi的覆盖加载机制
            xml_files = sorted([f for f in os.listdir(folder) if f.lower().endswith('.xml')])
            
            for file_name in xml_files:
                file_path = os.path.join(folder, file_name)
                try:
                    tree = ET.parse(file_path)
                    root = tree.getroot()
                    
                    # Kodi 的 keymap 加载是不区分大小写的，我们遍历子节点匹配
                    for global_node in root:
                        if global_node.tag.lower() == 'global':
                            for keyboard_node in global_node:
                                if keyboard_node.tag.lower() == 'keyboard':
                                    # 遍历所有的按键标签
                                    for elem in keyboard_node:
                                        tag = elem.tag.lower()
                                        text = elem.text or ''
                                        button_code = None
                                        
                                        # 处理 <key id="xxx">
                                        if tag == 'key' and 'id' in elem.attrib:
                                            try:
                                                button_code = int(elem.attrib['id'])
                                            except ValueError:
                                                pass
                                        else:
                                            # 处理具有命名标签的映射
                                            if tag in name_to_code:
                                                button_code = name_to_code[tag]
                                                
                                        # 将得到的 button_code 映射覆盖，这就是Kodi执行的加载合并逻辑
                                        if button_code is not None:
                                            global_keyboard_map[button_code] = text
                                            
                except Exception as e:
                    log(f"Failed to parse keymap {file_path}: {e}", xbmc.LOGERROR)

        # 从最终合并完成的字典中，筛选出分配给了我们的 toggle_favourite 脚本的按钮代码
        allowed_button_codes = set()
        for code, action_text in global_keyboard_map.items():
            if 'plugin.video.filteredmovies' in action_text and 'toggle_favourite' in action_text:
                allowed_button_codes.add(code)

        log(f"Parsed favourite button codes: {allowed_button_codes}")
        return allowed_button_codes

    def onInit(self):
        # Set property to indicate window is open
        # Clear T9 input
        self.setProperty("t9_input", "")
        # Load state from Skin into Python memory
        self._load_state_from_skin()
        # 立即初始化筛选高亮
        self.update_highlights()
        
        
        # 初始化属性，确保非空
        xbmcgui.Window(10000).setProperty("MFG.T9Input", "")
        # self.refresh_container()
        self.input_queue = queue.Queue()
        self.running = True
        self.worker = threading.Thread(target=self._t9_input_worker)
        self.worker.daemon = True
        self.worker.start()
        # 异步准备搜索索引，避免阻塞窗口初始化。
        if get_setting('auto_write_search_index') != 'false':
            t9_helper.helper.ensure_search_index_ready_async(show_progress=True)

    def _t9_input_worker(self):
        monitor = xbmc.Monitor()
        last_input = ""
        last_input_time = time.time()
        while self.running:
            # 检查 Kodi 是否正在关闭
            if monitor.abortRequested():
                log("Abort requested, closing window")
                self.close()
                break

            try:
                events = []
                # 每0.1秒唤醒检查一次
                try:
                    events.append(self.input_queue.get(timeout=0.1))
                    # 只要队列不为空，就一次性取出所有积压事件
                    while not self.input_queue.empty():
                        try:
                            events.append(self.input_queue.get_nowait())
                        except queue.Empty:
                            break
                except queue.Empty:
                    pass
                
                current_input = xbmcgui.Window(10000).getProperty("MFG.T9Input") or ""
                # current_input = last_input
                # 批量处理所有事件
                for event in events:
                    if not event: 
                        continue
                    etype, evalue = event
                    if etype == 'input':
                        current_input += evalue
                    elif etype == 'delete':
                        current_input = current_input[:-1] if current_input else ""
                    elif etype == 'clear':
                        current_input = ""
                    elif etype == 'close':
                        log("close worker")
                        return
                if events:
                    xbmcgui.Window(10000).setProperty("MFG.T9Input", current_input)
                    last_input_time = time.time()

                    # 系列电影搜索未启用时，拦截输入
                    if current_input and current_input != "000000":
                        mt_state = self.filter_state.get('filter.mediatype', {})
                        if mt_state.get('value') == '系列电影':
                            enable_set_search = get_setting('enable_set_search') == 'true'
                            if not enable_set_search:
                                notification("系列电影搜索未启用，请在设置中开启")
                                current_input = ""
                                xbmcgui.Window(10000).setProperty("MFG.T9Input", current_input)
                                last_input = current_input
                                continue

                if current_input == "000000":
                    t9_helper.helper.ensure_search_index_ready_async(show_progress=True, skip_check=True)
                    notification("已开始重建电影/剧集 T9 索引...", sound=True)
                    log("Magic code 000000 detected. Rebuilding movie C16/tvshow C15 T9 storage.", xbmc.LOGWARNING)
                    current_input = ""
                    xbmcgui.Window(10000).setProperty("MFG.T9Input", current_input)
                    last_input = current_input

                if current_input != last_input:
                    if not current_input:
                        # 输入已清空，立即刷新列表
                        self.refresh_container()
                        last_input_time = time.time()
                        last_input = current_input
                    else:
                        if (time.time() - last_input_time > 0.5):
                            self.refresh_container()
                            last_input = current_input
                
            except Exception as e:
                log(f"Worker error: {e}", xbmc.LOGERROR)

    def cleanup(self):
        self.running = False
        if hasattr(self, 'input_queue'):
            self.input_queue.put(('close', None))
        if hasattr(self, 'worker') and self.worker.is_alive():
            self.worker.join(timeout=1.0)
        self._save_state_to_skin()
        
        # 清除全局属性
        xbmcgui.Window(10000).clearProperty("MFG.T9Input")

    def onAction(self, action):
        action_id = action.getId()
        button_code = action.getButtonCode()
        log(f"onAction: ID={action_id} ButtonCode={button_code}")
        
        # 关闭/返回/ESC等必须立即处理的按键
        if action_id == 10:  # ESC
            self.close()
            return
            
        if action_id == 122:  # HOME / Search / Built-in
            # 自动放行的全局系统按键列表
            if button_code in [61616, 61622, 61674, 61448, 61467, 61588, 61440]:
                log(f"Auto allowing action 122 for system button_code {button_code}")
                return

            # 延迟加载按键映射
            if not hasattr(self, 'fav_button_codes'):
                log("Parsing custom keymaps for the first time on action 122")
                self.fav_button_codes = self._fav_from_custom_keymaps()

            # 动态检查该 button_code 是否在用户的 keymap中被绑定为 toggle_favourite
            if self.fav_button_codes and button_code in self.fav_button_codes:
                log(f"Ignoring action 122 for button_code {button_code} because it is mapped to toggle_favourite")
                return
            self.close()
            return
        if action_id == 92:  # NavBack
            # 有输入情况输入，没有输入退出页面
            current_text = xbmcgui.Window(10000).getProperty("MFG.T9Input")
            if current_text and len(current_text) > 0:
                self.input_queue.put(('clear', None))
            else:
                self.close()
            return

        # 其他输入全部交给worker
        if 58 <= action_id <= 67:  # 0-9
            digit = str(action_id - 58)
            if self._is_digit_letter_mode() and digit in _MULTITAP_MAP:
                self._open_char_selector(digit)
            else:
                self.input_queue.put(('input', digit))
            return
        if action_id in [110, 112, 80]:  # Backspace, Delete, ItemDelete
            self.input_queue.put(('delete', None))
            return
        if action_id == 13:  # Stop -> 打开虚拟键盘输入
            self._open_keyboard_input()
            return

        pass

    def _is_digit_letter_mode(self):
        try:
            return get_setting('input_mode') != 'pure_digit'
        except Exception:
            return True

    def _open_char_selector(self, digit):
        """打开字符选择弹窗，连续按键循环选择。"""
        while digit:
            dlg = CharSelectorDialog(
                'Custom_1117_CharSelector.xml', ADDON_PATH, 'Default', '1080i')
            dlg.setup(digit)
            dlg.doModal()
            selected = dlg.selected_char
            pending = getattr(dlg, '_pending_digit', None)
            del dlg
            if selected:
                self.input_queue.put(('input', selected))
            # 如果用户按了不同的数字键，继续为新数字打开选择器
            if pending and pending in _MULTITAP_MAP:
                digit = pending
            elif pending:
                # 0 或 1 直接输入
                self.input_queue.put(('input', pending))
                digit = None
            else:
                digit = None

    def _open_keyboard_input(self):
        """打开虚拟键盘，将输入结果（大写字母）设为 T9 输入"""
        current_input = xbmcgui.Window(10000).getProperty("MFG.T9Input") or ""
        kb = xbmc.Keyboard(current_input, "搜索")
        kb.doModal()
        if kb.isConfirmed():
            text = kb.getText().strip().upper()
            # 清空后设置新值，让 worker 检测到变化并刷新
            self.input_queue.put(('clear', None))
            if text:
                self.input_queue.put(('input', text))

    def _handle_filter_click(self, controlId):
        # 检查 controlId 是否为已知的筛选按钮
        if controlId not in FILTER_ID_TO_INFO_MAP:
            return False
            
        group, val = FILTER_ID_TO_INFO_MAP[controlId]
        click_obj = {'id': controlId, 'value': val}
        
        # 处理评分筛选 (多选逻辑)
        if group == 'filter.rating':
            current_list = self.filter_state.get(group, [])
            default_id = FILTER_MAP[group]['default']
            
            # 检查当前点击的项是否已选中
            exists = False
            for x in current_list:
                if x['id'] == controlId:
                    exists = True
                    break
            
            if controlId == default_id:
                # 点击默认项（如"全部"），清空其他选项并只选中默认项
                _, def_val = FILTER_ID_TO_INFO_MAP[default_id]
                new_list = [{'id': default_id, 'value': def_val}]
            else:
                # 切换逻辑：如果已存在则移除，否则添加
                if exists:
                    new_list = [x for x in current_list if x['id'] != controlId]
                else:
                    new_list = current_list + [click_obj]
                
                # 如果选中了其他项，移除默认项
                has_default = any(x['id'] == default_id for x in new_list)
                if has_default and len(new_list) > 1:
                    new_list = [x for x in new_list if x['id'] != default_id]
                
                # 如果列表为空（取消了所有选择），自动选中默认项
                if not new_list:
                    _, def_val = FILTER_ID_TO_INFO_MAP[default_id]
                    new_list = [{'id': default_id, 'value': def_val}]
            
            self.filter_state[group] = new_list
            
            # 更新评分组所有按钮的UI状态
            # (因为多选会改变多个按钮的状态)
            self.update_highlights(group) # 优化为只更新该组
            
        else:
            # 单选逻辑 (其他筛选组)
            old_obj = self.filter_state.get(group)
            if old_obj and old_obj['id'] == controlId:
                return True # 点击已选中的项，无变化
            
            self.filter_state[group] = click_obj
            
            # 增量更新UI：取消旧的高亮，高亮新的
            if old_obj:
                self._set_button_state(old_obj['id'], False)
            self._set_button_state(controlId, True)
            
        self.refresh_container()
        return True

    def onClick(self, controlId):
        
        # 检查是否为筛选按钮 (ID 1000-6999，排除 T9 组 6000/6050)
        if 1000 <= controlId <= 6999:
            if self._handle_filter_click(controlId):
                return
            
            # 稍微延迟一点，以便 Skin.SetString (来自 XML onclick) 完成
            xbmc.sleep(50) 
            # self.update_highlights()
            # 触发容器刷新以更新列表
            self.refresh_container()

    
    def refresh_container(self):
        # 更新 ReloadID 以触发 XML 中的 content 刷新
        log("Refreshing container via ReloadID")
        # 触发刷新动画 (Fade Out)
        xbmcgui.Window(10000).setProperty("MFG.IsRefreshing", "true")
         # 等待淡出动画完成

        # 保存当前状态到 Skin，以便 default.py 读取
        self._save_state_to_skin()
        
        # 更新 ReloadID
        import time
        reload_id = str(time.time())
        xbmcgui.Window(10000).setProperty("MFG.ReloadID", "clear_" + reload_id)
        xbmc.sleep(100)
        reload_id = str(time.time())
        xbmcgui.Window(10000).setProperty("MFG.ReloadID", reload_id)


class DialogSelectWindow(xbmcgui.WindowXMLDialog):
    def __init__(self, strXMLname, strFallbackPath, strDefaultName, forceFallback='1080i'):
        super(DialogSelectWindow, self).__init__(strXMLname, strFallbackPath, strDefaultName, forceFallback)
        self.items = []
        self.selected_index = -1
        self.callback = None

    def setItems(self, items):
        self.items = items
        
    def setCallback(self, callback):
        self.callback = callback

    def onInit(self):
        self.list_control = self.getControl(100)
        focus_index = 0
        for i, item in enumerate(self.items):
            # item: {"label": "...", "index": ..., "is_active": bool}
            li = xbmcgui.ListItem(label=item["label"])
            if item["is_active"]:
                li.setProperty("IsActive", "true")
                focus_index = i
            self.list_control.addItem(li)
        
        # 将焦点设置到活动项
        self.list_control.selectItem(focus_index)
        self.setFocus(self.list_control)

    def onClick(self, controlId):
        if controlId == 100:
            self.selected_index = self.list_control.getSelectedPosition()
            if self.callback and self.selected_index >= 0 and self.selected_index < len(self.items):
                self.callback(self.items[self.selected_index])
            self.close()

    def onAction(self, action):
        # 允许的操作：方向键(1-4)，翻页(5-6)，确认(7)
        # 鼠标操作：100-107
        if action.getId() in [1, 2, 3, 4, 5, 6, 7] or (100 <= action.getId() <= 107):
            return
        self.close()

class OSDListWindow(xbmcgui.WindowXMLDialog):
    def __init__(self, strXMLname, strFallbackPath, strDefaultName, forceFallback='1080i'):
        super(OSDListWindow, self).__init__(strXMLname, strFallbackPath, strDefaultName, forceFallback)
        self.items = []
        self.callback = None

    def setItems(self, items):
        self.items = items
        
    def setCallback(self, callback):
        self.callback = callback

    def onInit(self):
        self.list_control = self.getControl(80000)
        focus_index = 0
        for i, item in enumerate(self.items):
            li = xbmcgui.ListItem(label=item["label"])
            if item["is_active"]:
                li.setProperty("IsActive", "true")
                focus_index = i
            self.list_control.addItem(li)
        
        self.list_control.selectItem(focus_index)
        self.setFocus(self.list_control)

    def onClick(self, controlId):
        if controlId == 80000:
            idx = self.list_control.getSelectedPosition()
            if self.callback and idx >= 0 and idx < len(self.items):
                self.callback(self.items[idx])
                # 不要关闭，仅更新
                self.close()
            else:
                self.close()

    def onAction(self, action):
        # 处理返回/退出键
        if action.getId() in [92, 10]:
            self.close()
        # 如果需要，传递其他动作，或者让基类处理
        super(OSDListWindow, self).onAction(action)


# 语言代码（ISO 639-1）→ 国家代码（ISO 3166-1 alpha-2，大写）映射，用于获取国旗图片


def _get_flag_path(lang_code):
    """根据语言代码返回本地国旗图片路径，与 fuse2 皮肤逻辑相同。"""
    if not lang_code or lang_code in ('unk', 'und', ''):
        return None
    import os
    flags_dir = os.path.join(ADDON_PATH, 'resources', 'skins', 'Default', 'media', 'flags')
    code = lang_code.lower()
    path = os.path.join(flags_dir, f'{code}.png')
    if os.path.exists(path):
        return path
    # 尝试 ISO 639-1 两字母码
    try:
        iso2 = xbmc.convertLanguage(lang_code, xbmc.ISO_639_1)
        if iso2:
            path2 = os.path.join(flags_dir, f'{iso2.lower()}.png')
            if os.path.exists(path2):
                return path2
    except Exception:
        pass
    return None


class MediaSelectWindow(xbmcgui.WindowXMLDialog):
    """统一字幕/音轨选择器窗口（Custom_1112_MediaSelect.xml）。

    用法：
        w = MediaSelectWindow('Custom_1112_MediaSelect.xml', ADDON_PATH, 'Default', '1080i')
        w.setInitialTab("subtitle")  # 或 "audio"
        w.doModal()
    数据获取和切换逻辑全部在窗口内部处理。
    """

    def __init__(self, strXMLname, strFallbackPath, strDefaultName, forceFallback='1080i'):
        super(MediaSelectWindow, self).__init__(strXMLname, strFallbackPath, strDefaultName, forceFallback)
        self.subtitle_items = []
        self.audio_items = []
        self.initial_tab = "subtitle"
        self.current_tab = "subtitle"
        self._switching_tab = False
        # 字幕状态（窗口打开后保持同步）
        self._sub_current = -1
        self._sub_enabled = False
        self._player = None
        # 音轨状态
        self._audio_current = -1

    def setInitialTab(self, tab):
        """在 doModal() 之前调用，设置默认聚焦的 Tab。"""
        self.initial_tab = tab

    # 保留向后兼容的 setter（不再必须调用）
    def setSubtitleItems(self, items):
        self.subtitle_items = items or []

    def setAudioItems(self, items):
        self.audio_items = items or []

    def setOnSubtitleSelect(self, callback):
        pass  # 已废弃，内部处理

    def setOnAudioSelect(self, callback):
        pass  # 已废弃，内部处理

    def onInit(self):
        from .media_info import get_subtitle_items, get_audio_items
        self.subtitle_list = self.getControl(100)
        self.audio_list = self.getControl(101)

        # 拉取数据
        sub_items, sub_current, sub_enabled, player = get_subtitle_items(suppress_warning=(self.initial_tab == "audio"))
        audio_items, audio_current = get_audio_items(suppress_warning=(self.initial_tab == "subtitle"))

        self.subtitle_items = sub_items or []
        self.audio_items = audio_items or []
        self._sub_current = sub_current if sub_current is not None else -1
        self._sub_enabled = sub_enabled or False
        self._player = player
        self._audio_current = audio_current if audio_current is not None else -1

        log(f"[MediaSelectWindow.onInit] sub_current={self._sub_current}, sub_enabled={self._sub_enabled}, audio_current={self._audio_current}")

        # 填充字幕列表
        sub_focus = 0
        for i, item in enumerate(self.subtitle_items):
            li = xbmcgui.ListItem(label=item.get("label", ""))
            li.setProperty("SubLanguage", item.get("language", ""))
            li.setProperty("SubName", item.get("name", ""))
            li.setProperty("SubExtraFlags", item.get("extra_flags", ""))
            if item.get("is_active"):
                li.setProperty("IsActive", "true")
                sub_focus = i
            flag_path = _get_flag_path(item.get("lang_code", ""))
            if flag_path:
                li.setArt({'thumb': flag_path})
            self.subtitle_list.addItem(li)
        if self.subtitle_items:
            self.subtitle_list.selectItem(sub_focus)

        # 填充音轨列表
        audio_focus = 0
        for i, item in enumerate(self.audio_items):
            li = xbmcgui.ListItem(label=item.get("label", ""))
            language_and_name = item.get("language_and_name", "")
            if not language_and_name:
                language_value = item.get("language", "")
                name_value = item.get("name", "")
                if language_value and name_value:
                    language_and_name = f"{language_value}-{name_value}"
                else:
                    language_and_name = language_value or name_value

            li.setProperty("AudioLanguageAndName", language_and_name)

            codec_value = item.get("codec", "")
            code_info_value = item.get("code_info", "")
            li.setProperty("AudioCodec", codec_value)
            li.setProperty("AudioCodeInfo", code_info_value)
            li.setProperty("AudioChannel", item.get("channel", ""))
            li.setProperty("AudioExtraFlags", item.get("extra_flags", ""))
            if item.get("is_active"):
                li.setProperty("IsActive", "true")
                audio_focus = i
            flag_path = _get_flag_path(item.get("lang_code", ""))
            if flag_path:
                li.setArt({'thumb': flag_path})
            self.audio_list.addItem(li)
        if self.audio_items:
            self.audio_list.selectItem(audio_focus)

        # 切换到初始 Tab
        self.current_tab = self.initial_tab
        xbmc.sleep(50)
        if self.initial_tab == "subtitle":
            self.setFocus(self.subtitle_list)
        else:
            self.setFocus(self.audio_list)

    def _switch_tab(self, tab):
        """切换活动 Tab 并将焦点移到对应列表。"""
        self._switching_tab = True
        self.current_tab = tab
        xbmcgui.Window(10000).setProperty("MFG.SelectorTab", tab)
        xbmc.sleep(50)
        if tab == "subtitle":
            self.setFocus(self.subtitle_list)
        else:
            self.setFocus(self.audio_list)
        self._switching_tab = False

    def onClick(self, controlId):
        if controlId == 10:
            self._switch_tab("subtitle")
        elif controlId == 11:
            self._switch_tab("audio")
        elif controlId == 100:
            # 字幕列表选中
            idx = self.subtitle_list.getSelectedPosition()
            if 0 <= idx < len(self.subtitle_items):
                selected = self.subtitle_items[idx]
                real_index = selected["index"]
                log(f"[MediaSelectWindow] subtitle clicked idx={idx}, real_index={real_index}, sub_current={self._sub_current}, sub_enabled={self._sub_enabled}")
                if self._sub_enabled and real_index == self._sub_current:
                    self._player.showSubtitles(False)
                    self._sub_enabled = False
                    from .common import notification
                    notification("字幕已关闭")
                    self._update_active(self.subtitle_list, -1)
                else:
                    self._player.setSubtitleStream(real_index)
                    self._player.showSubtitles(True)
                    self._sub_enabled = True
                    self._sub_current = real_index
                    from .common import notification
                    notification(f"字幕已切换至: {selected['label'].strip()}")
                    self._update_active(self.subtitle_list, idx)
            else:
                self.close()
        elif controlId == 101:
            # 音轨列表选中
            idx = self.audio_list.getSelectedPosition()
            if 0 <= idx < len(self.audio_items):
                selected = self.audio_items[idx]
                real_index = selected["index"]
                log(f"[MediaSelectWindow] audio clicked idx={idx}, real_index={real_index}, audio_current={self._audio_current}")
                if real_index == self._audio_current:
                    from .common import notification
                    notification("已是当前音轨")
                else:
                    xbmc.Player().setAudioStream(real_index)
                    self._audio_current = real_index
                    from .common import notification
                    notification(f"音轨已切换至: {selected['label'].strip()}")
                    self._update_active(self.audio_list, idx)
            else:
                self.close()

    def onAction(self, action):
        if self._switching_tab:
            return
        action_id = action.getId()
        if action_id in [10, 92]:
            # 返回/退出键：关闭
            self.close()
        elif action_id == 2 and self.current_tab == "subtitle":
            # 字幕 Tab 按右 → 切换到音轨 Tab
            self._switch_tab("audio")
        elif action_id == 1 and self.current_tab == "audio":
            # 音轨 Tab 按左 → 切换到字幕 Tab
            self._switch_tab("subtitle")
        elif action_id == 3:
            # UP 键：若焦点在 Tab 按钮上，跳到当前列表最底部
            focused = self.getFocusId()
            if focused in [10, 11]:
                if self.current_tab == "subtitle":
                    count = self.subtitle_list.size()
                    if count > 0:
                        self.subtitle_list.selectItem(count - 1)
                    self.setFocus(self.subtitle_list)
                else:
                    count = self.audio_list.size()
                    if count > 0:
                        self.audio_list.selectItem(count - 1)
                    self.setFocus(self.audio_list)
            # 其他控件的上键交给系统处理
        elif action_id == 7:
            # 确定键：由 onClick 处理，这里忽略
            return
        elif action_id in [4, 5, 6, 88, 89, 91] or (100 <= action_id <= 107):
            # 其他方向键/翻页/音量加减/静音，鼠标：交给控件处理
            return
        else:
            log(f"[MediaSelectWindow] onAction unhandled action_id={action_id}, closing window")
            self.close()

    def _update_active(self, list_control, active_idx):
        """更新列表中所有项的 IsActive 属性。"""
        count = list_control.size()
        for i in range(count):
            item = list_control.getListItem(i)
            item.setProperty("IsActive", "true" if i == active_idx else "")

    def close(self):
        xbmcgui.Window(10000).clearProperty("MFG.SelectorTab")
        super(MediaSelectWindow, self).close()

