/* 
 * CaelLab BY-SA Code License 
 * Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS 
 * Source: https://git.caellab.com/yunyun/GoodPlanCraftLauncher 
 * Source: https://github.com/yunyun-3782/GoodPlanCraftLauncher 
 */



const { app, BrowserWindow, ipcMain, net, dialog, Menu, shell } = require('electron');
const path = require('path');
const { spawn, spawnSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const { tmpdir } = require('os');
const { pipeline } = require('stream/promises');

process.env.TZ = 'Asia/Shanghai';

app.commandLine.appendSwitch('js-flags', '--compile-hints-always');
app.commandLine.appendSwitch('disk-cache-size', '134217728');

function formatTimestamp() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, '0');
  const day = String(now.getDate()).padStart(2, '0');
  const hours = String(now.getHours()).padStart(2, '0');
  const minutes = String(now.getMinutes()).padStart(2, '0');
  const seconds = String(now.getSeconds()).padStart(2, '0');
  const ms = String(now.getMilliseconds()).padStart(3, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
}

function checkDebuggerAttached() {
  try {
    if (process.execArgv.some(arg => arg.includes('--inspect') || arg.includes('--debug-brk'))) {
      return true;
    }
    if (process.env.ELECTRON_ENABLE_STACK_DUMPING) {
      return true;
    }
  } catch (e) {}
  return false;
}


let isDeveloperMode = false;

let BASE_DIR;
let GAME_DIR;
let CACHE_DIR;
let LOG_DIR;
let LOG_FILE;
let APP_VERSION;
let CONFIG_DIR;

const DEFAULT_SETTINGS = {
  game: {
    memory: '2',
    playerName: 'GPCL_Player',
    javaPath: '',
    jvmArgs: '',
    windowMode: 'windowed'
  },
  appearance: {
    theme: 'light',
    scale: '110'
  },
  download: {
    maxConcurrent: 64,
    javaMirror: 'tsinghua',
    customJavaMirror: ''
  },
  advanced: {
    autoCheckUpdate: true,
    preventMultipleLaunch: true,
    autoClearLogs: true,
    logRetentionValue: 7,
    logRetentionUnit: 'day',
    playStartupAnimation: true,
    developerMode: false
  }
};

function parseIni(text) {
  const result = {};
  let currentSection = null;
  const lines = text.split(/\r?\n/);
  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith(';') || trimmed.startsWith('#')) {
      continue;
    }
    const sectionMatch = trimmed.match(/^\[(.+)\]$/);
    if (sectionMatch) {
      currentSection = sectionMatch[1].toLowerCase();
      if (!result[currentSection]) {
        result[currentSection] = {};
      }
      continue;
    }
    const keyValueMatch = trimmed.match(/^([^=]+)=(.*)$/);
    if (keyValueMatch && currentSection) {
      const key = keyValueMatch[1].trim();
      let value = keyValueMatch[2].trim();
      if (value.toLowerCase() === 'true') {
        value = true;
      } else if (value.toLowerCase() === 'false') {
        value = false;
      } else {
        const num = Number(value);
        if (!isNaN(num)) {
          value = num;
        }
      }
      result[currentSection][key] = value;
    }
  }
  return result;
}

function stringifyIni(obj) {
  let result = '';
  for (const [section, sectionData] of Object.entries(obj)) {
    result += `[${section}]\n`;
    for (const [key, value] of Object.entries(sectionData)) {
      result += `${key}=${value}\n`;
    }
    result += '\n';
  }
  return result.trim();
}

function mergeWithDefaults(settings) {
  const merged = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
  for (const [section, sectionData] of Object.entries(settings)) {
    if (merged[section]) {
      for (const [key, value] of Object.entries(sectionData)) {
        if (key in merged[section]) {
          merged[section][key] = value;
        }
      }
    }
  }
  return merged;
}

function loadSettings() {
  try {
    const configPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
    if (fs.existsSync(configPath)) {
      const content = fs.readFileSync(configPath, 'utf8');
      const parsed = parseIni(content);
      const merged = mergeWithDefaults(parsed);
      writeLog('配置已加载');
      return merged;
    }
  } catch (e) {
    writeLog(`加载配置失败: ${e.message}`);
  }
  writeLog('使用默认配置');
  return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
}

function saveSettings(settings) {
  try {
    const configDir = path.join(BASE_DIR, 'gpcl', 'config');
    const configPath = path.join(configDir, 'setting.ini');
    if (!fs.existsSync(configDir)) {
      fs.mkdirSync(configDir, { recursive: true });
    }
    const content = stringifyIni(settings);
    fs.writeFileSync(configPath, content, 'utf8');
    writeLog('配置已保存');
    return true;
  } catch (e) {
    writeLog(`保存配置失败: ${e.message}`);
    return false;
  }
}

function initPaths() {
  BASE_DIR = app.getPath('userData');
  GAME_DIR = path.join(BASE_DIR, '.minecraft');
  CACHE_DIR = path.join(BASE_DIR, 'gpcl', 'cache');
  LOG_DIR = path.join(BASE_DIR, 'gpcl', 'log');
  CONFIG_DIR = path.join(BASE_DIR, 'gpcl', 'config');
  const startupTime = new Date().toISOString().replace(/:/g, '-').replace('.', '_');
  LOG_FILE = path.join(LOG_DIR, `${startupTime}.log`);

  const requiredDirs = [
    path.join(BASE_DIR, 'gpcl', 'config'),
    path.join(BASE_DIR, 'gpcl', 'users'),
    LOG_DIR,
    CACHE_DIR
  ];

  for (const dir of requiredDirs) {
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
  }
}

function cleanupExpiredLogs() {
  try {
    const settings = loadSettings();
    const autoClearLogs = settings.advanced?.autoClearLogs;
    
    if (!autoClearLogs) {
      writeLog('[日志清理] 自动清理日志已禁用');
      return;
    }

    const retentionValue = settings.advanced?.logRetentionValue || 7;
    const retentionUnit = settings.advanced?.logRetentionUnit || 'day';

    // 计算过期时间
    const now = Date.now();
    let expireTime;
    
    switch (retentionUnit) {
      case 'hour':
        expireTime = now - retentionValue * 60 * 60 * 1000;
        break;
      case 'day':
        expireTime = now - retentionValue * 24 * 60 * 60 * 1000;
        break;
      case 'month':
        expireTime = now - retentionValue * 30 * 24 * 60 * 60 * 1000;
        break;
      case 'year':
        expireTime = now - retentionValue * 365 * 24 * 60 * 60 * 1000;
        break;
      default:
        expireTime = now - retentionValue * 24 * 60 * 60 * 1000;
    }

    if (!fs.existsSync(LOG_DIR)) {
      writeLog('[日志清理] 日志目录不存在');
      return;
    }

    const files = fs.readdirSync(LOG_DIR);
    let cleanedCount = 0;

    for (const file of files) {
      if (!file.endsWith('.log')) continue;
      
      const filePath = path.join(LOG_DIR, file);
      try {
        const stat = fs.statSync(filePath);
        if (stat.mtime.getTime() < expireTime) {
          fs.unlinkSync(filePath);
          cleanedCount++;
        }
      } catch (e) {
        writeLog(`[日志清理] 清理文件失败: ${file} - ${e.message}`);
      }
    }

    if (cleanedCount > 0) {
      writeLog(`[日志清理] 已清理 ${cleanedCount} 个过期日志文件`);
    } else {
      writeLog('[日志清理] 没有需要清理的过期日志');
    }
  } catch (e) {
    writeLog(`[日志清理] 清理过程出错: ${e.message}`);
  }
}

let mainWindow;
APP_VERSION = require('./package.json').version;

function writeLog(message) {
  const timestamp = formatTimestamp();
  const logLine = `[${timestamp}] ${message}\n`;
  try {
    if (LOG_FILE) {
      fs.appendFileSync(LOG_FILE, logLine, 'utf8');
    }
    console.log(`[${timestamp}] LOG:`, message);
  } catch (e) {
    console.error(`[${formatTimestamp()}] 写入日志失败:`, e);
  }
}

let isDownloading = false;
let currentDownloadVersion = null;
let shouldCancelDownload = false;
let currentDownloadType = null; // 'game' or 'java'
let currentDownloadPath = null; // 下载的文件或文件夹路径
let javaDownloadResolve = null; // Java下载对话框的Promise resolve
let javaDownloadReject = null; // Java下载对话框的Promise reject
let isJavaDownloading = false; // Java下载中标志

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 700,
    minWidth: 800,
    minHeight: 600,
    maxWidth: 1920,
    maxHeight: 1080,
    frame: false,
    title: 'GoodPlanCraftLauncher',
    icon: path.join(__dirname, 'gpcl', 'logo.ico'),
    backgroundColor: '#0d0d15',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
      devTools: true
    }
  });

  try { Menu.setApplicationMenu(null); } catch (e) {}
  mainWindow.setMenuBarVisibility(false);
  mainWindow.setAutoHideMenuBar(true);

  mainWindow.loadFile(path.join(__dirname, 'renderer', 'splash.html'));

  // 关闭窗口前拦截
  mainWindow.on('close', (event) => {
    if (isDownloading || isJavaDownloading) {
      event.preventDefault();
      // 发送询问到渲染进程
      mainWindow.webContents.send('confirm-close-while-downloading');
    }
  });

  mainWindow.webContents.on('before-input-event', (event, input) => {
    if (isDeveloperMode) return;
    const key = input.key && input.key.toLowerCase();
    const ctrlOrCmd = input.control || input.meta;
    if ((ctrlOrCmd && key === 'r') || input.key === 'F5') event.preventDefault();
    if ((input.control && input.shift && key === 'i') || input.key === 'F12' || (input.meta && input.alt && key === 'i')) event.preventDefault();
  });

  mainWindow.webContents.on('context-menu', (e) => {
    if (!isDeveloperMode) e.preventDefault();
  });
}

app.on('web-contents-created', (event, contents) => {
  try {
    contents.on('context-menu', (e) => {
      if (!isDeveloperMode) e.preventDefault();
    });
    contents.on('before-input-event', (event, input) => {
      if (isDeveloperMode) return;
      const key = input.key && input.key.toLowerCase();
      const ctrlOrCmd = input.control || input.meta;
      if ((ctrlOrCmd && key === 'r') || input.key === 'F5') event.preventDefault();
      if ((input.control && input.shift && key === 'i') || input.key === 'F12' || (input.meta && input.alt && key === 'i')) event.preventDefault();
    });
  } catch (e) {}
});

// 单实例锁：当检测到第二个实例时，凸显已有窗口
app.on('second-instance', () => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) mainWindow.restore();
    mainWindow.focus();
  }
});

function createGPCLShortcut() {
  try {
    const startMenuDir = path.join(app.getPath('appData'), 'Microsoft', 'Windows', 'Start Menu', 'Programs');
    const shortcutPath = path.join(startMenuDir, 'GPCL.lnk');
    
    if (fs.existsSync(shortcutPath)) {
      return;
    }
    
    if (app.isPackaged) {
      const exePath = process.execPath;
      shell.writeShortcutLink(shortcutPath, 'create', {
        target: exePath,
        description: 'GoodPlanCraftLauncher - Minecraft 启动器',
        icon: exePath,
        iconIndex: 0
      });
      writeLog('[快捷方式] 已创建 GPCL 快捷方式');
    }
  } catch (e) {
    writeLog(`[快捷方式] 创建 GPCL 快捷方式失败: ${e.message}`);
  }
}

app.whenReady().then(() => {
  // 检测是否是 electron-builder 的测试运行
  const isTesting = process.argv.includes('--squirrel-install') || 
                    process.argv.includes('--squirrel-obsolete') ||
                    process.argv.includes('--squirrel-updated') ||
                    process.argv.includes('--testing');
  
  if (isTesting) {
    console.log('[启动] 检测到构建测试模式，跳过窗口创建');
    app.quit();
    return;
  }

  // 提前初始化 BASE_DIR 以读取配置文件
  BASE_DIR = app.getPath('userData');

  // 检查是否启用防止多次启动
  let preventMultipleLaunch = true;
  try {
    const configPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
    if (fs.existsSync(configPath)) {
      const content = fs.readFileSync(configPath, 'utf8');
      const parsed = parseIni(content);
      if (parsed.advanced) {
        if (parsed.advanced.preventMultipleLaunch === false || parsed.advanced.preventmultiplelaunch === false) {
          preventMultipleLaunch = false;
        }
      }
    }
  } catch (e) {}

  if (preventMultipleLaunch) {
    const gotLock = app.requestSingleInstanceLock();
    if (!gotLock) {
      writeLog('检测到已有实例运行，退出');
      app.quit();
      return;
    }
  }

  initPaths();
  
  cleanupExpiredLogs();
  
  try {
    const devConfigPath = path.join(BASE_DIR, 'gpcl', 'config', 'setting.ini');
    if (fs.existsSync(devConfigPath)) {
      const devContent = fs.readFileSync(devConfigPath, 'utf8');
      const devParsed = parseIni(devContent);
      if (devParsed.advanced && devParsed.advanced.developerMode === true) {
        isDeveloperMode = true;
        writeLog('[开发者模式] 启动时已启用');
      }
    }
  } catch (e) {}
  
  createGPCLShortcut();
  
  writeLog(`GPCL v${APP_VERSION} 启动`);
  console.log('日志文件路径:', LOG_FILE);
  createWindow();
});
app.on('window-all-closed', () => app.quit());

ipcMain.handle('show-message-dialog', async (_, options) => {
  if (!mainWindow) return { success: false };
  const result = await dialog.showMessageBox(mainWindow, {
    type: options.type || 'info',
    title: options.title || '提示',
    message: options.message || '',
    detail: options.detail || '',
    buttons: options.buttons || ['确定'],
    defaultId: options.defaultId || 0,
    cancelId: options.cancelId || 0,
    noLink: true
  });
  return { success: true, response: result.response };
});

// Java版本 → 官方runtime目录名映射
const JAVA_RUNTIME_MAP = {
  "8": "jre-legacy",
  "16": "java-runtime-beta",
  "17": "java-runtime-gamma",
  "21": "java-runtime-delta",
  "25": "java-runtime-epsilon"
};

// Java版本 → 下载URL的key映射
const JAVA_DOWNLOAD_KEYS = {
  "8": "jre-legacy",
  "17": "java-runtime-gamma",
  "21": "java-runtime-delta",
  "25": "java-runtime-epsilon"
};

// 可选择的Java版本列表
const JAVA_VERSIONS_AVAILABLE = ["8", "17", "21", "25"];

