import os
import re
import shutil
import subprocess

def get_ascii_and_punct():
    chars = [chr(i) for i in range(0x20, 0x7F)]
    puncts = "，。！？；：“”‘’（）【】《》、·…—～￥\n\r\t"
    return "".join(chars) + puncts

def extract_text_from_files(directory, ext_list):
    text_set = set()
    for root, _, files in os.walk(directory):
        for file in files:
            if any(file.endswith(ext) for ext in ext_list):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                        for char in content:
                            if char.strip() or char in [' ', '\n', '\t']:
                                text_set.add(char)
                except:
                    pass
    return text_set

def get_wendingguanggaoti_chars(project_dir, root_dir):
    chars = set()
    
    # 1. plugins.js: core.fillText and core.fillBoldText
    plugins_path = os.path.join(project_dir, 'plugins.js')
    if os.path.exists(plugins_path):
        with open(plugins_path, 'r', encoding='utf-8') as f:
            content = f.read()
            for line in content.split('\n'):
                if 'WenDingGuangGaoTi' in line:
                    matches = re.findall(r'["\']([^"\']*)["\']', line)
                    for match in matches:
                        # exclude styling tokens
                        if match != 'WenDingGuangGaoTi' and 'px' not in match and '#' not in match and 'rgb' not in match:
                            for c in match: chars.add(c)
                    if '+' in line:
                        for c in '0123456789': chars.add(c)
                        
    # 2. index.html: start screen buttons
    index_path = os.path.join(root_dir, 'index.html')
    if os.path.exists(index_path):
        with open(index_path, 'r', encoding='utf-8') as f:
            content = f.read()
            for btn_id in ['playGame', 'loadGame', 'replayGame', 'gameIntroduce', 'aboutEnemys']:
                match = re.search(fr'id\s*=\s*[\'"]{btn_id}[\'"][^>]*>(.*?)</', content)
                if match:
                    for c in match.group(1): chars.add(c)
                    
    # 3. \g[广告体] and \g[WenDingGuangGaoTi]
    for root, _, files in os.walk(project_dir):
        for file in files:
            if file.endswith('.js'):
                with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
                    content = f.read()
                    matches = re.findall(r'\\g\[(?:广告体|WenDingGuangGaoTi)\]([^"\']*)', content)
                    for match in matches:
                        text = match.split('\\')[0]
                        for c in text: chars.add(c)

    return chars
def get_chaozifont_chars(project_dir, root_dir):
    chars = set()
    
    # 1. index.html: #startLogo
    index_path = os.path.join(root_dir, 'index.html')
    if os.path.exists(index_path):
        with open(index_path, 'r', encoding='utf-8') as f:
            content = f.read()
            match = re.search(r'id\s*=\s*[\'"]startLogo[\'"][^>]*>(.*?)</', content)
            if match:
                for c in match.group(1): chars.add(c)
                
    # 2. floors/*.js: title and \t[name]
    floors_dir = os.path.join(project_dir, 'floors')
    if os.path.exists(floors_dir):
        for root, _, files in os.walk(floors_dir):
            for file in files:
                if file.endswith('.js'):
                    with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
                        content = f.read()
                        matches = re.findall(r'["\']title["\']\s*:\s*["\']([^"\']+)["\']', content)
                        for match in matches:
                            for c in match: chars.add(c)
                        # Extract dialog titles like \t[商人,woman]
                        dialog_titles = re.findall(r'\\t\[([^,\]]+)[^\]]*\]', content)
                        for match in dialog_titles:
                            for c in match: chars.add(c)
                            
    # Also extract from data.js, items.js, enemys.js, events.js for \t[...] and \g[书法字体]
    for filename in ['data.js', 'items.js', 'enemys.js', 'events.js', 'plugins.js']:
        filepath = os.path.join(project_dir, filename)
        if os.path.exists(filepath):
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()
                dialog_titles = re.findall(r'\\t\[([^,\]]+)[^\]]*\]', content)
                for match in dialog_titles:
                    for c in match: chars.add(c)
                # extract \g[书法字体] and \g[ChaoZiSheZengYuBoShouShuJianFan]
                g_matches = re.findall(r'\\g\[(?:书法字体|ChaoZiSheZengYuBoShouShuJianFan)\]([^"\']*)', content)
                for match in g_matches:
                    text = match.split('\\')[0]
                    for c in text: chars.add(c)
                    
    return chars