// 获取Java运行时目录（官方目录结构，使用绝对路径）
function getJavaRuntimeDir(javaVersion) {
  const runtimeName = JAVA_RUNTIME_MAP[javaVersion] || `jre${javaVersion}`;
  // 使用官方Minecraft运行时目录：C:\Users\用户名\AppData\Roaming\.minecraft\runtime
  return path.join(os.homedir(), 'AppData', 'Roaming', '.minecraft', 'runtime', runtimeName);
}

// 在目录中查找java.exe（自动拼接\bin\，使用java.exe以便捕获错误输出）
function findJavaExecutable(runtimeDir) {
  // 优先使用 bin\java.exe（可以捕获控制台输出用于调试）
  const javaPath = path.join(runtimeDir, 'bin', 'java.exe');
  if (fs.existsSync(javaPath)) {
    try {
      fs.accessSync(javaPath, fs.constants.R_OK);
      return javaPath;
    } catch (e) {
      return null;
    }
  }
  // 兜底：递归查找
  return findJavaExecutableInDir(runtimeDir);
}

// 检查指定版本的Java是否已安装
async function checkJavaInstalled(javaVersion) {
  const runtimeDir = getJavaRuntimeDir(javaVersion);
  if (fs.existsSync(runtimeDir)) {
    const exe = findJavaExecutable(runtimeDir);
    if (exe) {
      return { installed: true, javaPath: exe };
    }
  }
  return { installed: false, javaPath: null };
}

// 获取指定Minecraft版本所需的Java版本
function getJavaVersionForMCVersion(versionId) {
  // Minecraft 1.16及以下: Java 8
  // Minecraft 1.17: Java 16
  // Minecraft 1.18-1.20: Java 17
  // Minecraft 1.21+: Java 21
  const parts = versionId.split('.');
  let major = parseInt(parts[0], 10);
  let minor = parseInt(parts[1], 10);
  // 如果版本号是 "26" 这种没有点的格式
  if (isNaN(major)) major = 1;
  // 处理像 "26" 这种格式（没有 minor）
  if (isNaN(minor)) {
    // 26 及以上使用 Java 25
    if (major >= 26) return '25';
    // 21-25 使用 Java 21
    if (major >= 21) return '21';
    // 否则默认 Java 17
    return '17';
  }
  // 处理 1.x.x 的旧版本
  if (major === 1) {
    if (minor < 17) return '8';
    if (minor === 17) return '16';
    if (minor <= 20) return '17';
    return '21';
  }
  // 处理 2.x.x 和更大的版本（21, 24, 26, ...）
  // 26 及以上使用 Java 25
  if (major >= 26) return '25';
  // 21-25 使用 Java 21
  if (major >= 21) return '21';
  return '17';
}

// 获取版本数据中指定的Java版本
function getJavaVersionFromVersionData(versionData) {
  if (versionData?.javaVersion?.majorVersion) {
    return String(versionData.javaVersion.majorVersion);
  }
  if (versionData?.javaVersion?.component) {
    const m = versionData.javaVersion.component.match(/\d+/);
    if (m) return m[0];
  }
  return getJavaVersionForMCVersion(versionData.id);
}

// 查找本地Java运行时（只查找，不下载）
async function ensureJavaRuntime(version) {
  const javaVersion = typeof version === 'string' ? version : String(version);
  const runtimeDir = getJavaRuntimeDir(javaVersion);
  
  writeLog(`[Java] 查找 Java ${javaVersion} 运行时: ${runtimeDir}`);
  
  if (fs.existsSync(runtimeDir)) {
    const exe = findJavaExecutable(runtimeDir);
    if (exe) {
      writeLog(`[Java] 找到 Java ${javaVersion}: ${exe}`);
      return exe;
    }
  }
  
  writeLog(`[Java] 未找到 Java ${javaVersion} 运行时`);
  return null;
}

// 完整的Java运行时保证（包括下载和安装）
async function ensureJavaRuntimeWithInstall(version, mainWindow) {
  const javaVersion = typeof version === 'string' ? version : String(version);
  
  // 先检查是否已安装
  const installed = await checkJavaInstalled(javaVersion);
  if (installed.installed && installed.javaPath) {
    writeLog(`[Java] Java ${javaVersion} 已安装: ${installed.javaPath}`);
    return { success: true, javaPath: installed.javaPath };
  }
  
  // 如果未安装，触发下载流程
  writeLog(`[Java] Java ${javaVersion} 未安装，开始下载流程`);
  return null;
}

// 用户确认关闭并清理
ipcMain.handle('confirm-close-download', async () => {
  if (isDownloading && currentDownloadVersion) {
    shouldCancelDownload = true;
    
    try {
      // 清理游戏版本下载
      const versionDir = currentDownloadPath || path.join(GAME_DIR, 'versions', currentDownloadVersion);
      const cancelFilePath = path.join(versionDir, 'CancelDownload.txt');
      const cancelContent = `GoodPlanCraftLauncher ${APP_VERSION}\n取消时间：${formatTimestamp()}\n\n正确的关闭\n您在下载完成前正确地关闭了GPCL。`;
      
      if (fs.existsSync(versionDir)) {
        fs.writeFileSync(cancelFilePath, cancelContent, 'utf8');
        const files = fs.readdirSync(versionDir);
        for (const file of files) {
          const filePath = path.join(versionDir, file);
          if (file !== 'CancelDownload.txt') {
            if (fs.statSync(filePath).isDirectory()) {
              fs.rmSync(filePath, { recursive: true, force: true });
            } else {
              fs.unlinkSync(filePath);
            }
          }
        }
      }
      writeLog(`已清理游戏版本下载目录: ${versionDir}`);
    } catch (e) {
      console.error('清理下载失败:', e);
      writeLog(`清理下载失败: ${e.message}`);
    }
    
    isDownloading = false;
    currentDownloadVersion = null;
    currentDownloadType = null;
    currentDownloadPath = null;
  }
  if (mainWindow) {
    mainWindow.destroy();
  }
  return { success: true };
});

// 取消关闭
ipcMain.handle('cancel-close', () => {
  return { success: true };
});

// 取消启动游戏
let launchCancelFlag = false;
ipcMain.handle('cancel-launch', () => {
  launchCancelFlag = true;
  writeLog('[启动] 用户取消启动');
  return { success: true };
});

// 检查更新（使用Node.js网络请求避免CORS问题）
const https = require('https');
ipcMain.handle('check-for-updates', async () => {
  return new Promise((resolve) => {
    const url = 'https://gamets.caellab.com/gpcl/data.json';
    writeLog('[更新] 开始检查更新...');
    
    https.get(url, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });
      
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          writeLog('[更新] 获取到数据: ' + data);
          resolve({ success: true, data: json });
        } catch (e) {
          writeLog('[更新] JSON解析失败: ' + e.message);
          resolve({ success: false, error: 'JSON解析失败: ' + e.message });
        }
      });
    }).on('error', (e) => {
      writeLog('[更新] 请求失败: ' + e.message);
      resolve({ success: false, error: e.message });
    });
  });
});

// 获取当前版本号（从package.json读取）
ipcMain.handle('get-app-version', () => {
  try {
    const packagePath = path.join(__dirname, 'package.json');
    if (fs.existsSync(packagePath)) {
      const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
      return pkg.version || '1.0.0';
    }
  } catch (e) {
    writeLog('[版本] 读取package.json失败: ' + e.message);
  }
  return '1.0.0';
});

// 打开外部链接
ipcMain.handle('open-external', (_, url) => {
  try {
    shell.openExternal(url);
    return { success: true };
  } catch (e) {
    writeLog('[外部链接] 打开失败: ' + e.message);
    return { success: false, error: e.message };
  }
});

// IPC: 卸载Java运行时
ipcMain.handle('uninstall-java', async (_, javaVersion) => {
  try {
    const runtimeDir = getJavaRuntimeDir(javaVersion);
    writeLog(`[Java] 开始卸载 Java ${javaVersion}，目录: ${runtimeDir}`);

    if (!fs.existsSync(runtimeDir)) {
      return { success: false, error: `Java ${javaVersion} 未安装` };
    }

    fs.rmSync(runtimeDir, { recursive: true, force: true });
    writeLog(`[Java] Java ${javaVersion} 已卸载，目录已删除: ${runtimeDir}`);

    if (mainWindow) {
      mainWindow.webContents.send('java-uninstalled', { javaVersion });
    }

    return { success: true };
  } catch (e) {
    writeLog(`[Java] 卸载 Java ${javaVersion} 失败: ${e.message}`);
    return { success: false, error: e.message };
  }
});

// 获取Java镜像源设置
ipcMain.handle('get-java-mirror-settings', () => {
  try {
    const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
    if (fs.existsSync(settingsPath)) {
      return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
    }
    return {
      mirror: 'tsinghua',
      customUrl: ''
    };
  } catch (e) {
    return {
      mirror: 'tsinghua',
      customUrl: ''
    };
  }
});

// 获取Java版本对应的下载URL
ipcMain.handle('get-java-download-url', async (_, javaVersion) => {
  try {
    const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
    let settings = {
      mirror: 'tsinghua',
      customUrl: ''
    };
    
    if (fs.existsSync(settingsPath)) {
      settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
    }
    
    // 根据镜像源生成URL
    const url = getJavaDownloadUrl(javaVersion, settings.mirror, settings.customUrl);
    writeLog(`[Java] 获取下载URL: Java ${javaVersion} - ${url}`);
    return { success: true, url };
  } catch (e) {
    writeLog(`[Java] 获取下载URL失败: ${e.message}`);
    return { success: false, error: e.message };
  }
});

// 根据镜像源生成下载URL
function getJavaDownloadUrl(javaVersion, mirror, customUrl) {
  // 清华大学镜像
  const tsinghuaMirrors = {
    "8": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jre/x64/windows/OpenJDK8U-jre_x64_windows_hotspot_8u492b09.zip",
    "17": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jre/x64/windows/OpenJDK17U-jre_x64_windows_hotspot_17.0.19_10.zip",
    "21": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jre/x64/windows/OpenJDK21U-jre_x64_windows_hotspot_21.0.11_10.zip",
    "25": "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jre/x64/windows/OpenJDK25U-jre_x64_windows_hotspot_25.0.3_9.zip"
  };
  
  // 如果使用清华大学镜像
  if (mirror === 'tsinghua') {
    return tsinghuaMirrors[javaVersion] || tsinghuaMirrors["17"];
  }
  
  // 如果使用自定义镜像
  if (mirror === 'custom' && customUrl) {
    // 替换{v}变量
    let url = customUrl;
    
    // Java版本号（8, 17, 21等）
    url = url.replace(/\{v\}/gi, javaVersion);
    
    // 完整版本号的映射
    const versionMap = {
      "8": "8u492b09",
      "17": "17.0.19_10",
      "21": "21.0.11_10",
      "25": "25.0.3_9"
    };
    url = url.replace(/\{version\}/gi, versionMap[javaVersion] || javaVersion);
    
    return url;
  }
  
  // 默认使用清华大学镜像
  return tsinghuaMirrors[javaVersion] || tsinghuaMirrors["17"];
}

// 保存Java镜像源设置
function saveJavaMirrorSettings(mirror, customUrl) {
  try {
    const settingsDir = path.join(GAME_DIR, 'settings');
    if (!fs.existsSync(settingsDir)) {
      fs.mkdirSync(settingsDir, { recursive: true });
    }
    const settingsPath = path.join(settingsDir, 'java_mirror.json');
    fs.writeFileSync(settingsPath, JSON.stringify({
      mirror,
      customUrl
    }, null, 2));
    writeLog(`[Java] 镜像源设置已保存: ${mirror}`);
    return true;
  } catch (e) {
    writeLog(`[Java] 保存镜像源设置失败: ${e.message}`);
    return false;
  }
}

// 取消下载（只清理文件，不关闭窗口）
ipcMain.handle('cancel-download-only', async () => {
  writeLog('[取消下载] 开始取消下载流程');
  
  // 设置取消标志
  shouldCancelDownload = true;
  
  // 清空下载队列
  const pendingCount = downloadQueue.length;
  downloadQueue = [];
  writeLog(`[取消下载] 已清空 ${pendingCount} 个待处理下载任务`);
  
  // 等待当前活跃的下载完成（最多等待2秒）
  const waitStart = Date.now();
  while (activeDownloads > 0 && (Date.now() - waitStart) < 2000) {
    await new Promise(r => setTimeout(r, 100));
  }
  writeLog(`[取消下载] 活跃下载数: ${activeDownloads}`);
  
  if ((isDownloading && currentDownloadVersion) || isJavaDownloading) {
    try {
      if (currentDownloadType === 'game' && currentDownloadPath) {
        // 清理游戏版本下载
        const versionDir = currentDownloadPath;
        if (fs.existsSync(versionDir)) {
          const files = fs.readdirSync(versionDir);
          for (const file of files) {
            const filePath = path.join(versionDir, file);
            try {
              if (fs.statSync(filePath).isDirectory()) {
                fs.rmSync(filePath, { recursive: true, force: true });
              } else {
                fs.unlinkSync(filePath);
              }
            } catch (e) {
              writeLog(`清理文件失败: ${filePath} - ${e.message}`);
            }
          }
        }
        writeLog(`[取消下载] 已清理游戏版本下载目录: ${versionDir}`);
      } else if (currentDownloadType === 'java' && currentDownloadPath) {
        // 清理Java下载
        const javaFile = currentDownloadPath;
        if (fs.existsSync(javaFile)) {
          try {
            fs.unlinkSync(javaFile);
            writeLog(`[取消下载] 已清理Java下载文件: ${javaFile}`);
          } catch (e) {
            writeLog(`清理Java文件失败: ${e.message}`);
          }
        }
      }
    } catch (e) {
      console.error('清理下载失败:', e);
      writeLog(`清理下载失败: ${e.message}`);
    }
    
    // 重置所有下载状态
    isDownloading = false;
    isJavaDownloading = false;
    currentDownloadVersion = null;
    currentDownloadType = null;
    currentDownloadPath = null;
    activeDownloads = 0;
    totalDownloadFiles = 0;
    completedDownloadFiles = 0;
  }
  
  // 通知渲染进程下载已取消
  for (const w of BrowserWindow.getAllWindows()) {
    w.webContents.send('download-cancelled');
  }
  
  writeLog('[取消下载] 取消下载流程完成');
  return { success: true };
});

// 显示Java版本选择对话框
ipcMain.handle('show-java-install-dialog', async (_, options) => {
  try {
    const { mcVersion, recommendedJava } = options;
    
    // 构建版本选择列表，按推荐程度排序
    const versionPriority = {
      [recommendedJava]: 0,
      "17": 1,
      "21": 2,
      "25": 3,
      "8": 4
    };
    
    const sortedVersions = [...JAVA_VERSIONS_AVAILABLE].sort((a, b) => {
      return (versionPriority[a] || 99) - (versionPriority[b] || 99);
    });
    
    const result = await new Promise((resolve, reject) => {
      javaDownloadResolve = resolve;
      javaDownloadReject = reject;
      
      // 发送消息给渲染进程显示对话框
      if (mainWindow) {
        mainWindow.webContents.send('show-java-install-dialog', {
          mcVersion,
          recommendedJava,
          availableVersions: sortedVersions
        });
      } else {
        reject(new Error('窗口未初始化'));
      }
    });
    
    return result;
  } catch (e) {
    writeLog(`Java安装对话框错误: ${e.message}`);
    return { success: false, cancelled: true };
  }
});

// 用户在对话框中选择Java版本
ipcMain.handle('select-java-version', async (_, data) => {
  const { selectedVersion, cancelled } = data;
  
  if (cancelled) {
    if (javaDownloadResolve) {
      javaDownloadResolve({ success: false, cancelled: true });
      javaDownloadResolve = null;
      javaDownloadReject = null;
    }
    return { success: false, cancelled: true };
  }
  
  if (selectedVersion && javaDownloadResolve) {
    javaDownloadResolve({ success: true, selectedVersion });
    javaDownloadResolve = null;
    javaDownloadReject = null;
    return { success: true, selectedVersion };
  }
  
  return { success: false, cancelled: true };
});

// 下载并安装Java运行时
async function downloadAndInstallJava(javaVersion) {
  const runtimeDir = getJavaRuntimeDir(javaVersion);
  
  writeLog(`[Java] 开始安装 Java ${javaVersion} 运行时`);
  
  try {
    // 检查是否已存在
    if (fs.existsSync(runtimeDir)) {
      const exe = findJavaExecutable(runtimeDir);
      if (exe) {
        writeLog(`[Java] Java ${javaVersion} 已存在，无需下载`);
        return { success: true, javaPath: exe };
      }
    }
    
    // 获取Java运行时的下载URL（支持自定义镜像源）
    const settingsPath = path.join(GAME_DIR, 'settings', 'java_mirror.json');
    let settings = { mirror: 'tsinghua', customUrl: '' };
    
    try {
      if (fs.existsSync(settingsPath)) {
        settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
      }
    } catch (e) {}
    
    const downloadUrl = getJavaDownloadUrl(javaVersion, settings.mirror, settings.customUrl);
    writeLog(`[Java] 使用镜像源: ${settings.mirror}, URL: ${downloadUrl}`);
    
    // 使用Electron webContents下载（更可靠）
    const result = await downloadJavaWithWebContents(javaVersion, downloadUrl);
    return result;
    
  } catch (e) {
    writeLog(`[Java] Java ${javaVersion} 安装失败: ${e.message}`);
    
    // 清理失败的安装
    if (fs.existsSync(runtimeDir)) {
      try {
        fs.rmSync(runtimeDir, { recursive: true, force: true });
      } catch (e2) {}
    }
    
    return { success: false, error: e.message };
  }
}

// 使用Electron webContents下载Java（推荐方法）
async function downloadJavaWithWebContents(javaVersion, downloadUrl) {
  const runtimeName = JAVA_RUNTIME_MAP[javaVersion];
  const runtimeDir = getJavaRuntimeDir(javaVersion);
  const tempDir = path.join(CACHE_DIR, 'java_temp');
  const tempFile = path.join(tempDir, `java-${javaVersion}.zip`);

  if (!fs.existsSync(tempDir)) {
    fs.mkdirSync(tempDir, { recursive: true });
  }

  writeLog(`[Java] 使用Electron下载 Java ${javaVersion}: ${downloadUrl}`);

  // 设置下载路径，以便取消时能正确删除
  currentDownloadVersion = `java-${javaVersion}`;
  currentDownloadType = 'java';
  currentDownloadPath = tempFile;
  shouldCancelDownload = false;
  isJavaDownloading = true;

  return new Promise((resolve, reject) => {
    const win = BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0];
    if (!win) {
      isJavaDownloading = false;
      currentDownloadVersion = null;
      currentDownloadType = null;
      currentDownloadPath = null;
      reject(new Error('没有可用的窗口'));
      return;
    }

    // 使用webContents.downloadURL
    win.webContents.downloadURL(downloadUrl, true);

    // 监听下载开始
    win.webContents.session.once('will-download', (event, item) => {
      // 设置下载路径
      item.setSavePath(tempFile);
      
      const totalBytes = item.getTotalBytes();
      writeLog(`[Java] 开始下载，总大小: ${totalBytes} bytes`);

      // 监听进度
      item.on('updated', (event, state) => {
        if (state === 'progressing') {
          const received = item.getReceivedBytes();
          const percent = totalBytes > 0 ? Math.floor((received / totalBytes) * 100) : 0;
          
          for (const w of BrowserWindow.getAllWindows()) {
            w.webContents.send('download-progress', {
              label: `Java ${javaVersion}`,
              percent: percent,
              bytesDownloaded: received,
              current: completedDownloadFiles,
              total: totalDownloadFiles
            });
          }

          if (shouldCancelDownload) {
            item.cancel();
            writeLog(`[Java] 下载已取消`);
          }
        }
      });

      // 监听完成
      item.on('done', (event, state) => {
        if (state === 'cancelled') {
          isJavaDownloading = false;
          currentDownloadVersion = null;
          currentDownloadType = null;
          currentDownloadPath = null;
          reject(new Error('下载已取消'));
          return;
        }

        if (state === 'interrupted') {
          isJavaDownloading = false;
          currentDownloadVersion = null;
          currentDownloadType = null;
          currentDownloadPath = null;
          reject(new Error('下载中断'));
          return;
        }

        if (state === 'completed') {
          writeLog(`[Java] 下载完成，开始解压...`);
          
          // 验证文件
          const fileStats = fs.statSync(tempFile);
          const fileSize = fileStats.size;
          
          if (fileSize < 1024) {
            reject(new Error(`下载失败：文件太小 (${fileSize} bytes)`));
            return;
          }
          
          // 检查ZIP头
          const header = Buffer.alloc(4);
          const fd = fs.openSync(tempFile, 'r');
          fs.readSync(fd, header, 0, 4, 0);
          fs.closeSync(fd);
          
          if (header.toString('hex') !== '504b0304') {
            reject(new Error('下载失败：文件不是有效的ZIP格式'));
            return;
          }
          
          writeLog(`[Java] 文件验证通过，大小: ${fileSize} bytes`);
          
          // 解压
          const parentDir = path.dirname(runtimeDir);
          if (!fs.existsSync(parentDir)) {
            fs.mkdirSync(parentDir, { recursive: true });
          }
          
          if (fs.existsSync(runtimeDir)) {
            fs.rmSync(runtimeDir, { recursive: true, force: true });
          }
          
          try {
            const AdmZip = require('adm-zip');
            const zip = new AdmZip(tempFile);
            zip.extractAllTo(parentDir, false);
            writeLog(`[Java] 解压完成`);
          } catch (zipError) {
            reject(new Error(`解压失败: ${zipError.message}`));
            return;
          }
          
          // 重命名目录
          const entries = fs.readdirSync(parentDir);
          let extractedDir = null;
          for (const entry of entries) {
            const entryPath = path.join(parentDir, entry);
            if (fs.statSync(entryPath).isDirectory()) {
              if (entry.includes('jdk') || entry.includes('jre')) {
                const binPath = path.join(entryPath, 'bin', 'java.exe');
                if (fs.existsSync(binPath)) {
                  extractedDir = entryPath;
                  writeLog(`[Java] 找到目录: ${entry}`);
                  break;
                }
              }
            }
          }
          
          if (extractedDir && extractedDir !== runtimeDir) {
            if (fs.existsSync(runtimeDir)) {
              fs.rmSync(runtimeDir, { recursive: true, force: true });
            }
            fs.renameSync(extractedDir, runtimeDir);
            writeLog(`[Java] 已重命名为: ${runtimeName}`);
          }
          
          // 清理临时文件
          try {
            fs.unlinkSync(tempFile);
          } catch (e) {}
          
          // 验证安装
          const javaPath = findJavaExecutable(runtimeDir);
          if (!javaPath) {
            reject(new Error('Java运行时安装失败，未找到java.exe'));
            return;
          }
          
          writeLog(`[Java] Java ${javaVersion} 安装成功: ${javaPath}`);
          isJavaDownloading = false;
          currentDownloadVersion = null;
          currentDownloadType = null;
          currentDownloadPath = null;
          resolve({ success: true, javaPath });
        } else {
          isJavaDownloading = false;
          currentDownloadVersion = null;
          currentDownloadType = null;
          currentDownloadPath = null;
          reject(new Error(`下载失败: ${state}`));
        }
      });
    });

    // 超时处理（10分钟）
    setTimeout(() => {
      if (isJavaDownloading) {
        writeLog(`[Java] 下载超时`);
        isJavaDownloading = false;
        currentDownloadVersion = null;
        currentDownloadType = null;
        currentDownloadPath = null;
        reject(new Error('下载超时'));
      }
    }, 600000);
  }).catch((e) => {
    writeLog(`[Java] Java ${javaVersion} 安装失败: ${e.message}`);
    if (fs.existsSync(runtimeDir)) {
      try {
        fs.rmSync(runtimeDir, { recursive: true, force: true });
      } catch (e2) {}
    }
    return { success: false, error: e.message };
  });
}

// 从Minecraft元数据获取Java运行时的下载信息
async function getJavaRuntimeDownloadInfo(javaVersion) {
  try {
    const runtimeKey = JAVA_DOWNLOAD_KEYS[javaVersion];
    if (!runtimeKey) {
      writeLog(`[Java] 不支持的Java版本: ${javaVersion}`);
      return null;
    }
    
    writeLog(`[Java] 正在查找 Java ${javaVersion} (${runtimeKey}) 的下载信息...`);
    
    // 使用清华大学开源软件镜像站（TUNA）的Adoptium镜像
    const runtimeInfo = {
      "8": {
        id: "jre-legacy",
        version: "8",
        url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jre/x64/windows/OpenJDK8U-jre_x64_windows_hotspot_8u492b09.zip"
      },
      "17": {
        id: "java-runtime-gamma",
        version: "17",
        url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jre/x64/windows/OpenJDK17U-jre_x64_windows_hotspot_17.0.19_10.zip"
      },
      "21": {
        id: "java-runtime-delta",
        version: "21",
        url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jre/x64/windows/OpenJDK21U-jre_x64_windows_hotspot_21.0.11_10.zip"
      },
      "25": {
        id: "java-runtime-epsilon",
        version: "25",
        url: "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jre/x64/windows/OpenJDK25U-jre_x64_windows_hotspot_25.0.3_9.zip"
      }
    };
    
    const javaInfo = runtimeInfo[javaVersion];
    if (javaInfo) {
      writeLog(`[Java] ✓ 找到 Java ${javaVersion} 下载信息`);
      writeLog(`[Java] URL: ${javaInfo.url}`);
      return {
        url: javaInfo.url,
        name: javaInfo.id,
        sha1: null,
        size: null
      };
    }
    
    writeLog(`[Java] ✗ 无法找到 Java ${javaVersion} 的下载信息`);
    return null;
    
  } catch (e) {
    writeLog(`[Java] 获取Java运行时下载信息失败: ${e.message}`);
    return null;
  }
}

// 获取MC版本推荐的Java版本
function getRecommendedJavaVersion(mcVersion) {
  // Minecraft 1.16.5及以下: Java 8
  // Minecraft 1.17: Java 16/17
  // Minecraft 1.18-1.20.4: Java 17
  // Minecraft 1.20.5+: Java 21
  // Minecraft 1.21+: Java 21/25
  // Minecraft 26+: Java 21

  const parts = mcVersion.split('.');
  let major = parseInt(parts[0], 10);
  let minor = parseInt(parts[1], 10);
  let patch = parts.length > 2 ? parseInt(parts[2], 10) : 0;

  // 如果版本号是 "26" 这种没有点的格式
  if (isNaN(major)) major = 1;

  // 处理像 "26" 这种格式（只有 major 没有 minor）
  if (isNaN(minor)) {
    // 26 及以上使用 Java 25
    if (major >= 26) return '25';
    // 21-25 使用 Java 21
    if (major >= 21) return '21';
    return '17';
  }

  // 处理 1.x.x 的旧版本
  if (major === 1) {
    if (minor < 17) return '8';
    if (minor === 17) return '17';
    if (minor <= 20) return '17';
    if (minor === 21 && patch <= 4) return '21';
    if (minor === 21 && patch > 4) return '21';
    if (minor >= 22) return '21';
    return '17';
  }

  // 处理 2.x.x 和更大的版本（21, 24, 26, ...）
  // 26 及以上使用 Java 25
  if (major >= 26) return '25';
  // 21-25 使用 Java 21
  if (major >= 21) return '21';
  return '17';
}

function findJavaExecutableInDir(rootDir) {
  const stack = [rootDir];
  while (stack.length > 0) {
    const current = stack.pop();
    if (!fs.existsSync(current)) continue;
    const javaw = path.join(current, 'bin', 'javaw.exe');
    const java = path.join(current, 'bin', 'java.exe');
    if (fs.existsSync(javaw)) return javaw;
    if (fs.existsSync(java)) return java;
    try {
      const entries = fs.readdirSync(current, { withFileTypes: true });
      for (const e of entries) {
        if (e.isDirectory()) stack.push(path.join(current, e.name));
      }
    } catch (e) {}
  }
  return null;
}

async function fetchText(url) {
  return new Promise((resolve, reject) => {
    let finished = false;

    const fallbackToHttps = (reason) => {
      if (finished) return;
      try {
        const https = require('https');
        const req2 = https.get(url, { timeout: 20000 }, res2 => {
          let body = '';
          res2.on('data', d => body += d);
          res2.on('end', () => { finished = true; resolve(body); });
          res2.on('error', err2 => { if (!finished) { finished = true; reject(err2); } });
        });
        req2.on('timeout', () => { req2.destroy(); if (!finished) { finished = true; reject('超时'); } });
        req2.on('error', err2 => { if (!finished) { finished = true; reject(err2); } });
      } catch (e2) {
        if (!finished) { finished = true; reject(e2); }
      }
    };

    try {
      const req = net.request(url);
      const to = setTimeout(() => { try { req.abort(); } catch {} ; fallbackToHttps('timeout'); }, 20000);

      req.on('response', res => {
        clearTimeout(to);
        let body = '';
        res.on('data', d => body += d);
        res.on('end', () => { if (!finished) { finished = true; resolve(body); } });
        res.on('error', err => { if (!finished) fallbackToHttps(err); });
      });

      req.on('error', err => { if (!finished) fallbackToHttps(err); });
      req.end();
    } catch (e) {
      fallbackToHttps(e);
    }
  });
}