def get_weibei_chars(project_dir):
    chars = set()
    for root, _, files in os.walk(project_dir):
        for file in files:
            if file.endswith('.js'):
                with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
                    content = f.read()
                    matches = re.findall(r'\\g\[(?:魏碑体|SanJiXinWeiBeiJian-2)\]([^"\']*)', content)
                    for match in matches:
                        text = match.split('\\')[0]
                        for c in text: chars.add(c)
    return chars

def main():
    root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    project_dir = os.path.join(root_dir, "project")
    libs_dir = os.path.join(root_dir, "libs")
    tools_dir = os.path.join(root_dir, "tools")
    if not os.path.exists(tools_dir): os.makedirs(tools_dir)
    
    print("Extracting ALL texts for general fonts...")
    exts = [".js", ".html", ".css", ".json"]
    global_chars_set = extract_text_from_files(project_dir, exts)
    if os.path.exists(libs_dir): global_chars_set.update(extract_text_from_files(libs_dir, exts))
    
    for file in os.listdir(root_dir):
        if file.endswith(".html"):
            try:
                with open(os.path.join(root_dir, file), 'r', encoding='utf-8') as f:
                    for char in f.read():
                        if char.strip() or char in [' ', '\n', '\t']: global_chars_set.add(char)
            except: pass

    basic = get_ascii_and_punct()
    for char in basic: global_chars_set.add(char)
    global_chars = "".join(sorted(list(global_chars_set)))
    
    wending_chars = "".join(sorted(list(get_wendingguanggaoti_chars(project_dir, root_dir) | set(basic))))
    chaozi_chars = "".join(sorted(list(get_chaozifont_chars(project_dir, root_dir) | set(basic))))
    weibei_chars = "".join(sorted(list(get_weibei_chars(project_dir) | set(basic))))
    
    print(f"Global unique characters: {len(global_chars)}")
    print(f"WenDingGuangGaoTi specific characters: {len(wending_chars)}")
    print(f"ChaoZiSheZengYuBoShouShuJianFan specific characters: {len(chaozi_chars)}")
    print(f"SanJiXinWeiBeiJian-2 specific characters: {len(weibei_chars)}")
    
    fonts_dir = os.path.join(project_dir, "fonts")
    fonts_src_dir = os.path.join(project_dir, "fonts_src")
    if not os.path.exists(fonts_src_dir): os.makedirs(fonts_src_dir)
        
    for font_file in os.listdir(fonts_dir):
        if font_file.endswith(".ttf") or font_file.endswith(".otf"):
            src_path = os.path.join(fonts_dir, font_file)
            backup_path = os.path.join(fonts_src_dir, font_file)
            if not os.path.exists(backup_path): shutil.move(src_path, backup_path)
            
            if 'WenDingGuangGaoTi' in font_file:
                target_chars = wending_chars
            else:
                target_chars = global_chars
                
            chars_file = os.path.join(tools_dir, f"chars_{font_file}.txt")
            with open(chars_file, "w", encoding="utf-8") as f:
                f.write(target_chars)
            
            print(f"Subsetting {font_file} with {len(target_chars)} chars...")
            subset_args = [
                "pyftsubset", backup_path, f"--text-file={chars_file}",
                f"--output-file={src_path}", "--layout-features=*",
                "--glyph-names", "--symbol-cmap", "--legacy-cmap",
                "--notdef-glyph", "--notdef-outline", "--recommended-glyphs",
                "--name-IDs=*", "--name-legacy", "--name-languages=*"
            ]
            
            try:
                subprocess.run(subset_args, check=True, capture_output=True)
                print(f"  {font_file}: {os.path.getsize(backup_path)/1024:.1f} KB -> {os.path.getsize(src_path)/1024:.1f} KB")
            except subprocess.CalledProcessError as e:
                if b"TTLibFileIsCollectionError" in e.stderr:
                    try:
                        subprocess.run(subset_args + ["--font-number=0"], check=True, capture_output=True)
                        print(f"  {font_file} (collection 0): {os.path.getsize(backup_path)/1024:.1f} KB -> {os.path.getsize(src_path)/1024:.1f} KB")
                    except Exception as e2: pass
                else: print(f"Failed to subset {font_file}: {e.stderr.decode('utf-8', errors='replace')}")

if __name__ == "__main__":
    main()