let MAX_CONCURRENT = 128;
let downloadQueue = [];
let activeDownloads = 0;
let totalDownloadFiles = 0;
let completedDownloadFiles = 0;
let currentDownloadSpeed = 0;

async function downloadWithPool(url, dest, label) {
  return new Promise((resolve, reject) => {
    downloadQueue.push({ url, dest, label, resolve, reject });
    processDownloadQueue();
  });
}

async function processDownloadQueue() {
  while (downloadQueue.length > 0 && activeDownloads < MAX_CONCURRENT && !shouldCancelDownload) {
    const task = downloadQueue.shift();
    activeDownloads++;
    downloadFileInternal(task.url, task.dest, task.label)
      .then(() => {
        task.resolve();
      })
      .catch(err => {
        task.reject(err);
      })
      .finally(() => {
        activeDownloads--;
        completedDownloadFiles++;
        processDownloadQueue();
      });
  }
  
  // 如果取消下载，拒绝队列中所有剩余任务
  if (shouldCancelDownload) {
    while (downloadQueue.length > 0) {
      const task = downloadQueue.shift();
      task.reject(new Error('下载已取消'));
    }
    // 通知渲染进程下载已取消
    for (const w of BrowserWindow.getAllWindows()) {
      w.webContents.send('download-cancelled');
    }
  }
}

async function downloadFile(url, dest, label) {
  if (fs.existsSync(dest)) {
    const stat = fs.statSync(dest);
    if (stat.size > 1024) {
      for (const w of BrowserWindow.getAllWindows()) {
        w.webContents.send('download-progress', {
          label,
          percent: 100,
          current: completedDownloadFiles,
          total: totalDownloadFiles
        });
      }
      return;
    }
  }
  await downloadWithPool(url, dest, label);
}

async function downloadFileInternal(url, dest, label) {
  return new Promise((resolve, reject) => {
    const dir = path.dirname(dest);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

    // 确保使用二进制模式写入
    const file = fs.createWriteStream(dest, { encoding: null });
    let loaded = 0;
    let total = 0;

    const fallbackToHttps = (reason) => {
      writeLog(`[下载] ${label} HTTP请求失败: ${reason}，尝试HTTPS`);
      try {
        const https = require('https');
        
        const options = {
          headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
          }
        };
        
        const req2 = https.get(url, options, res2 => {
          // 处理重定向
          if (res2.statusCode >= 300 && res2.statusCode < 400 && res2.headers.location) {
            writeLog(`[下载] ${label} 重定向到: ${res2.headers.location}`);
            file.close();
            // 递归处理重定向
            downloadFileInternal(res2.headers.location, dest, label).then(resolve).catch(reject);
            return;
          }
          
          total = parseInt(res2.headers['content-length'] || '0', 10);
          
          // 检查内容类型
          const contentType = res2.headers['content-type'] || '';
          if (!contentType.includes('zip') && !contentType.includes('octet-stream')) {
            writeLog(`[下载] ${label} 警告: 内容类型是 ${contentType}，可能不是ZIP文件`);
          }
          
          writeLog(`[下载] ${label} 开始下载，大小: ${total} bytes`);
          
          res2.on('data', d => {
            if (shouldCancelDownload) {
              req2.destroy();
              file.destroy();
              reject(new Error('下载已取消'));
              return;
            }
            loaded += d.length;
            file.write(d);
            const p = total > 0 ? Math.floor((loaded / total) * 100) : 0;
            for (const w of BrowserWindow.getAllWindows()) {
              w.webContents.send('download-progress', {
                label,
                percent: p,
                bytesDownloaded: loaded,
                current: completedDownloadFiles,
                total: totalDownloadFiles
              });
            }
          });
          res2.on('end', () => {
            file.end();
            writeLog(`[下载] ${label} 下载完成，实际大小: ${loaded} bytes`);
            resolve();
          });
          res2.on('error', err2 => {
            file.destroy();
            reject(err2);
          });
        });
        req2.on('timeout', () => { req2.destroy(); reject('超时'); });
        req2.on('error', err2 => {
          file.destroy();
          reject(err2);
        });
      } catch (e2) {
        file.destroy();
        reject(e2);
      }
    };

    try {
      const req = net.request(url);
      const to = setTimeout(() => { try { req.abort(); } catch {} ; fallbackToHttps('timeout'); }, 60000);
      
      // 设置User-Agent头
      req.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

      req.on('response', res => {
        clearTimeout(to);
        
        // 处理重定向
        if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
          writeLog(`[下载] ${label} 重定向到: ${res.headers.location}`);
          file.close();
          // 递归处理重定向
          downloadFileInternal(res.headers.location, dest, label).then(resolve).catch(reject);
          return;
        }
        
        total = parseInt(res.headers['content-length'] || '0', 10);
        
        // 检查内容类型
        const contentType = res.headers['content-type'] || '';
        if (!contentType.includes('zip') && !contentType.includes('octet-stream')) {
          writeLog(`[下载] ${label} 警告: 内容类型是 ${contentType}，可能不是ZIP文件`);
        }
        
        writeLog(`[下载] ${label} 开始下载，大小: ${total} bytes`);
        
        res.on('data', d => {
          if (shouldCancelDownload) {
            try { req.abort(); } catch {}
            file.destroy();
            reject(new Error('下载已取消'));
            return;
          }
          loaded += d.length;
          file.write(d);
          const p = total > 0 ? Math.floor((loaded / total) * 100) : 0;
          for (const w of BrowserWindow.getAllWindows()) {
            w.webContents.send('download-progress', {
              label,
              percent: p,
              bytesDownloaded: loaded,
              current: completedDownloadFiles,
              total: totalDownloadFiles
            });
          }
        });
        res.on('end', () => {
          file.end();
          writeLog(`[下载] ${label} 下载完成，实际大小: ${loaded} bytes`);
          resolve();
        });
        res.on('error', err => {
          file.destroy();
          fallbackToHttps(err);
        });
      });

      req.on('error', err => {
        file.destroy();
        fallbackToHttps(err);
      });

      req.end();
    } catch (e) {
      file.destroy();
      fallbackToHttps(e);
    }
  });
}

// 带超时的下载包装器，超时后会抛出错误让调用方尝试下一个源
async function downloadFileInternalWithTimeout(url, dest, label, timeoutMs) {
  return new Promise((resolve, reject) => {
    let settled = false;
    const timer = setTimeout(() => {
      if (!settled) {
        settled = true;
        reject(new Error(`连接超时 (${timeoutMs / 1000}秒)`));
      }
    }, timeoutMs);

    downloadFileInternal(url, dest, label)
      .then(result => {
        if (!settled) {
          settled = true;
          clearTimeout(timer);
          resolve(result);
        }
      })
      .catch(err => {
        if (!settled) {
          settled = true;
          clearTimeout(timer);
          reject(err);
        }
      });
  });
}

async function installVersion(versionId, maxConcurrent = 20, downloadPath = null) {
  const targetDir = downloadPath || path.join(GAME_DIR, 'versions', versionId);
  const versionDir = targetDir;
  const cancelFilePath = path.join(versionDir, 'CancelDownload.txt');
  if (fs.existsSync(cancelFilePath)) {
    try { fs.unlinkSync(cancelFilePath); } catch (e) {}
  }

  // 重置取消标志
  shouldCancelDownload = false;
  isDownloading = true;
  currentDownloadVersion = versionId;
  currentDownloadType = 'game';
  currentDownloadPath = targetDir;
  writeLog(`开始下载：${versionId} 到 ${targetDir}`);

  try {
    MAX_CONCURRENT = maxConcurrent;
    downloadQueue = [];
    activeDownloads = 0;
    totalDownloadFiles = 0;
    completedDownloadFiles = 0;

    const versionDir = path.join(GAME_DIR, 'versions', versionId);
    if (!fs.existsSync(versionDir)) fs.mkdirSync(versionDir, { recursive: true });

    const versionJsonPath = path.join(versionDir, versionId + '.json');
    const versionJarPath = path.join(versionDir, versionId + '.jar');

    // 获取版本清单
    const manifest = JSON.parse(await fetchText('https://piston-meta.mojang.com/mc/game/version_manifest_v2.json'));
    const vInfo = manifest.versions.find(v => v.id === versionId);
    if (!vInfo) throw new Error('版本不存在: ' + versionId);

    // 下载版本JSON
    if (!fs.existsSync(versionJsonPath)) {
      const vData = await fetchText(vInfo.url);
      fs.writeFileSync(versionJsonPath, vData);
    }

    const versionData = JSON.parse(fs.readFileSync(versionJsonPath, 'utf8'));

    // 下载客户端JAR
    if (!fs.existsSync(versionJarPath)) {
      await downloadFile(versionData.downloads.client.url, versionJarPath, '客户端');
    }
    
    // 检查是否取消
    if (shouldCancelDownload) {
      writeLog(`下载取消：${versionId}`);
      throw new Error('下载已取消');
    }

    // 下载资源索引
    const assetIndexPath = path.join(GAME_DIR, 'assets', 'indexes', versionData.assetIndex.id + '.json');
    if (!fs.existsSync(assetIndexPath)) {
      await downloadFile(versionData.assetIndex.url, assetIndexPath, '资源索引');
    }
    
    // 检查是否取消
    if (shouldCancelDownload) {
      writeLog(`下载取消：${versionId}`);
      throw new Error('下载已取消');
    }

    // 等待资源索引下载完成并读取
    let assetIndexData;
    for (let i = 0; i < 10; i++) {
      try {
        assetIndexData = JSON.parse(fs.readFileSync(assetIndexPath, 'utf8'));
        break;
      } catch (e) {
        await new Promise(r => setTimeout(r, 500));
      }
    }
    if (!assetIndexData) throw new Error('无法读取资源索引');

    // 收集所有需要下载的文件
    const toDownload = [];

    // 资源文件
    for (const [name, obj] of Object.entries(assetIndexData.objects)) {
      const hash = obj.hash;
      const prefix = hash.substring(0, 2);
      const assetPath = path.join(GAME_DIR, 'assets', 'objects', prefix, hash);
      if (!fs.existsSync(assetPath)) {
        toDownload.push({
          url: `https://resources.download.minecraft.net/${prefix}/${hash}`,
          dest: assetPath,
          label: `资源: ${name}`
        });
      }
    }

    // 库文件
    for (const lib of versionData.libraries) {
      if (lib.downloads?.artifact) {
        const libPath = path.join(GAME_DIR, 'libraries', lib.downloads.artifact.path);
        if (!fs.existsSync(libPath)) {
          toDownload.push({
            url: lib.downloads.artifact.url,
            dest: libPath,
            label: `库: ${lib.name}`
          });
        }
      }
      if (lib.downloads?.classifiers) {
        for (const [cls, info] of Object.entries(lib.downloads.classifiers)) {
          if (cls.includes('natives-windows')) {
            const libPath = path.join(GAME_DIR, 'libraries', info.path);
            if (!fs.existsSync(libPath)) {
              toDownload.push({
                url: info.url,
                dest: libPath,
                label: `原生库: ${lib.name}`
              });
            }
          }
        }
      }
    }

    totalDownloadFiles = toDownload.length;
    completedDownloadFiles = 0;

    // 并发下载所有文件
    const downloadPromises = toDownload.map(item => downloadFile(item.url, item.dest, item.label));
    await Promise.all(downloadPromises);

    // 解压原生库
    const nativesDir = path.join(GAME_DIR, 'versions', versionId, 'natives');
    if (!fs.existsSync(nativesDir)) fs.mkdirSync(nativesDir, { recursive: true });

    for (const lib of versionData.libraries) {
      if (lib.downloads?.classifiers) {
        for (const [cls, info] of Object.entries(lib.downloads.classifiers)) {
          if (cls.includes('natives-windows')) {
            const libPath = path.join(GAME_DIR, 'libraries', info.path);
            if (fs.existsSync(libPath)) {
              const AdmZip = require('adm-zip');
              const zip = new AdmZip(libPath);
              zip.extractAllTo(nativesDir, true);
            }
          }
        }
      }
    }

    if (shouldCancelDownload) {
      writeLog(`下载取消：${versionId}`);
      throw new Error('下载已取消');
    }

    writeLog(`下载完成：${versionId}`);
    
    // 更新版本最后启动时间
    updateVersionLastPlayed(versionId, targetDir);
    
    return { success: true };
  } catch (e) {
    writeLog(`下载失败：${versionId} - ${e.message}`);
    return { success: false, error: e.message };
  } finally {
    isDownloading = false;
    currentDownloadVersion = null;
    currentDownloadType = null;
    currentDownloadPath = null;
    shouldCancelDownload = false;
    downloadQueue = [];
    activeDownloads = 0;
    totalDownloadFiles = 0;
    completedDownloadFiles = 0;
  }
}

// 更新版本的最后启动时间
function updateVersionLastPlayed(versionId, targetDir) {
  try {
    const versionDir = targetDir || path.join(GAME_DIR, 'versions', versionId);
    const gpclDir = path.join(versionDir, 'gpcl');
    const configPath = path.join(gpclDir, 'config.json');
    
    let settings = {};
    
    // 读取现有配置
    if (fs.existsSync(configPath)) {
      try {
        settings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
      } catch (e) {
        settings = {};
      }
    }
    
    // 更新最后启动时间
    settings.lastPlayed = new Date().toISOString();
    
    // 确保目录存在
    if (!fs.existsSync(gpclDir)) {
      fs.mkdirSync(gpclDir, { recursive: true });
    }
    
    // 保存配置
    fs.writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf8');
    writeLog(`[版本] ${versionId} 最后启动时间已更新: ${settings.lastPlayed}`);
    
  } catch (e) {
    writeLog(`[版本] 更新 ${versionId} 最后启动时间失败: ${e.message}`);
  }
}

ipcMain.handle('scan-versions', async (_, gameDir, silent = false) => {
  const targetDir = gameDir ? path.resolve(gameDir) : GAME_DIR;
  const versionsDir = path.join(targetDir, 'versions');
  if (!fs.existsSync(versionsDir)) {
    if (!silent) writeLog('扫描本地版本：未找到versions目录');
    return [];
  }
  const entries = fs.readdirSync(versionsDir, { withFileTypes: true });
  const versions = [];
  const skippedVersions = [];
  
  for (const entry of entries) {
    if (entry.isDirectory()) {
      const versionDir = path.join(versionsDir, entry.name);
      try {
        const files = fs.readdirSync(versionDir);
        
        // 检查是否只有一个 CancelDownload.txt 文件
        if (files.length === 1 && files[0] === 'CancelDownload.txt') {
          skippedVersions.push(entry.name);
          if (!silent) writeLog(`扫描本地版本：跳过空目录 "${entry.name}"（仅包含 CancelDownload.txt）`);
          continue;
        }
        
        // 查找所有 .json 文件
        const jsonFiles = files.filter(f => f.endsWith('.json'));
        
        // 如果没有 .json 文件，跳过
        if (jsonFiles.length === 0) {
          skippedVersions.push(entry.name);
          if (!silent) writeLog(`扫描本地版本：跳过 "${entry.name}"（无 .json 文件）`);
          continue;
        }
        
        // 收集所有版本（包括原版和带模组加载器的版本）
        // 原版: 1.21.1 -> versionId = "1.21.1"
        // Forge: 1.21.1-forge -> versionId = "1.21.1-forge"
        // Fabric: 1.21.1-fabric -> versionId = "1.21.1-fabric"
        
        const baseVersionId = entry.name; // 文件夹名
        const mcVersion = baseVersionId; // MC 版本号
        
        // 总是添加原版
        if (jsonFiles.includes(`${baseVersionId}.json`)) {
          versions.push(baseVersionId);
        }
        
        // 检查是否有模组加载器版本
        for (const jsonFile of jsonFiles) {
          const jsonVersionId = jsonFile.replace('.json', '');
          // 如果不是原版，且不在列表中，添加它
          if (jsonVersionId !== baseVersionId && !versions.includes(jsonVersionId)) {
            versions.push(jsonVersionId);
          }
        }
        
      } catch (e) {
        writeLog(`扫描本地版本：读取目录 "${entry.name}" 失败 - ${e.message}`);
      }
    }
  }
  
  if (skippedVersions.length > 0 && !silent) {
    writeLog(`扫描本地版本：跳过 ${skippedVersions.length} 个无效目录 - [${skippedVersions.join(', ')}]`);
  }
  if (!silent) writeLog(`扫描本地版本：共 ${versions.length} 个版本 - [${versions.join(', ')}]`);
  return versions;
});

ipcMain.handle('get-version-manifest', async () => {
  try {
    if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
    const cacheFile = path.join(CACHE_DIR, 'version_manifest.json');

    let data;
    let fromCache = false;
    if (fs.existsSync(cacheFile)) {
      const stat = fs.statSync(cacheFile);
      const age = Date.now() - stat.mtimeMs;
      if (age < 10 * 60 * 1000) {
        data = fs.readFileSync(cacheFile, 'utf8');
        fromCache = true;
      }
    }

    if (!data) {
      try {
        data = await fetchText('https://piston-meta.mojang.com/mc/game/version_manifest_v2.json');
        fs.writeFileSync(cacheFile, data);
        writeLog('远程拉取版本列表：成功');
      } catch (e) {
        writeLog(`远程拉取版本列表：失败 - ${e.message}`);
        return [];
      }
    } else {
      writeLog(`读取本地缓存版本列表：成功`);
    }

    const parsed = JSON.parse(data);
    // 返回所有类型的版本，包括正式版、快照版、旧测试版
    writeLog(`版本列表：共 ${parsed.versions.length} 个版本`);
    return parsed.versions;
  } catch (e) {
    writeLog(`版本列表解析失败：${e.message}`);
    return [];
  }
});

ipcMain.handle('download-version', (event, versionId, maxConcurrent, downloadPath) => installVersion(versionId, maxConcurrent, downloadPath));

ipcMain.handle('download-with-modloader', (event, versionId, loaderType, loaderVersion, maxConcurrent) => 
  installVersionWithModLoader(versionId, loaderType, loaderVersion, maxConcurrent));

ipcMain.handle('get-version-config', (event, versionId) => readVersionConfig(versionId));
ipcMain.handle('get-version-display-name', (event, versionId) => generateVersionDisplayName(versionId));
ipcMain.handle('get-launch-display-name', (event, versionId) => generateLaunchDisplayName(versionId));

ipcMain.handle('delete-version', async (_, versionId) => {
  try {
    const versionDir = path.join(GAME_DIR, 'versions', versionId);
    
    if (!fs.existsSync(versionDir)) {
      return { success: false, error: '版本不存在' };
    }
    
    writeLog(`[删除版本] 开始删除版本: ${versionId}`);
    
    fs.rmSync(versionDir, { recursive: true, force: true });
    
    writeLog(`[删除版本] 版本 ${versionId} 删除成功`);
    
    return { success: true };
  } catch (e) {
    writeLog(`[删除版本] 删除版本失败: ${e.message}`);
    return { success: false, error: e.message };
  }
});

ipcMain.handle('get-version-settings', async (_, versionId) => {
  try {
    const versionDir = path.join(GAME_DIR, 'versions', versionId);
    const gpclDir = path.join(versionDir, 'gpcl');
    const configPath = path.join(gpclDir, 'config.json');
    
    if (!fs.existsSync(configPath)) {
      return { success: true, settings: null };
    }
    
    const content = fs.readFileSync(configPath, 'utf8');
    const settings = JSON.parse(content);
    
    return { success: true, settings };
  } catch (e) {
    writeLog(`[版本设置] 读取版本 ${versionId} 设置失败: ${e.message}`);
    return { success: false, error: e.message, settings: null };
  }
});

ipcMain.handle('save-version-settings', async (_, versionId, settings) => {
  try {
    const versionDir = path.join(GAME_DIR, 'versions', versionId);
    const gpclDir = path.join(versionDir, 'gpcl');
    
    if (!fs.existsSync(gpclDir)) {
      fs.mkdirSync(gpclDir, { recursive: true });
    }
    
    const configPath = path.join(gpclDir, 'config.json');
    
    // 保存设置
    fs.writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf8');
    
    writeLog(`[版本设置] 版本 ${versionId} 设置已保存`);
    
    return { success: true };
  } catch (e) {
    writeLog(`[版本设置] 保存版本 ${versionId} 设置失败: ${e.message}`);
    return { success: false, error: e.message };
  }
});

ipcMain.handle('get-game-dir', () => {
  return GAME_DIR;
});

ipcMain.handle('get-player-name', () => {
  try {
    const playerFile = path.join(app.getPath('userData'), 'gpcl', 'player.json');
    if (fs.existsSync(playerFile)) {
      const data = JSON.parse(fs.readFileSync(playerFile, 'utf8'));
      return data.name || 'GPCL_Player';
    }
  } catch (e) {
    console.error('读取玩家名失败:', e);
  }
  return 'GPCL_Player';
});

ipcMain.handle('save-player-name', (_, name) => {
  try {
    const gpclDir = path.join(app.getPath('userData'), 'gpcl');
    if (!fs.existsSync(gpclDir)) fs.mkdirSync(gpclDir, { recursive: true });
    const playerFile = path.join(gpclDir, 'player.json');
    fs.writeFileSync(playerFile, JSON.stringify({ name: name || 'GPCL_Player' }));
    return { success: true };
  } catch (e) {
    console.error('保存玩家名失败:', e);
    return { success: false, error: e.message };
  }
});

ipcMain.handle('window-minimize', () => {
  if (mainWindow) mainWindow.minimize();
  return { success: true };
});

ipcMain.handle('window-focus', () => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }
    mainWindow.show();
    mainWindow.focus();
  }
  return { success: true };
});

ipcMain.handle('window-close', () => {
  if (mainWindow) mainWindow.close();
  return { success: true };
});

ipcMain.handle('select-directory', async () => {
  const result = await dialog.showOpenDialog(mainWindow, {
    properties: ['openDirectory']
  });
  if (result.canceled || result.filePaths.length === 0) {
    return null;
  }
  return result.filePaths[0];
});

// 检查指定Minecraft版本所需的Java是否已安装
ipcMain.handle('check-java', async (_, versionOrJavaVersion) => {
  try {
    // 判断是Minecraft版本号还是Java版本号
    const isMcVersion = versionOrJavaVersion && versionOrJavaVersion.match(/^\d+\.\d+/);
    
    if (isMcVersion) {
      // Minecraft版本，检查该版本需要的Java
      const versionId = versionOrJavaVersion;
      const jsonPath = path.join(GAME_DIR, 'versions', versionId, versionId + '.json');
      if (!fs.existsSync(jsonPath)) {
        return { success: false, error: '版本未下载' };
      }
      
      const versionData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
      const javaVersion = getJavaVersionFromVersionData(versionData);
      const result = await checkJavaInstalled(javaVersion);
      
      return {
        success: true,
        installed: result.installed,
        javaVersion: javaVersion,
        javaPath: result.javaPath
      };
    } else {
      // Java版本，直接检查该版本是否安装
      const javaVersion = versionOrJavaVersion;
      const result = await checkJavaInstalled(javaVersion);
      
      return {
        success: true,
        installed: result.installed,
        javaVersion: javaVersion,
        javaPath: result.javaPath
      };
    }
  } catch (e) {
    writeLog(`检查Java失败：${e.message}`);
    return { success: false, error: e.message };
  }
});

// 解析 Minecraft 启动参数模板
function parseMCArguments(template, vars) {
  let result = template;
  for (const [key, value] of Object.entries(vars)) {
    result = result.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value);
  }
  return result;
}

// 智能分割参数（处理引号内的空格）
function splitArgs(str) {
  const args = [];
  let current = '';
  let inQuotes = false;
  for (let i = 0; i < str.length; i++) {
    const c = str[i];
    if (c === '"') {
      inQuotes = !inQuotes;
    } else if (c === ' ' && !inQuotes) {
      if (current.length > 0) {
        args.push(current);
        current = '';
      }
    } else {
      current += c;
    }
  }
  if (current.length > 0) {
    args.push(current);
  }
  return args;
}

// 检查库规则是否允许当前平台
function checkLibraryRules(rules) {
  if (!rules || rules.length === 0) return true;
  let allowed = false;
  for (const rule of rules) {
    let match = true;
    if (rule.os) {
      const platform = os.platform();
      if (rule.os.name === 'windows' && platform !== 'win32') match = false;
      if (rule.os.name === 'linux' && platform !== 'linux') match = false;
      if (rule.os.name === 'osx' && platform !== 'darwin') match = false;
    }
    if (match) {
      allowed = (rule.action === 'allow');
    }
  }
  return allowed;
}

// 构建 Minecraft 启动参数
function buildLaunchArgs(vd, gameDir, versionId, username, windowMode, memoryMB, serverIp) {
  const cp = [];
  // 优先使用 downloads.client 的 jar 路径
  const clientJar = path.join(gameDir, 'versions', versionId, `${versionId}.jar`);
  if (fs.existsSync(clientJar)) {
    cp.push(clientJar);
  }
  // 添加库（带平台规则过滤）
  let missingLibs = 0;
  for (const lib of vd.libraries || []) {
    if (!checkLibraryRules(lib.rules)) continue;
    // 普通 artifact
    if (lib.downloads?.artifact?.path) {
      const libPath = path.join(gameDir, 'libraries', lib.downloads.artifact.path);
      if (fs.existsSync(libPath)) {
        cp.push(libPath);
      } else {
        missingLibs++;
        writeLog(`[启动] 库文件缺失: ${lib.downloads.artifact.path}`);
      }
    }
    // Natives（Windows 平台）
    if (lib.downloads?.classifiers?.['natives-windows']?.path) {
      const nativePath = path.join(gameDir, 'libraries', lib.downloads.classifiers['natives-windows'].path);
      if (fs.existsSync(nativePath)) {
        cp.push(nativePath);
      } else {
        missingLibs++;
        writeLog(`[启动] Native库缺失: ${lib.downloads.classifiers['natives-windows'].path}`);
      }
    }
  }
  if (missingLibs > 0) {
    writeLog(`[启动] 警告: ${missingLibs} 个库文件缺失`);
  }

  const nativesDir = path.join(gameDir, 'versions', versionId, 'natives');
  const assetsDir = path.join(gameDir, 'assets');

  const jvmArgs = [
    `-Djava.library.path=${nativesDir}`,
    '-cp', cp.join(';'),
  ];

  // 添加内存参数
  if (memoryMB) {
    jvmArgs.unshift(`-Xmx${memoryMB}M`);
    writeLog(`[启动] 已设置最大内存: ${memoryMB}M`);
  } else {
    writeLog(`[启动] 未设置内存限制`);
  }

  const vars = {
    auth_player_name: username,
    version_name: versionId,
    game_directory: gameDir,
    assets_root: assetsDir,
    assets_index_name: vd.assetIndex?.id || vd.assets || versionId,
    auth_uuid: '00000000000000000000000000000000',
    auth_access_token: 'offline',
    auth_session: 'offline',
    user_type: 'legacy',
    user_properties: '{}',
    version_type: 'GPCL'
  };

  let gameArgs = [];

  // 新版格式 (1.13+)
  if (vd.arguments?.game) {
    for (const arg of vd.arguments.game) {
      if (typeof arg === 'string') {
        gameArgs.push(parseMCArguments(arg, vars));
      }
      // 忽略条件参数（简化处理）
    }
  }
  // 旧版格式 (1.12.2 及以下)
  else if (vd.minecraftArguments) {
    const parsed = parseMCArguments(vd.minecraftArguments, vars);
    gameArgs = splitArgs(parsed);
  }
  // 兜底
  else {
    gameArgs = [
      '--username', username,
      '--version', versionId,
      '--gameDir', gameDir,
      '--assetsDir', assetsDir,
      '--assetIndex', vd.assetIndex?.id || vd.assets || versionId,
      '--accessToken', 'offline',
      '--userType', 'legacy'
    ];
  }

  // 根据窗口模式添加参数
  if (windowMode === 'fullscreen') {
    gameArgs.push('--fullscreen');
    writeLog(`[启动] 已添加全屏参数: --fullscreen`);
  } else if (windowMode === 'borderless') {
    // 无边框窗口模式：使用全屏但无边框的分辨率
    gameArgs.push('--fullscreen');
    gameArgs.push('--width');
    gameArgs.push('1920');
    gameArgs.push('--height');
    gameArgs.push('1080');
    writeLog(`[启动] 已添加无边框窗口参数: --fullscreen --width 1920 --height 1080`);
  } else if (windowMode === 'minimized') {
    // 最小化模式，不添加特殊参数，但会在游戏启动后自动最小化
    writeLog(`[启动] 已设置最小化模式`);
  }

  // 如果有服务器IP，添加服务器连接参数
  if (serverIp && serverIp.trim()) {
    const ipParts = serverIp.trim().split(':');
    const serverAddress = ipParts[0];
    const serverPort = ipParts[1] || '25565';
    
    gameArgs.push('--server');
    gameArgs.push(serverAddress);
    gameArgs.push('--port');
    gameArgs.push(serverPort);
    
    writeLog(`[启动] 已添加服务器连接参数: --server ${serverAddress} --port ${serverPort}`);
  }

  return { jvmArgs, gameArgs, mainClass: vd.mainClass };
}

ipcMain.handle('launch-minecraft', async (_, opt) => {
  try {
    // 重置取消标志
    launchCancelFlag = false;
    
    const { versionId, username, gameDir: rawGameDir, windowMode } = opt;
    // 使用传入的路径或默认的 GAME_DIR
    const gameDir = rawGameDir ? path.resolve(rawGameDir) : GAME_DIR;
    // 窗口模式：fullscreen, windowed, borderless
    const mode = windowMode || 'windowed';
    writeLog(`启动游戏：玩家 ${username}，版本 ${versionId}，目录: ${gameDir}，窗口模式: ${mode}`);
    
    // 检查是否已取消
    if (launchCancelFlag) {
      writeLog('[启动] 用户已取消启动');
      return { success: false, error: '启动已取消', cancelled: true };
    }
    
    const jsonPath = path.join(gameDir, 'versions', versionId, `${versionId}.json`);
    if (!fs.existsSync(jsonPath)) {
      writeLog(`启动游戏失败：版本 ${versionId} 未下载`);
      return { success: false, error: '请先下载游戏' };
    }

    const vd = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
    const javaVersion = getJavaVersionFromVersionData(vd);
    
    // 检查是否已取消
    if (launchCancelFlag) {
      writeLog('[启动] 用户已取消启动');
      return { success: false, error: '启动已取消', cancelled: true };
    }
    
    // 加载设置
    const settings = loadSettings();
    let memoryMB = null;
    let actualWindowMode = mode;
    let serverIp = null;
    
    // 首先检查是否有版本特定设置
    try {
      const versionConfigPath = path.join(gameDir, 'versions', versionId, 'gpcl', 'config.json');
      if (fs.existsSync(versionConfigPath)) {
        const versionSettings = JSON.parse(fs.readFileSync(versionConfigPath, 'utf8'));
        
        // 处理内存设置
        if (versionSettings.memory && versionSettings.memory !== 'global') {
          const memoryVal = parseFloat(versionSettings.memory);
          if (!isNaN(memoryVal)) {
            if (versionSettings.memory === '0.5') {
              memoryMB = 512; // 0.5GB = 512MB
            } else {
              memoryMB = Math.floor(memoryVal * 1024);
            }
            writeLog(`[启动] 使用版本内存设置: ${memoryMB}M`);
          }
        }
        
        // 处理窗口模式设置
        if (versionSettings.windowMode && versionSettings.windowMode !== 'global') {
          actualWindowMode = versionSettings.windowMode;
          writeLog(`[启动] 使用版本窗口模式: ${actualWindowMode}`);
        }
        
        // 处理启动模式和服务器IP
        if (versionSettings.startupMode === 'join' && versionSettings.serverIp) {
          serverIp = versionSettings.serverIp;
          writeLog(`[启动] 使用版本启动模式: 加入服务器 ${serverIp}`);
        }
      }
    } catch (e) {
      writeLog(`[启动] 读取版本设置失败: ${e.message}`);
    }
    
    // 如果没有版本特定设置，使用全局设置
    if (memoryMB === null && settings.game?.memory) {
      const globalMemory = parseFloat(settings.game.memory);
      if (!isNaN(globalMemory)) {
        memoryMB = Math.floor(globalMemory * 1024);
        writeLog(`[启动] 使用全局内存设置: ${memoryMB}M`);
      }
    }
    
    // 检查是否已取消
    if (launchCancelFlag) {
      writeLog('[启动] 用户已取消启动');
      return { success: false, error: '启动已取消', cancelled: true };
    }
    
    // 检查Java是否已安装
    let java = await ensureJavaRuntime(javaVersion);
    
    // 检查是否已取消（在Java检查后）
    if (launchCancelFlag) {
      writeLog('[启动] 用户已取消启动');
      return { success: false, error: '启动已取消', cancelled: true };
    }
    
    // 如果Java未安装，自动下载
    if (!java) {
      writeLog(`[Java] Java ${javaVersion} 未安装，自动开始下载`);
      
      // 发送通知给渲染进程
      if (mainWindow) {
        mainWindow.webContents.send('java-auto-download-start', {
          javaVersion: javaVersion,
          mcVersion: versionId
        });
      }
      
      // 自动下载并安装Java（期间检查取消标志）
      const installResult = await downloadAndInstallJava(javaVersion);
      
      // 检查是否已取消（在Java下载后）
      if (launchCancelFlag) {
        writeLog('[启动] 用户已取消启动（Java下载完成后）');
        return { success: false, error: '启动已取消', cancelled: true };
      }
      
      if (installResult.success) {
        java = installResult.javaPath;
        writeLog(`[Java] Java ${javaVersion} 安装成功: ${java}`);
        
        // 发送安装完成通知
        if (mainWindow) {
          mainWindow.webContents.send('java-auto-download-complete', {
            javaVersion: javaVersion,
            javaPath: java
          });
        }
      } else {
        writeLog(`[Java] Java ${javaVersion} 安装失败: ${installResult.error}`);
        return {
          success: false,
          error: `Java ${javaVersion} 安装失败: ${installResult.error}`
        };
      }
    }

    // 最终检查是否已取消（在启动游戏前）
    if (launchCancelFlag) {
      writeLog('[启动] 用户已取消启动（即将启动游戏前）');
      return { success: false, error: '启动已取消', cancelled: true };
    }

    const { jvmArgs, gameArgs, mainClass } = buildLaunchArgs(vd, gameDir, versionId, username, actualWindowMode, memoryMB, serverIp);
    const args = [...jvmArgs, mainClass, ...gameArgs];

    writeLog(`[启动] Java: ${java}`);
    writeLog(`[启动] 主类: ${mainClass}`);
    writeLog(`[启动] 窗口模式: ${actualWindowMode}`);
    writeLog(`[启动] 参数数: ${args.length}`);
    writeLog(`[启动] JVM参数: ${JSON.stringify(jvmArgs)}`);
    writeLog(`[启动] 游戏参数: ${JSON.stringify(gameArgs.slice(0, 10))}...`);
    writeLog(`[启动] Classpath条目数: ${jvmArgs[2].split(';').length}`);
    writeLog(`[启动] Classpath总长度: ${jvmArgs[2].length} 字符`);

    let proc;
    for (let i = 0; i < 3; i++) {
      try {
        proc = spawn(java, args, { cwd: gameDir, detached: false, windowsHide: false });
        writeLog(`启动游戏成功：PID ${proc.pid}`);
        break;
      } catch (e) {
        if (e.code === 'EBUSY' && i < 2) {
          await new Promise(r => setTimeout(r, 1500));
          continue;
        }
        writeLog(`启动游戏失败：${e.message}`);
        return { success: false, error: e.message };
      }
    }

    // 检测游戏窗口创建成功
    await detectGameWindow(proc.pid);

    // 捕获输出用于调试
    let stdoutData = '';
    let stderrData = '';
    proc.stdout?.on('data', d => {
      const text = d.toString();
      stdoutData += text;
      mainWindow.webContents.send('game-log', text);
      
      // 检测Minecraft窗口创建成功的标志
      if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
        writeLog(`[启动] 检测到窗口初始化日志`);
        if (mainWindow) {
          mainWindow.webContents.send('game-window-created', { pid: proc.pid });
        }
      }
    });
    proc.stderr?.on('data', d => {
      const text = d.toString();
      stderrData += text;
      mainWindow.webContents.send('game-log', text);
      
      // 检测Minecraft窗口创建成功的标志
      if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
        writeLog(`[启动] 检测到窗口初始化日志`);
        if (mainWindow) {
          mainWindow.webContents.send('game-window-created', { pid: proc.pid });
        }
      }
    });
    proc.on('error', (err) => {
      writeLog(`[启动] 进程错误: ${err.message}`);
    });
    proc.on('exit', (code) => {
      writeLog(`[启动] 进程退出，代码: ${code}`);
      if (code !== 0 && stderrData) {
        writeLog(`[启动] 错误输出: ${stderrData.slice(0, 1000)}`);
      }
    });

    return { success: true, pid: proc.pid };
  } catch (e) {
    writeLog(`启动游戏异常：${e.message}`);
    return { success: false, error: e.message };
  }
});

// 检测游戏窗口创建成功
async function detectGameWindow(pid) {
  writeLog(`[启动] 开始检测游戏窗口，PID: ${pid}`);

  return new Promise((resolve) => {
    let resolved = false;
    const done = () => {
      if (resolved) return;
      resolved = true;
      resolve();
    };

    const cancelCheck = setInterval(() => {
      if (launchCancelFlag) {
        writeLog(`[启动] 窗口检测期间检测到取消标志，立即退出`);
        clearInterval(cancelCheck);
        try { detector.kill(); } catch {}
        done();
      }
    }, 100);

    const timeout = setTimeout(() => {
      writeLog(`[启动] 游戏窗口检测超时（8秒），继续启动流程`);
      clearInterval(cancelCheck);
      try { detector.kill(); } catch {}
      if (mainWindow && !launchCancelFlag) {
        mainWindow.webContents.send('game-window-created', { pid, timeout: true });
      }
      done();
    }, 8000);

    const script = `
      $deadline = (Get-Date).AddSeconds(7)
      while ((Get-Date) -lt $deadline) {
        try {
          $p = Get-Process -Id ${pid} -ErrorAction Stop
          if ($p.MainWindowHandle -ne 0) { Write-Output "FOUND"; exit 0 }
        } catch { Write-Output "GONE"; exit 1 }
        Start-Sleep -Milliseconds 150
      }
      Write-Output "TIMEOUT"; exit 2
    `;
    const detector = spawn('powershell', ['-NoProfile', '-Command', script], { windowsHide: true });
    let output = '';

    detector.stdout.on('data', d => { output += d.toString(); });
    detector.stderr.on('data', d => { output += d.toString(); });

    detector.on('close', (code) => {
      clearInterval(cancelCheck);
      clearTimeout(timeout);

      if (launchCancelFlag) { done(); return; }

      if (output.includes('FOUND')) {
        writeLog(`[启动] 检测到游戏窗口（MainWindowHandle != 0）`);
      } else if (output.includes('GONE')) {
        writeLog(`[启动] 进程已退出`);
      } else {
        writeLog(`[启动] 窗口检测结束，code=${code}，output=${output.trim()}`);
      }

      if (mainWindow) {
        mainWindow.webContents.send('game-window-created', { pid, windowDetected: output.includes('FOUND') });
      }
      done();
    });

    detector.on('error', (err) => {
      clearInterval(cancelCheck);
      clearTimeout(timeout);
      writeLog(`[启动] 窗口检测器启动失败: ${err.message}`);
      if (mainWindow && !launchCancelFlag) {
        mainWindow.webContents.send('game-window-created', { pid, error: true });
      }
      done();
    });
  });
}

// IPC: 下载并安装Java运行时
ipcMain.handle('install-java', async (_, javaVersion) => {
  try {
    writeLog(`[Java] 开始安装 Java ${javaVersion}`);
    
    // 发送下载开始消息
    if (mainWindow) {
      mainWindow.webContents.send('java-download-started', { javaVersion });
    }
    
    const result = await downloadAndInstallJava(javaVersion);
    
    if (result.success) {
      writeLog(`[Java] Java ${javaVersion} 安装成功: ${result.javaPath}`);
      if (mainWindow) {
        mainWindow.webContents.send('java-download-completed', {
          javaVersion,
          javaPath: result.javaPath
        });
      }
      return result;
    } else {
      writeLog(`[Java] Java ${javaVersion} 安装失败: ${result.error}`);
      if (mainWindow) {
        mainWindow.webContents.send('java-download-failed', {
          javaVersion,
          error: result.error
        });
      }
      return result;
    }
  } catch (e) {
    writeLog(`[Java] 安装Java异常: ${e.message}`);
    if (mainWindow) {
      mainWindow.webContents.send('java-download-failed', {
        javaVersion,
        error: e.message
      });
    }
    return { success: false, error: e.message };
  }
});

let currentSettings = null;

ipcMain.handle('get-settings', () => {
  if (!currentSettings) {
    currentSettings = loadSettings();
  }
  return currentSettings;
});

ipcMain.handle('save-settings', (_, settings) => {
  currentSettings = mergeWithDefaults(settings);
  const success = saveSettings(currentSettings);
  return { success, settings: currentSettings };
});

ipcMain.handle('reset-settings', () => {
  currentSettings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
  const success = saveSettings(currentSettings);
  return { success, settings: currentSettings };
});

ipcMain.handle('restart-app', () => {
  writeLog('[重启] 正在重启应用...');
  app.relaunch();
  app.exit();
});

ipcMain.handle('set-developer-mode', (_, enabled) => {
  isDeveloperMode = !!enabled;
  writeLog('[开发者模式] ' + (isDeveloperMode ? '已启用' : '已禁用'));
  return { success: true };
});

let activeAnimWindow = null;

function cleanupAnimTempDir(dir) {
  try {
    if (fs.existsSync(dir)) {
      fs.rmSync(dir, { recursive: true, force: true });
    }
  } catch (e) {}
}

ipcMain.handle('play-startup-animation', async () => {
  if (activeAnimWindow && !activeAnimWindow.isDestroyed()) {
    try { activeAnimWindow.close(); } catch (e) {}
    activeAnimWindow = null;
  }

  writeLog('[启动动画] 开始播放启动动画');

  const iconDir = path.join(__dirname, 'static', 'icon');
  const tempAnimDir = path.join(app.getPath('temp'), 'gpcl-anim-' + Date.now());
  try { fs.mkdirSync(tempAnimDir, { recursive: true }); } catch (e) {}

  var imageNames = ['caellab.png', 'gamets.png', 'gamets.ico', 'GPCL.png'];
  for (var _i = 0; _i < imageNames.length; _i++) {
    var src = path.join(iconDir, imageNames[_i]);
    if (fs.existsSync(src)) {
      try { fs.copyFileSync(src, path.join(tempAnimDir, imageNames[_i])); } catch (e) {}
    }
  }

  var hasCaelab = fs.existsSync(path.join(tempAnimDir, 'caellab.png'));
  var hasGamets = fs.existsSync(path.join(tempAnimDir, 'gamets.png')) || fs.existsSync(path.join(tempAnimDir, 'gamets.ico'));
  var hasGpcl = fs.existsSync(path.join(tempAnimDir, 'GPCL.png'));
  var gametsFile = fs.existsSync(path.join(tempAnimDir, 'gamets.png')) ? 'gamets.png' : 'gamets.ico';

  writeLog('[启动动画] 资源: caellab=' + hasCaelab + ' gamets=' + hasGamets + ' gpcl=' + hasGpcl);

  var ANIM_DURATION = 30;

  var htmlContent = '<!DOCTYPE html>\n<html><head><meta charset="utf-8"><style>\n' +
'*{margin:0;padding:0;box-sizing:border-box}\n' +
'html,body{width:100%;height:100%;overflow:hidden;background:#0a0a0a}\n' +
'canvas{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1}\n' +
'.scene{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;z-index:10;opacity:0}\n' +
'#welcome-txt{text-align:center;font-family:"Microsoft YaHei","Segoe UI",sans-serif}\n' +
'#welcome-txt h1{font-size:56px;font-weight:700;color:#fff;letter-spacing:3px;text-shadow:0 0 25px rgba(91,135,49,0.6),0 0 50px rgba(91,135,49,0.2);margin-bottom:16px}\n' +
'#welcome-txt p{font-size:20px;color:rgba(180,180,180,0.7);letter-spacing:5px}\n' +
'.logo-row{display:flex;flex-direction:row;align-items:center;justify-content:center}\n' +
'#logo-caellab img{max-height:130px;max-width:300px;object-fit:contain;filter:drop-shadow(0 0 18px rgba(91,135,49,0.35))}\n' +
'#logo-both .logo-row{gap:50px}\n' +
'#logo-both img{max-height:110px;max-width:240px;object-fit:contain}\n' +
'#logo-both .caellab-img{filter:drop-shadow(0 0 18px rgba(91,135,49,0.35))}\n' +
'#logo-both .gamets-img{filter:drop-shadow(0 0 14px rgba(255,255,255,0.25))}\n' +
'#logo-gpcl img{max-height:170px;max-width:340px;object-fit:contain;filter:drop-shadow(0 0 25px rgba(91,135,49,0.4))}\n' +
'.logo-text{text-align:center;font-family:"Microsoft YaHei","Segoe UI",sans-serif;margin-top:18px}\n' +
'.logo-text-bottom{position:absolute;bottom:40px;left:0;width:100%;margin-top:0}\n' +
'.logo-title{font-size:20px;color:rgba(200,200,200,0.85);letter-spacing:4px;margin-bottom:6px}\n' +
'.footer-link{font-size:14px;color:rgba(91,135,49,0.7);text-decoration:underline;pointer-events:auto;cursor:pointer}\n' +
'.copyright{font-size:13px;color:rgba(160,160,160,0.6);letter-spacing:1px;margin-bottom:6px}\n' +
'.license-text{font-size:12px;color:rgba(140,140,140,0.5);margin:0}\n' +
'.license-text .footer-link{font-size:12px}\n' +
'.disclaimer{font-size:12px;color:rgba(120,120,120,0.45);max-width:600px;line-height:1.6;margin:40px auto 0;text-align:center}\n' +
'#sweep-line{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:25;opacity:0}\n' +
'#sweep-line .band{position:absolute;top:0;height:100%;width:100px;background:linear-gradient(90deg,transparent,rgba(91,135,49,0.1),rgba(127,255,0,0.12),rgba(91,135,49,0.1),transparent)}\n' +
'#fade{position:fixed;top:0;left:0;width:100%;height:100%;background:#0a0a0a;opacity:0;pointer-events:none;z-index:30;transition:opacity 1.5s ease-in}\n' +
'#fade.show{opacity:1}\n' +
'#flash{position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;pointer-events:none;z-index:28}\n' +
'</style></head><body>\n' +
'<canvas id="c"></canvas>\n' +
'<div class="scene" id="welcome"><div id="welcome-txt"><h1>Minecraft\u6B22\u8FCE\u4F60\uFF01</h1><p>By GPCL And AllMinecraftPlayer</p></div></div>\n' +
'<div class="scene" id="logo-caellab"><div class="logo-row"><img src="caellab.png" alt="CaelLab"></div><div class="logo-text"><div class="logo-title">\u865A\u821F\u5B9E\u9A8C\u5BA4</div><a href="https://www.caellab.com" target="_blank" class="footer-link">caellab.com</a></div></div>\n' +
'<div class="scene" id="logo-both"><div class="logo-row"><img class="caellab-img" src="caellab.png" alt="CaelLab"><img class="gamets-img" src="' + gametsFile + '" alt="GameTS"></div><div class="logo-text logo-text-bottom"><div class="copyright">Copyright \u00A9 2026 Yunyun(\u4E91\u4E91) By \u865A\u821F\u5B9E\u9A8C\u5BA4(CaelLab) / CaelLabGameTS</div><p class="license-text">Licensed under <a href="https://www.caellab.com/license/bysa-code" target="_blank" class="footer-link">CaelLab BY-SA Code License</a>. Open Source on <a href="https://git.caellab.com/yunyun/GoodPlanCraftLauncher" target="_blank" class="footer-link">GitCaelLab</a>.</p></div></div>\n' +
'<div class="scene" id="logo-gpcl"><img src="GPCL.png" alt="GPCL"><div class="logo-text logo-text-bottom"><div class="disclaimer">GoodPlanCraftLauncher \u4E0E Mojang Studios \u53CA Microsoft\u3001Xbox \u65E0\u5B98\u65B9\u5173\u8054\u3002Minecraft \u4E3A Mojang Studios \u5546\u6807\u3002</div></div></div>\n' +
'<div id="sweep-line"><div class="band" id="sweep-band"></div></div>\n' +
'<div id="flash"></div><div id="fade"></div>\n' +
'<script>\n' +
'(function(){\n' +
'var canvas=document.getElementById("c"),ctx=canvas.getContext("2d");\n' +
'var W,H,dpr=window.devicePixelRatio||1;\n' +
'function resize(){W=window.innerWidth;H=window.innerHeight;canvas.width=W*dpr;canvas.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0)}\n' +
'resize();window.addEventListener("resize",resize);\n' +
'var startTime=performance.now();\n' +
'var DUR=' + ANIM_DURATION + ';\n' +
'function rand(a,b){return a+Math.random()*(b-a)}\n' +
'function easeOut(t){return 1-Math.pow(1-t,3)}\n' +
'function easeInOut(t){return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2}\n' +
'function clamp(v,a,b){return v<a?a:v>b?b:v}\n' +
'function lerp(a,b,t){return a+(b-a)*t}\n' +
'var BS=24;\n' +
'var MC_COLORS=["#8B6B3D","#6B6B6B","#5B8731","#3E6B1F","#6B4226","#5A5A5A","#7B5B2D","#4A4A4A"];\n' +
'var terrCols=Math.ceil(W/BS)+1;\n' +
'var terrH=[];\n' +
'for(var i=0;i<terrCols;i++){terrH.push(2+~~rand(0,4))}\n' +
'var parts=[];\n' +
'for(var i=0;i<160;i++){\n' +
'  var a=rand(0,6.283),sp=rand(0.12,0.6);\n' +
'  parts.push({x:rand(0,W),y:rand(0,H),vx:Math.cos(a)*sp,vy:Math.sin(a)*sp,\n' +
'    sz:rand(8,18),c:MC_COLORS[~~rand(0,MC_COLORS.length)],a:0,ta:rand(.08,.25),\n' +
'    rot:rand(0,6.28),rs:rand(-.012,.012)});\n' +
'}\n' +
'var star={active:false,x:0,y:0,sx:0,sy:0,tx:0,ty:0,t:0,dur:1.6,trail:[]};\n' +
'var sparkles=[];\n' +
'function addSparkle(x,y){for(var i=0;i<6;i++){var a=rand(0,6.28),sp=rand(0.5,2);\n' +
'  sparkles.push({x:x,y:y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp,a:1,sz:rand(3,6)})}}\n' +
'var shockwaves=[];\n' +
'function addShock(x,y,c,mr,d){shockwaves.push({x:x,y:y,r:0,mr:mr,c:c,a:.35,d:d,t:0})}\n' +
'var phaseShown={};\n' +
'function showScene(id,o,d){document.title="show:"+id+":"+o+":"+d}\n' +
'function hideScene(id,d){document.title="hide:"+id+":"+d}\n' +
'var waitingForDismiss=false;\n' +
'window.dismissAnimation=function(){\n' +
'  var f=document.getElementById("fade");f.style.transition="opacity 1.2s ease-in";f.classList.add("show");\n' +
'  setTimeout(function(){document.title="ended"},1300);\n' +
'};\n' +
'function drawBlock(x,y,sz,color,alpha){\n' +
'  ctx.save();ctx.globalAlpha=alpha;\n' +
'  ctx.fillStyle=color;ctx.fillRect(x,y,sz,sz);\n' +
'  ctx.fillStyle="rgba(255,255,255,0.09)";ctx.fillRect(x,y,sz,1);ctx.fillRect(x,y,1,sz);\n' +
'  ctx.fillStyle="rgba(0,0,0,0.18)";ctx.fillRect(x,y+sz-1,sz,1);ctx.fillRect(x+sz-1,y,1,sz);\n' +
'  ctx.restore();\n' +
'}\n' +
'function drawTerrain(t){\n' +
'  var a=clamp(t*.3,0,.2);\n' +
'  for(var c=0;c<terrCols;c++){\n' +
'    for(var r=0;r<terrH[c];r++){\n' +
'      var y=H-(r+1)*BS;var x=c*BS;\n' +
'      var col=r===0?"#3E6B1F":r===1?"#5B4B2D":"#3D3D3D";\n' +
'      drawBlock(x,y,BS,col,a);\n' +
'    }\n' +
'  }\n' +
'}\n' +
'function drawGrid(t){\n' +
'  var a=clamp(t*.3,0,.018);\n' +
'  ctx.save();ctx.strokeStyle="rgba(255,255,255,"+a+")";ctx.lineWidth=0.5;\n' +
'  for(var x=0;x<W;x+=BS){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke()}\n' +
'  for(var y=0;y<H;y+=BS){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke()}\n' +
'  ctx.restore();\n' +
'}\n' +
'function frame(now){\n' +
'  var t=(now-startTime)/1000;\n' +
'  ctx.clearRect(0,0,W,H);\n' +
'  ctx.fillStyle="rgba(10,10,10,"+clamp(t*.5,0,1)+")";ctx.fillRect(0,0,W,H);\n' +
'  drawGrid(t);\n' +
'  drawTerrain(t);\n' +
'  if(t>1&&t<8){\n' +
'    var gp=clamp((t-1)*.6,0,.3);var r=100+gp*120;\n' +
'    var g=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,r);\n' +
'    g.addColorStop(0,"rgba(91,135,49,"+(.06*gp)+")");g.addColorStop(1,"rgba(62,107,31,0)");\n' +
'    ctx.fillStyle=g;ctx.fillRect(0,0,W,H);\n' +
'  }\n' +
'  if(t>22&&t<28){\n' +
'    var gp2=clamp((t-22)*.5,0,.25);var r2=120+gp2*130;\n' +
'    var g2=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,r2);\n' +
'    g2.addColorStop(0,"rgba(91,135,49,"+(.06*gp2)+")");g2.addColorStop(1,"rgba(62,107,31,0)");\n' +
'    ctx.fillStyle=g2;ctx.fillRect(0,0,W,H);\n' +
'  }\n' +
'  for(var i=0;i<parts.length;i++){var p=parts[i];\n' +
'    p.a=clamp(p.a+.003,0,p.ta);p.x+=p.vx;p.y+=p.vy;p.rot+=p.rs;\n' +
'    p.vx*=.9995;p.vy*=.9995;\n' +
'    if(p.x<-30)p.x=W+30;if(p.x>W+30)p.x=-30;\n' +
'    if(p.y<-30)p.y=H+30;if(p.y>H+30)p.y=-30;\n' +
'    ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);\n' +
'    drawBlock(-p.sz/2,-p.sz/2,p.sz,p.c,p.a);ctx.restore();\n' +
'  }\n' +
'  if(star.active){\n' +
'    var st=clamp((t-star.t)/star.dur,0,1);var e=easeInOut(st);\n' +
'    star.x=lerp(star.sx,star.tx,e);star.y=lerp(star.sy,star.ty,e);\n' +
'    star.trail.push({x:star.x,y:star.y,a:1});if(star.trail.length>35)star.trail.shift();\n' +
'    for(var j=0;j<star.trail.length;j++){var tr=star.trail[j];tr.a*=.93;\n' +
'      ctx.save();ctx.globalAlpha=tr.a*.5;ctx.fillStyle="#7FFF00";\n' +
'      ctx.fillRect(tr.x-2,tr.y-2,4,4);ctx.restore();}\n' +
'    ctx.save();ctx.fillStyle="#7FFF00";ctx.shadowColor="#7FFF00";ctx.shadowBlur=15;\n' +
'    ctx.fillRect(star.x-5,star.y-5,10,10);\n' +
'    ctx.shadowBlur=30;ctx.fillStyle="rgba(127,255,0,0.12)";\n' +
'    ctx.fillRect(star.x-14,star.y-14,28,28);ctx.restore();\n' +
'    if(Math.random()>.6)addSparkle(star.x+rand(-8,8),star.y+rand(-8,8));\n' +
'    if(st>=1)star.active=false;\n' +
'  }\n' +
'  for(var s=sparkles.length-1;s>=0;s--){var sp=sparkles[s];\n' +
'    sp.x+=sp.vx;sp.y+=sp.vy;sp.a*=.95;sp.vx*=.97;sp.vy*=.97;\n' +
'    if(sp.a<.01){sparkles.splice(s,1);continue}\n' +
'    ctx.save();ctx.globalAlpha=sp.a;ctx.fillStyle="#7FFF00";\n' +
'    ctx.fillRect(sp.x-sp.sz/2,sp.y-sp.sz/2,sp.sz,sp.sz);ctx.restore();\n' +
'  }\n' +
'  for(var s=shockwaves.length-1;s>=0;s--){var sw=shockwaves[s];\n' +
'    sw.t+=1/60;var pct=sw.t/sw.d;if(pct>=1){shockwaves.splice(s,1);continue}\n' +
'    sw.r=sw.mr*easeOut(pct);sw.a=.35*(1-pct);\n' +
'    ctx.save();ctx.strokeStyle=sw.c;ctx.globalAlpha=sw.a;ctx.lineWidth=2+2*(1-pct);\n' +
'    ctx.beginPath();ctx.arc(sw.x,sw.y,sw.r,0,6.28);ctx.stroke();ctx.restore();\n' +
'  }\n' +
'  if(t>1.5&&!phaseShown.w){phaseShown.w=true;showScene("welcome",1,1)}\n' +
'  if(t>7&&!phaseShown.hidew){phaseShown.hidew=true;hideScene("welcome",1)}\n' +
'  if(t>8.5&&!phaseShown.star){\n' +
'    phaseShown.star=true;\n' +
'    star.active=true;star.sx=-50;star.sy=H*.55;star.tx=W/2;star.ty=H/2;star.t=t;star.trail=[];\n' +
'  }\n' +
'  if(t>10.5&&!phaseShown.ca){phaseShown.ca=true;addShock(W/2,H/2,"rgba(91,135,49,0.3)",400,1.2);showScene("logo-caellab",1,1.2)}\n' +
'  if(t>14.5&&!phaseShown.hideca){phaseShown.hideca=true;hideScene("logo-caellab",.8)}\n' +
'  if(t>15.5&&!phaseShown.both){phaseShown.both=true;showScene("logo-both",1,1.2)}\n' +
'  if(t>20.5&&!phaseShown.hideboth){phaseShown.hideboth=true;hideScene("logo-both",1)}\n' +
'  if(t>22&&!phaseShown.gpcl){phaseShown.gpcl=true;addShock(W/2,H/2,"rgba(127,255,0,0.12)",350,1);showScene("logo-gpcl",1,1.5)}\n' +
'  if(t>26&&!phaseShown.sweep){\n' +
'    phaseShown.sweep=true;\n' +
'    var sl=document.getElementById("sweep-line");sl.style.opacity="1";\n' +
'    var band=document.getElementById("sweep-band");var swStart=performance.now();\n' +
'    function sweepFrame(swNow){var swT=(swNow-swStart)/1500;\n' +
'      if(swT>1){sl.style.opacity="0";return}\n' +
'      band.style.left=(-200+(W+400)*easeInOut(swT))+"px";\n' +
'      requestAnimationFrame(sweepFrame)}\n' +
'    requestAnimationFrame(sweepFrame);\n' +
'  }\n' +
'  if(t>27.5&&!phaseShown.launch){phaseShown.launch=true;document.title="launch"}\n' +
'  if(t>28&&!phaseShown.hgpcl){phaseShown.hgpcl=true;hideScene("logo-gpcl",1)}\n' +
'  if(t>29.5&&!phaseShown.fadeblk){phaseShown.fadeblk=true;document.getElementById("fade").classList.add("show")}\n' +
'  if(t>DUR&&!waitingForDismiss){waitingForDismiss=true;document.title="waiting"}\n' +
'  requestAnimationFrame(frame);\n' +
'}\n' +
'document.title="meta:"+DUR;\n' +
'requestAnimationFrame(frame);\n' +
'})();\n' +
'<\/script></body></html>';

  var tempHtmlPath = path.join(tempAnimDir, 'index.html');
  try { fs.writeFileSync(tempHtmlPath, htmlContent, 'utf8'); } catch (e) {
    writeLog('[启动动画] 写入临时文件失败: ' + e.message);
    cleanupAnimTempDir(tempAnimDir);
    return { success: false, error: '写入临时文件失败', duration: 0 };
  }

  return new Promise(function(resolve) {
    var animWindow = new BrowserWindow({
      fullscreen: true,
      frame: false,
      skipTaskbar: true,
      backgroundColor: '#000000',
      webPreferences: {
        nodeIntegration: false,
        contextIsolation: true
      }
    });

    activeAnimWindow = animWindow;

    animWindow.setAlwaysOnTop(true, 'screen-saver');
    animWindow.webContents.setWindowOpenHandler(function(details) {
      shell.openExternal(details.url);
      return { action: 'deny' };
    });
    animWindow.loadFile(tempHtmlPath);

    var resolved = false;

    animWindow.on('page-title-updated', function(e, title) {
      e.preventDefault();

      if (title.startsWith('show:')) {
        var parts = title.split(':');
        var sceneId = parts[1];
        var opacity = parts[2];
        var dur = parts[3];
        animWindow.webContents.executeJavaScript(
          '(function(){var el=document.getElementById("' + sceneId + '");' +
          'if(el){el.style.transition="opacity ' + dur + 's ease-out";' +
          'el.style.opacity="' + opacity + '"}})()'
        );
        return;
      }

      if (title.startsWith('hide:')) {
        var parts = title.split(':');
        var sceneId = parts[1];
        var dur = parts[2];
        animWindow.webContents.executeJavaScript(
          '(function(){var el=document.getElementById("' + sceneId + '");' +
          'if(el){el.style.transition="opacity ' + dur + 's ease-in";' +
          'el.style.opacity="0"}})()'
        );
        return;
      }

      if (title === 'launch' && !resolved) {
        resolved = true;
        writeLog('[启动动画] 动画即将结束，提前启动MC');
        resolve({ success: true, duration: ANIM_DURATION });
        return;
      }

      if (title === 'waiting') {
        writeLog('[启动动画] 进入黑屏等待阶段');
        return;
      }

      if (title === 'ended') {
        writeLog('[启动动画] 动画退场完毕');
        try { animWindow.close(); } catch (e) {}
        return;
      }
    });

    animWindow.on('closed', function() {
      if (activeAnimWindow === animWindow) activeAnimWindow = null;
      cleanupAnimTempDir(tempAnimDir);
      if (!resolved) {
        resolved = true;
        resolve({ success: false, error: '动画窗口已关闭', duration: 0 });
      }
    });

    setTimeout(function() {
      if (!resolved) {
        resolved = true;
        writeLog('[启动动画] 动画加载超时');
        resolve({ success: false, error: '动画加载超时', duration: 0 });
        try { if (!animWindow.isDestroyed()) animWindow.close(); } catch (e) {}
      }
    }, 35000);
  });
});

ipcMain.handle('dismiss-startup-animation', function() {
  if (activeAnimWindow && !activeAnimWindow.isDestroyed()) {
    writeLog('[启动动画] MC窗口就绪，执行退场');
    try {
      activeAnimWindow.webContents.executeJavaScript(
        'if(typeof dismissAnimation==="function")dismissAnimation()'
      );
    } catch (e) {}
  }
});

ipcMain.handle('read-json-file', async (_, subPath, fileName) => {
  try {
    const dir = path.join(BASE_DIR, 'gpcl', subPath);
    const filePath = path.join(dir, fileName);
    if (!fs.existsSync(filePath)) {
      return null;
    }
    const content = fs.readFileSync(filePath, 'utf8');
    return JSON.parse(content);
  } catch (e) {
    writeLog(`读取文件失败: ${e.message}`);
    return null;
  }
});

ipcMain.handle('write-json-file', async (_, subPath, fileName, data) => {
  try {
    const dir = path.join(BASE_DIR, 'gpcl', subPath);
    const filePath = path.join(dir, fileName);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
    return true;
  } catch (e) {
    writeLog(`写入文件失败: ${e.message}`);
    return false;
  }
});

// 获取 Forge 版本列表
ipcMain.handle('get-forge-versions', async (_, mcVersion) => {
  try {
    writeLog(`[Forge] 获取版本列表: ${mcVersion}`);
    const versions = await fetchForgeVersions(mcVersion);
    return { success: true, versions };
  } catch (e) {
    writeLog(`[Forge] 获取版本列表失败: ${e.message}`);
    return { success: false, error: e.message, versions: [] };
  }
});

// 从 Forge Maven 获取版本列表
async function fetchForgeVersions(mcVersion) {
  return new Promise((resolve, reject) => {
    const https = require('https');
    const url = `https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json`;
    
    https.get(url, { timeout: 10000 }, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });
      
      res.on('end', () => {
        try {
          const metadata = JSON.parse(data);
          const versions = [];
          
          // 查找对应 MC 版本的 Forge 版本
          const versionArray = metadata[mcVersion];
          if (versionArray && Array.isArray(versionArray)) {
            versionArray.forEach((fullVersion) => {
              const parts = fullVersion.split('-');
              const forgeVersion = parts.slice(1).join('-');
              versions.push({
                id: fullVersion,
                name: `Forge ${forgeVersion}`,
                version: forgeVersion,
                mcVersion: mcVersion
              });
            });
          }
          
          // 按版本号排序（最新的在前）
          versions.sort((a, b) => compareVersions(b.version, a.version));
          
          // 只返回前 10 个版本
          const topVersions = versions.slice(0, 10);
          
          // 标记推荐版本（第一个）
          if (topVersions.length > 0) {
            topVersions[0].name += ' (推荐)';
          }
          
          writeLog(`[Forge] 找到 ${topVersions.length} 个版本`);
          resolve(topVersions);
        } catch (e) {
          writeLog(`[Forge] 解析错误: ${e.message}`);
          reject(new Error('解析 Forge 元数据失败'));
        }
      });
    }).on('error', (e) => {
      reject(new Error(`请求 Forge 元数据失败: ${e.message}`));
    }).on('timeout', () => {
      reject(new Error('请求 Forge 元数据超时'));
    });
  });
}

// 版本号比较函数
function compareVersions(v1, v2) {
  const parts1 = v1.split(/[.-]/).map(p => parseInt(p, 10) || 0);
  const parts2 = v2.split(/[.-]/).map(p => parseInt(p, 10) || 0);
  
  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    const a = parts1[i] || 0;
    const b = parts2[i] || 0;
    if (a > b) return 1;
    if (a < b) return -1;
  }
  return 0;
}

async function installVersionWithModLoader(versionId, loaderType, loaderVersion, maxConcurrent) {
  try {
    writeLog(`[模组加载器] 开始下载: ${loaderType} ${loaderVersion} for MC ${versionId}`);
    
    // 1. 先下载原版游戏
    writeLog(`[模组加载器] 下载原版 MC ${versionId}`);
    await installVersion(versionId, maxConcurrent);
    
    if (shouldCancelDownload) {
      return { success: false, error: '下载已取消' };
    }
    
    // 2. 根据模组加载器类型进行安装
    let finalVersionId;
    if (loaderType === 'forge') {
      finalVersionId = await installForge(versionId, loaderVersion);
    } else {
      return { success: false, error: `不支持的模组加载器: ${loaderType}` };
    }
    
    writeLog(`[模组加载器] 安装完成: ${finalVersionId}`);
    
    // 更新版本最后启动时间
    updateVersionLastPlayed(finalVersionId);
    
    return { success: true, finalVersionId };
    
  } catch (e) {
    writeLog(`[模组加载器] 安装失败: ${e.message}`);
    return { success: false, error: e.message };
  }
}

// 安装 Forge：直接从 Forge Maven 下载完整的版本 JSON
async function installForge(mcVersion, forgeVersion) {
  writeLog(`[Forge] 安装: MC ${mcVersion}, Forge ${forgeVersion}`);
  
  const versionDir = path.join(GAME_DIR, 'versions', mcVersion);
  if (!fs.existsSync(versionDir)) {
    throw new Error(`原版版本 ${mcVersion} 不存在`);
  }
  
  // 构造完整的 Forge 版本 ID（如 1.20.1-47.2.0）
  const fullForgeVersionId = `${mcVersion}-${forgeVersion}`;
  
  // Forge 在 Maven 上提供了完整的版本 JSON（含 mainClass、libraries、inheritsFrom 等）
  const forgeJsonUrl = `https://maven.minecraftforge.net/net/minecraftforge/forge/${fullForgeVersionId}/forge-${fullForgeVersionId}.json`;
  
  writeLog(`[Forge] 下载版本 JSON: ${forgeJsonUrl}`);
  let forgeJsonData;
  try {
    forgeJsonData = await fetchText(forgeJsonUrl);
  } catch (e) {
    throw new Error(`无法下载 Forge 版本 JSON: ${e.message}`);
  }
  
  let forgeVersionData;
  try {
    forgeVersionData = JSON.parse(forgeJsonData);
  } catch (e) {
    const snippet = forgeJsonData.substring(0, 100);
    writeLog(`[Forge] JSON 解析失败，响应内容: ${snippet}`);
    throw new Error(`Forge 版本 JSON 解析失败，服务器可能返回了错误页面`);
  }
  
  const forgeJsonId = `${mcVersion}-forge`;
  forgeVersionData.id = forgeJsonId;
  
  const forgeJsonPath = path.join(versionDir, `${forgeJsonId}.json`);
  fs.writeFileSync(forgeJsonPath, JSON.stringify(forgeVersionData, null, 2));
  
  const gpclDir = path.join(versionDir, 'gpcl');
  if (!fs.existsSync(gpclDir)) fs.mkdirSync(gpclDir, { recursive: true });
  
  const configPath = path.join(gpclDir, 'config.ini');
  const configContent = `[Version]
Minecraft=${mcVersion}
Forge=${forgeVersion}
`;
  fs.writeFileSync(configPath, configContent, 'utf8');
  
  writeLog(`[Forge] 安装完成: ${forgeJsonId}`);
  return forgeJsonId;
}

// 读取 config.ini 配置文件
function readVersionConfig(versionId) {
  const versionDir = path.join(GAME_DIR, 'versions', versionId);
  const configPath = path.join(versionDir, 'gpcl', 'config.ini');
  
  if (!fs.existsSync(configPath)) {
    return null;
  }
  
  try {
    const content = fs.readFileSync(configPath, 'utf8');
    const config = {
      Minecraft: '',
      Forge: ''
    };
    
    const lines = content.split('\n');
    let inVersionSection = false;
    
    for (const line of lines) {
      const trimmed = line.trim();
      
      if (trimmed === '[Version]') {
        inVersionSection = true;
        continue;
      }
      
      if (!inVersionSection) continue;
      
      const parts = trimmed.split('=');
      if (parts.length === 2) {
        const key = parts[0].trim();
        const value = parts[1].trim();
        if (config.hasOwnProperty(key)) {
          config[key] = value;
        }
      }
    }
    
    return config;
  } catch (e) {
    writeLog(`[Config] 读取配置失败: ${e.message}`);
    return null;
  }
}

// 生成版本显示名称
function generateVersionDisplayName(versionId) {
  const config = readVersionConfig(versionId);
  
  if (!config) {
    return versionId;
  }
  
  const parts = [];
  parts.push(config.Minecraft || versionId);
  
  if (config.Forge) {
    parts.push(`Forge${config.Forge}`);
  }
  
  return parts.join(' ');
}

// 生成启动页显示名称
function generateLaunchDisplayName(versionId) {
  const config = readVersionConfig(versionId);
  
  if (!config) {
    return versionId;
  }
  
  const parts = [];
  parts.push(config.Minecraft || versionId);
  
  if (config.Forge) {
    parts.push(`Forge${config.Forge}`);
  }
  
  return parts.join(' ');
}


