importPackage(java.awt);
importPackage(java.awt.geom);
include("font_util.js");
include("js_util.js");

// ========== 常量定义 ==========
const TOP_SCREEN_HEIGHT = 106;
const ROUTE_BAR_WIDTH = 10;
const ARROW_WIDTH = 12;

// ========== 字体加载 ==========
const ROBOTO_REGULAR = Resources.readFont(Resources.idr("fonts/roboto/roboto-regular.ttf"));
const ROBOTO_BOLD = Resources.readFont(Resources.idr("fonts/roboto/roboto-bold.ttf"));
const SOURCE_HAN_SANS_CN_REGULAR = Resources.readFont(Resources.idr("fonts/source-han-sans-cn/source-han-sans-cn-regular.otf"));
const SOURCE_HAN_SANS_CN_BOLD = Resources.readFont(Resources.idr("fonts/source-han-sans-cn/source-han-sans-cn-bold.otf"));

// ========== 图片资源加载 ==========
const BGIMAGE = Resources.readBufferedImage(Resources.idRelative("textures/bg.png"));
const DOOROPENIMAGE = Resources.readBufferedImage(Resources.idRelative("textures/dropn.png"));
const DOORCLOSEIMAGE = Resources.readBufferedImage(Resources.idRelative("textures/drcls.png"));

/**
 * 主绘制函数：根据路线信息和列车状态绘制LCD屏幕
 * @param {number} width - 屏幕宽度
 * @param {number} height - 屏幕高度
 * @param {Graphics2D} g - 绘图上下文
 * @param {object} routeInfo - 路线信息对象
 * @param {number} nextStationIndex - 下一站索引
 * @param {string} trainStatus - 列车状态
 * @param {boolean} rightDoor - 右侧车门标志
 * @param {string} doorOpeningSide - 开门侧
 * @param {boolean} isReversed - 是否反向
 * @param {object} train - 列车对象
 * @param {object} state - 动画状态对象
 */
function drawScreen(width, height, g, routeInfo, doorOpenInfo, nextStationIndex, trainStatus, rightDoor, doorOpeningSide, isReversed, train, state) {
    // 无路线信息时显示蓝屏"No Signal"
    if (!routeInfo) {
        drawBlueScreen(g, width, height);
        return;
    }

    const bgImage = BGIMAGE;
    const doorOpenImage = DOOROPENIMAGE;
    const doorCloseImage = DOORCLOSEIMAGE;

    // 白色背景
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, width, height);

    // 绘制背景图
    if (bgImage) {
        g.drawImage(bgImage, 0, 0, width, height, null);
    }

    // ========== 线路名称解析与绘制 ==========
    // 从routeName中提取花括号内的线路编号
    let routeNumber = TextUtil.getExtraParts(routeInfo.routeName).replaceAll(".*\\{([^}]*)}.*", "$1");
    let routeName = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(routeInfo.routeName)) ? getMatching(routeInfo.routeName, true) : routeNumber;
    let routeNameEn = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(routeInfo.routeName)) ? getMatching(routeInfo.routeName, false) : "Line " + routeNumber;

    // 用正则拆分线路名：前缀（字母/数字） + 后缀（中文等）
    let match = routeName.match(/^([A-Za-z0-9]+)(.+)$/);
    let font;
    let fm;

    let currentX = 100;

    // 计算时间文字的垂直中心，用于线路框Y坐标对齐（时间字体40，基线Y=60）
    let timeFont = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(40.0);
    let timeFm = g.getFontMetrics(timeFont);
    let timeCenterY = 60 - timeFm.getAscent() / 2 + timeFm.getDescent() / 2;

    if (match && match[1] && match[2]) {
        let prefix = match[1];
        let suffix = match[2];

        // 先检查前缀是否为纯数字（如"9号线"的"9"），再检查后缀是否以数字开头
        let isPrefixNumeric = /^\d+$/.test(prefix);
        let cjkMatch = !isPrefixNumeric ? suffix.match(/^([0-9]+)(.+)$/) : null;

        if (isPrefixNumeric || (cjkMatch && cjkMatch[1] && cjkMatch[2])) {
            // ========== CJK数字分支：大号数字 + 中文线路名 + 英文线路名 ==========
            let cjkNumber = isPrefixNumeric ? prefix : cjkMatch[1];
            let cjkText = isPrefixNumeric ? suffix : cjkMatch[2];

            // 计算中文文本宽度（字体24）
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
            fm = g.getFontMetrics(font);
            let cjkAscent = fm.getAscent();
            let cjkTextWidth = fm.stringWidth(cjkText);

            // 计算英文文本宽度和descent（字体14）
            font = ROBOTO_BOLD.deriveFont(14.0);
            fm = g.getFontMetrics(font);
            let enAscent = fm.getAscent();
            let enDescent = fm.getDescent();
            let enWidth = fm.stringWidth(routeNameEn);

            // 取中英文中较宽者作为文本区宽度
            let textWidth = Math.max(cjkTextWidth, enWidth);

            // 中文与英文之间的行距
            let lineGap = 20;

            // 文本块总高度：中文顶部 → 英文底部
            let textBlockHeight = cjkAscent + lineGap + enDescent;

            // 计算数字字体大小，使其略大于文本块高度以覆盖两行
            let targetNumberHeight = textBlockHeight * 1.3;
            let approxFontSize = targetNumberHeight * 1.5;
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(approxFontSize);
            fm = g.getFontMetrics(font);
            let numberHeight0 = fm.getAscent() + fm.getDescent();
            let numberFontSize = approxFontSize * targetNumberHeight / numberHeight0;
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(numberFontSize);
            fm = g.getFontMetrics(font);
            let numberAscent = fm.getAscent();
            let numberDescent = fm.getDescent();
            let numberHeight = numberAscent + numberDescent +3;
            let numberWidth = fm.stringWidth(cjkNumber);

            // 框内边距
            let padX = 6;
            let padY = 4;
            let gapX = 3; // 数字与文本间距

            // 框体尺寸：高度基于文本块
            let totalWidth = numberWidth + textWidth + padX * 2 + gapX;
            let boxHeight = textBlockHeight + padY * 2;

            // 框Y坐标与时间文字垂直居中对齐
            let boxY = Math.max(0, Math.round(timeCenterY - boxHeight / 2));

            // 绘制圆角矩形线路框
            g.setColor(routeInfo.routeColor);
            g.fillRoundRect(currentX, boxY, totalWidth, boxHeight, 5, 5);

            // 左侧：大号CJK数字（baseline对齐，使数字顶部 = 中文顶部）
            g.setColor(Color.WHITE);
            g.setFont(font);
            g.drawString(cjkNumber, currentX + padX, boxY + padY + numberAscent - 7.5);

            // 右侧：中文在上
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
            g.setFont(font);
            g.drawString(cjkText, currentX + padX + numberWidth + gapX, boxY + padY + cjkAscent);

            // 右侧：英文在下
            font = ROBOTO_BOLD.deriveFont(14.0);
            g.setFont(font);
            g.drawString(routeNameEn, currentX + padX + numberWidth + gapX, boxY + padY + cjkAscent + lineGap);

            currentX += totalWidth + 6;
        } else {
            // ========== CJK文本分支：前缀（字母/数字） + 后缀（中文） + 英文 ==========
            // 计算中文后缀宽度
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
            fm = g.getFontMetrics(font);
            let cjkAscent = fm.getAscent();
            let cjkWidth = fm.stringWidth(suffix);

            // 计算英文宽度
            font = ROBOTO_BOLD.deriveFont(14.0);
            fm = g.getFontMetrics(font);
            let enWidth = fm.stringWidth(routeNameEn);
            let textWidth = Math.max(cjkWidth, enWidth);

            // 计算前缀宽度
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(28.0);
            fm = g.getFontMetrics(font);
            let prefixAscent = fm.getAscent();
            let prefixDescent = fm.getDescent();
            let prefixHeight = prefixAscent + prefixDescent;
            let prefixWidth = fm.stringWidth(prefix);

            let padX = 6;
            let padY = 4;
            let gapX = 3;

            let totalWidth = prefixWidth + textWidth + padX * 2 + gapX;
            let boxHeight = prefixHeight + padY * 2;
            let boxY = Math.max(0, Math.round(timeCenterY - boxHeight / 2));

            g.setColor(routeInfo.routeColor);
            g.fillRoundRect(currentX, boxY, totalWidth, boxHeight, 5, 5);

            // 左侧：前缀
            g.setColor(Color.WHITE);
            g.setFont(font);
            g.drawString(prefix, currentX + padX, boxY + padY + prefixAscent);

            // 右侧：中文在上
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
            g.setFont(font);
            g.drawString(suffix, currentX + padX + prefixWidth + gapX, boxY + padY + cjkAscent);

            // 右侧：英文在下
            font = ROBOTO_BOLD.deriveFont(14.0);
            g.setFont(font);
            g.drawString(routeNameEn, currentX + padX + prefixWidth + gapX, boxY + padY + cjkAscent + 24);

            currentX += totalWidth + 6;
        }
    } else {
        // ========== 纯文字分支：无前缀后缀拆分，直接显示线路名 ==========
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
        fm = g.getFontMetrics(font);
        let cjkAscent = fm.getAscent();
        let cjkDescent = fm.getDescent();
        let cjkHeight = cjkAscent + cjkDescent;
        let cjkWidth = fm.stringWidth(routeName);

        font = ROBOTO_BOLD.deriveFont(14.0);
        fm = g.getFontMetrics(font);
        let enWidth = fm.stringWidth(routeNameEn);

        let padX = 6;
        let padY = 4;

        let totalWidth = Math.max(cjkWidth, enWidth) + padX * 2;
        let boxHeight = cjkHeight + padY * 2;
        let boxY = Math.max(0, Math.round(timeCenterY - boxHeight / 2));

        g.setColor(routeInfo.routeColor);
        g.fillRoundRect(currentX, boxY, totalWidth, boxHeight, 5, 5);

        // 中文在上
        g.setColor(Color.WHITE);
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
        g.setFont(font);
        g.drawString(routeName, currentX + padX, boxY + padY + cjkAscent);

        // 英文在下
        font = ROBOTO_BOLD.deriveFont(14.0);
        g.setFont(font);
        g.drawString(routeNameEn, currentX + padX, boxY + padY + cjkAscent + 24);

        currentX += totalWidth + 6;
    }

    // ========== 时间与日期绘制 ==========
    // 生成时间字符串：年月日 星期 时:分
    let currentTime = new Date();
    let weekDays = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
    let timeString = currentTime.getFullYear() + "-" + String(currentTime.getMonth() + 1).padStart(2, "0") + "-" + String(currentTime.getDate()).padStart(2, "0") + " " + weekDays[currentTime.getDay()] + " " + String(currentTime.getHours()).padStart(2, "0") + ":" + String(currentTime.getMinutes()).padStart(2, "0");

    // 绘制时间（40号字体，白色）
    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(40.0);
    fm = g.getFontMetrics(font);
    let timeAscent = fm.getAscent();
    let timeDescent = fm.getDescent();
    let timeBaseline = 55;
    //let timeCenterY = timeBaseline - timeAscent + (timeAscent + timeDescent) / 2;
    g.setFont(font);
    g.setColor(Color.WHITE);
    g.drawString(timeString, currentX, timeBaseline);

    // ========== 版权信息（蓝色区域下方左侧，灰色小字） ==========
    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(11.0);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString("Original Code by Jeffreyg1228 & Redesigned by Xiaotang27.(C)2026 TRTIMC      代码经过AI工具生成。", 10, TOP_SCREEN_HEIGHT);

    // ========== 下一站/当前站/终点站信息解析 ==========
    let nextStationName = "";
    let terminalStationName = "";
    let nextStationCjk = "";
    let nextStationNonCjk = "";
    let terminalStationCjk = "";
    let terminalStationNonCjk = "";

    // 根据列车状态确定显示的车站索引
    let displayStationIndex = trainStatus == STATUS_ARRIVED ? nextStationIndex : nextStationIndex;
    let nextStationIndex2 = trainStatus == STATUS_ARRIVED ? nextStationIndex + 1 : nextStationIndex;

    // 判断是否正在返回车厂
    let depotName = CONFIG.showDepotName ? routeInfo.depotName : DEFAULT_DEPOT_NAME;
    let returningToDepot = nextStationIndex2 >= routeInfo.stationInfoList.length && routeInfo.nextRouteInfo == null || trainStatus == STATUS_RETURNING_TO_DEPOT;

    if (returningToDepot) {
        // 返回车厂状态
        nextStationName = getMatching(RETURNING_TO_DEPOT_TIP, true);
        nextStationCjk = getMatching(RETURNING_TO_DEPOT_TIP, true);
        nextStationNonCjk = getMatching(RETURNING_TO_DEPOT_TIP, false);
        terminalStationName = depotName;
        terminalStationCjk = getMatching(depotName, true);
        terminalStationNonCjk = getMatching(depotName, false);
    } else if (displayStationIndex >= routeInfo.stationInfoList.length && routeInfo.nextRouteInfo != null) {
        // 已过当前线路终点，切换到下一条线路的第一站
        nextStationName = getMatching(routeInfo.nextRouteInfo.firstStation.stationName, true);
        nextStationCjk = getMatching(routeInfo.nextRouteInfo.firstStation.stationName, true);
        nextStationNonCjk = getMatching(routeInfo.nextRouteInfo.firstStation.stationName, false);
        terminalStationName = getMatching(routeInfo.nextRouteInfo.destination, true);
        terminalStationCjk = getMatching(routeInfo.nextRouteInfo.destination, true);
        terminalStationNonCjk = getMatching(routeInfo.nextRouteInfo.destination, false);
    } else {
        // 正常线路内行驶
        nextStationName = getMatching(routeInfo.stationInfoList[displayStationIndex].stationName, true);
        nextStationCjk = getMatching(routeInfo.stationInfoList[displayStationIndex].stationName, true);
        nextStationNonCjk = getMatching(routeInfo.stationInfoList[displayStationIndex].stationName, false);
        if (routeInfo.circularState == "CLOCKWISE") {
            terminalStationName = "内环";
            terminalStationCjk = "内环";
            terminalStationNonCjk = "Inner Loop";
        } else if (routeInfo.circularState == "ANTICLOCKWISE") {
            terminalStationName = "外环";
            terminalStationCjk = "外环";
            terminalStationNonCjk = "Outer Loop";
        } else {
            terminalStationName = getMatching(routeInfo.destination, true);
            terminalStationCjk = getMatching(routeInfo.destination, true);
            terminalStationNonCjk = getMatching(routeInfo.destination, false);
        }
    }

    // ========== 到站/行驶中屏幕：下一站/当前站/终点站绘制 ==========
    // 计算标签+站名组的高度中心，使其与时间中心对齐
    // 标签字体：中文18pt ascent≈16, 英文13pt descent≈4, 行距16 → 块高约36
    let labelCnSize = 18;
    let labelEnSize = 13;
    let labelLineGap = 16;

    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
    fm = g.getFontMetrics(font);
    let labelCnAscent = fm.getAscent();

    font = ROBOTO_REGULAR.deriveFont(labelEnSize);
    fm = g.getFontMetrics(font);
    let labelEnDescent = fm.getDescent();

    let labelBlockHeight = labelCnAscent + labelLineGap + labelEnDescent;
    let labelCnBaseline = timeCenterY - labelBlockHeight / 2 + labelCnAscent;
    let labelEnBaseline = labelCnBaseline + labelLineGap;

    // 站名字体：中文28pt, 英文18pt
    let nameCnSize = 28;
    let nameEnSize = 18;
    let nameLineGap = 22;

    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
    fm = g.getFontMetrics(font);
    let nameCnAscent = fm.getAscent();

    font = ROBOTO_BOLD.deriveFont(nameEnSize);
    fm = g.getFontMetrics(font);
    let nameEnDescent = fm.getDescent();

    let nameBlockHeight = nameCnAscent + nameLineGap + nameEnDescent;
    let nameCnBaseline = timeCenterY - nameBlockHeight / 2 + nameCnAscent;
    let nameEnBaseline = nameCnBaseline + nameLineGap;

    if (trainStatus == STATUS_ARRIVED) {
        // 当前站：标签右移 + 站名与标签间距加大
        let nextCenterX = width / 2 + 323;
        let terminalCenterX = width * 3 / 4 + 263;

        // --- 计算"当前站"标签组的总宽度，使标签和站名整体居中 ---
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        fm = g.getFontMetrics(font);
        let arrivedLabel = "当前站";
        let arrivedCnWidth = fm.stringWidth(arrivedLabel);
        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        fm = g.getFontMetrics(font);
        let arrivedEnWidth = fm.stringWidth("This Station");
        let arrivedLabelWidth = Math.max(arrivedCnWidth, arrivedEnWidth);

        // 站名尺寸
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        fm = g.getFontMetrics(font);
        let nameCnW = fm.stringWidth(nextStationCjk);
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        fm = g.getFontMetrics(font);
        let nameEnW = fm.stringWidth(nextStationNonCjk);
        let nameMaxW = Math.max(nameCnW, nameEnW);

        // 标签与站名间距79px
        let labelNameGap = 80;
        let groupTotalWidth = arrivedLabelWidth + labelNameGap + nameMaxW;
        let groupStartX = nextCenterX - groupTotalWidth / 2;

        // 绘制"当前站"标签（中文 + 英文，各自独立居中）
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(arrivedLabel, groupStartX + (arrivedLabelWidth - arrivedCnWidth) / 2, labelCnBaseline);

        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        g.setFont(font);
        g.drawString("This Station", groupStartX + (arrivedLabelWidth - arrivedEnWidth) / 2, labelEnBaseline);

        // 站名起始X = 标签右边界 + 120px间距
        let nameStartX = groupStartX + arrivedLabelWidth + labelNameGap;

        // 绘制当前站CJK站名
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        g.setFont(font);
        g.setColor(routeInfo.routeColor);
        g.drawString(nextStationCjk, nameStartX + (nameMaxW - nameCnW) / 2, nameCnBaseline);

        // 绘制当前站英文站名
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        g.setFont(font);
        g.drawString(nextStationNonCjk, nameStartX + (nameMaxW - nameEnW) / 2, nameEnBaseline);

        // --- 计算"终点站"标签组的总宽度 ---
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        fm = g.getFontMetrics(font);
        let terminalLabel = "终点站";
        let termCnWidth = fm.stringWidth(terminalLabel);
        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        fm = g.getFontMetrics(font);
        let termEnWidth = fm.stringWidth("Terminal Station");
        let termLabelWidth = Math.max(termCnWidth, termEnWidth);

        // 终点站名尺寸
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        fm = g.getFontMetrics(font);
        let termNameCnW = fm.stringWidth(terminalStationCjk);
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        fm = g.getFontMetrics(font);
        let termNameEnW = fm.stringWidth(terminalStationNonCjk);
        let termNameMaxW = Math.max(termNameCnW, termNameEnW);

        // 整体居中
        let termGroupTotalWidth = termLabelWidth + labelNameGap + termNameMaxW;
        let termGroupStartX = terminalCenterX - termGroupTotalWidth / 2;

        // 绘制"终点站"标签
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(terminalLabel, termGroupStartX + (termLabelWidth - termCnWidth) / 2, labelCnBaseline);

        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        g.setFont(font);
        g.drawString("Terminal Station", termGroupStartX + (termLabelWidth - termEnWidth) / 2, labelEnBaseline);

        let termNameStartX = termGroupStartX + termLabelWidth + labelNameGap;

        // 绘制终点站CJK站名
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        g.setFont(font);
        g.setColor(routeInfo.routeColor);
        g.drawString(terminalStationCjk, termNameStartX + (termNameMaxW - termNameCnW) / 2, nameCnBaseline);

        // 绘制终点站英文站名
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        g.setFont(font);
        g.drawString(terminalStationNonCjk, termNameStartX + (termNameMaxW - termNameEnW) / 2, nameEnBaseline);
    } else {
        // 行驶中状态：显示"下一站"和"终点站"
        let nextCenterX = width / 2 + 323;
        let terminalCenterX = width * 3 / 4 + 263;

        let labelNameGap = 80;
        // --- 计算"下一站"标签组的总宽度 ---
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        fm = g.getFontMetrics(font);
        let nextLabel = "下一站";
        let nextCnWidth = fm.stringWidth(nextLabel);
        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        fm = g.getFontMetrics(font);
        let nextEnWidth2 = fm.stringWidth("Next Station");
        let nextLabelWidth2 = Math.max(nextCnWidth, nextEnWidth2);

        // 下一站站名尺寸
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        fm = g.getFontMetrics(font);
        let nameCnW2 = fm.stringWidth(nextStationCjk);
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        fm = g.getFontMetrics(font);
        let nameEnW2 = fm.stringWidth(nextStationNonCjk);
        let nameMaxW2 = Math.max(nameCnW2, nameEnW2);

        // 整体居中
        let groupTotalWidth2 = nextLabelWidth2 + labelNameGap + nameMaxW2;
        let groupStartX2 = nextCenterX - groupTotalWidth2 / 2;

        // 绘制"下一站"标签
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(nextLabel, groupStartX2 + (nextLabelWidth2 - nextCnWidth) / 2, labelCnBaseline);

        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        g.setFont(font);
        g.drawString("Next Station", groupStartX2 + (nextLabelWidth2 - nextEnWidth2) / 2, labelEnBaseline);

        let nameStartX2 = groupStartX2 + nextLabelWidth2 + labelNameGap;

        // 绘制下一站CJK站名
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        g.setFont(font);
        g.setColor(routeInfo.routeColor);
        g.drawString(nextStationCjk, nameStartX2 + (nameMaxW2 - nameCnW2) / 2, nameCnBaseline);

        // 绘制下一站英文站名
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        g.setFont(font);
        g.drawString(nextStationNonCjk, nameStartX2 + (nameMaxW2 - nameEnW2) / 2, nameEnBaseline);

        // --- 计算"终点站"标签组的总宽度 ---
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        fm = g.getFontMetrics(font);
        let terminalLabel2 = "终点站";
        let termCnWidth2 = fm.stringWidth(terminalLabel2);
        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        fm = g.getFontMetrics(font);
        let termEnWidth2 = fm.stringWidth("Terminal Station");
        let termLabelWidth2 = Math.max(termCnWidth2, termEnWidth2);

        // 终点站名尺寸
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        fm = g.getFontMetrics(font);
        let termNameCnW2 = fm.stringWidth(terminalStationCjk);
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        fm = g.getFontMetrics(font);
        let termNameEnW2 = fm.stringWidth(terminalStationNonCjk);
        let termNameMaxW2 = Math.max(termNameCnW2, termNameEnW2);

        // 整体居中
        let termGroupTotalWidth2 = termLabelWidth2 + labelNameGap + termNameMaxW2;
        let termGroupStartX2 = terminalCenterX - termGroupTotalWidth2 / 2;

        // 绘制"终点站"标签
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(labelCnSize);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(terminalLabel2, termGroupStartX2 + (termLabelWidth2 - termCnWidth2) / 2, labelCnBaseline);

        font = ROBOTO_REGULAR.deriveFont(labelEnSize);
        g.setFont(font);
        g.drawString("Terminal Station", termGroupStartX2 + (termLabelWidth2 - termEnWidth2) / 2, labelEnBaseline);

        let termNameStartX2 = termGroupStartX2 + termLabelWidth2 + labelNameGap;

        // 绘制终点站CJK站名
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(nameCnSize);
        g.setFont(font);
        g.setColor(routeInfo.routeColor);
        g.drawString(terminalStationCjk, termNameStartX2 + (termNameMaxW2 - termNameCnW2) / 2, nameCnBaseline);

        // 绘制终点站英文站名
        font = ROBOTO_BOLD.deriveFont(nameEnSize);
        g.setFont(font);
        g.drawString(terminalStationNonCjk, termNameStartX2 + (termNameMaxW2 - termNameEnW2) / 2, nameEnBaseline);
    }

    // ========== 判断显示到站屏幕还是线路图 ==========
    let doorValue = train.doorValue();
    let showStationScreen = doorValue > 0;

    if (showStationScreen) {
        // 车门开启时显示到站屏幕
        let doorAnimationOffset = state.doorAnimationOffset || 0;
        drawStationScreen(width, height, g, routeInfo, nextStationIndex, rightDoor, doorOpeningSide, isReversed, train, state, doorOpenImage, doorCloseImage, doorAnimationOffset, trainStatus);
    } else {
        // 车门关闭时显示线路图（全线或区间模式）
        if (state.routeMapType === "section") {
            drawSectionRouteMap(width, height, g, routeInfo, nextStationIndex, state);
        } else {
            drawFullRouteMap(width, height, g, routeInfo, nextStationIndex, state);
        }
    }
}

/**
 * 到站屏幕绘制函数：显示站名、车门图标和开门侧提示
 * @param {number} width - 屏幕宽度
 * @param {number} height - 屏幕高度
 * @param {Graphics2D} g - 绘图上下文
 * @param {object} routeInfo - 路线信息
 * @param {number} nextStationIndex - 当前站索引
 * @param {boolean} rightDoor - 右侧车门标志
 * @param {string} doorOpeningSide - 开门侧
 * @param {boolean} isReversed - 是否反向
 * @param {object} train - 列车对象
 * @param {object} state - 动画状态
 * @param {Image} doorOpenImage - 开门图标
 * @param {Image} doorCloseImage - 关门图标
 * @param {number} doorAnimationOffset - 车门动画偏移量
 * @param {string} trainStatus - 列车状态
 */
function drawStationScreen(width, height, g, routeInfo, nextStationIndex, rightDoor, doorOpeningSide, isReversed, train, state, doorOpenImage, doorCloseImage, doorAnimationOffset, trainStatus) {
    let font;
    let fm;

    // 获取当前站信息
    let thisStation = routeInfo.stationInfoList[nextStationIndex];
    let stationCjk = TextUtil.getCjkParts(thisStation.stationName);
    let stationNonCjk = TextUtil.getNonCjkParts(thisStation.stationName);
    let hasCjk = !stationCjk.isEmpty();

    // 判断当前侧车门是否开启
    let doorValue = train.doorValue();
    let doorThisSide = rightDoor ? train.doorLeftOpen[0] : train.doorRightOpen[0]; // 不要修改！

    let centerX = width / 2;
    let nameY = TOP_SCREEN_HEIGHT + height / 3;

    let cjkFontSize = 72;
    let enFontSize = 36;
    let totalNameHeight = 0;
    let maxTextWidth = 0;

    // 绘制站名（CJK + 英文 或 纯英文/纯CJK）
    let cjkAscentC = 0, enDescentE = 0;
    if (hasCjk && !stationNonCjk.isEmpty()) {
        // 中文居中
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(cjkFontSize);
        fm = g.getFontMetrics(font);
        cjkAscentC = fm.getAscent();
        let cjkWidth = fm.stringWidth(stationCjk);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(stationCjk, centerX - cjkWidth / 2, nameY);

        // 英文居中，字体略小于中文，往下移2px
        font = ROBOTO_BOLD.deriveFont(enFontSize);
        fm = g.getFontMetrics(font);
        enDescentE = fm.getDescent();
        let enWidth = fm.stringWidth(stationNonCjk);
        let enY = nameY + cjkFontSize - 20;
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(stationNonCjk, centerX - enWidth / 2, enY);

        maxTextWidth = Math.max(cjkWidth, enWidth);
        totalNameHeight = cjkFontSize - 20 + enFontSize;
    } else {
        // 纯英文或纯CJK站名
        font = ROBOTO_BOLD.deriveFont(cjkFontSize);
        fm = g.getFontMetrics(font);
        cjkAscentC = fm.getAscent();
        enDescentE = fm.getDescent();
        g.setFont(font);
        g.setColor(Color.BLACK);
        let stationText = stationNonCjk.isEmpty() ? stationCjk : stationNonCjk;
        maxTextWidth = fm.stringWidth(stationText);
        g.drawString(stationText, centerX - maxTextWidth / 2, nameY);
        totalNameHeight = cjkFontSize;
    }

    // ========== 车门图标绘制：车门中心与站名视觉中心对齐 ==========
    // 站名视觉中心 = 中文顶部 + 总视觉高度/2
    let visualNameTop = nameY - cjkAscentC;
    let visualNameBottom = nameY + cjkFontSize + 2 + enDescentE;
    let nameCenterY = (visualNameTop + visualNameBottom) / 2;
    let doorHeight = 143;
    let doorY = nameCenterY - doorHeight / 2;
    let doorWidth = 80;
    let gapFromName = 200;

    let leftDoorX = centerX - maxTextWidth / 2 - gapFromName - doorWidth;
    let rightDoorX = centerX + maxTextWidth / 2 + gapFromName;

    let showDoorAnimation = doorValue > 0 && doorThisSide;

    if (showDoorAnimation) {
        // 开门动画：左右两侧车门向外偏移
        if (doorOpenImage) {
            g.drawImage(doorOpenImage, leftDoorX - doorAnimationOffset, doorY, doorWidth, doorHeight, null);
            g.drawImage(doorOpenImage, rightDoorX + doorWidth + doorAnimationOffset, doorY, -doorWidth, doorHeight, null);
        }
    } else {
        // 关门图标
        if (doorCloseImage) {
            let closeW = 160;
            let closeH = 143;
            g.drawImage(doorCloseImage, leftDoorX, doorY, closeW, closeH, null);
        }
    }

    // ========== 开门侧提示文字："本侧开门"/"对侧开门" ==========
    // 中文稍大，离屏幕边缘200px，上移13px
    let doorTextCn = doorThisSide ? "本侧开门" : "对侧开门";
    let doorTextEn = doorThisSide ? "Doors open on this side" : "Doors open on the other side";

    let bottomY = height - 68;

    let cnFontSize = 32;
    let enFontSize2 = 16;
    let doorTextGap = 2;

    // --- 左侧文字组：离左边缘200px居中 ---
    let leftCenterX = 200;

    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(cnFontSize);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let cnWidthLeft = fm.stringWidth(doorTextCn);
    g.drawString(doorTextCn, leftCenterX - cnWidthLeft / 2, bottomY);

    font = ROBOTO_BOLD.deriveFont(enFontSize2);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let enWidthLeft = fm.stringWidth(doorTextEn);
    g.drawString(doorTextEn, leftCenterX - enWidthLeft / 2, bottomY + cnFontSize + doorTextGap);

    // --- 右侧文字组：离右边缘200px居中 ---
    let rightCenterX = width - 200;

    // 右侧中文：基于自身宽度在右侧中心居中
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(cnFontSize);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let cnWidthRight = fm.stringWidth(doorTextCn);
    g.drawString(doorTextCn, rightCenterX - cnWidthRight / 2, bottomY);

    // 右侧英文：间距5px
    font = ROBOTO_BOLD.deriveFont(enFontSize2);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let enWidthRight = fm.stringWidth(doorTextEn);
    g.drawString(doorTextEn, rightCenterX - enWidthRight / 2, bottomY + cnFontSize + doorTextGap);

    // 判断是否为终点站
    let isTerminus = nextStationIndex >= routeInfo.stationInfoList.length - 1;
    let depotName = CONFIG.showDepotName ? routeInfo.depotName : DEFAULT_DEPOT_NAME;
    let returningToDepot = nextStationIndex + 1 >= routeInfo.stationInfoList.length && routeInfo.nextRouteInfo == null;
}

/**
 * 全线线路图绘制函数
 * @param {number} width - 屏幕宽度
 * @param {number} height - 屏幕高度
 * @param {Graphics2D} g - 绘图上下文
 * @param {object} routeInfo - 路线信息
 * @param {number} nextStationIndex - 下一站索引
 * @param {object} state - 动画状态
 */
function drawFullRouteMap(width, height, g, routeInfo, nextStationIndex, state) {
    let platformCount = routeInfo.stationInfoList.length;
    let x = 120;
    let y = height / 2 + 30;

    const totalWidth = width - 240;
    const stationSpacing = totalWidth / (platformCount - 1);

    // 箭头动画帧计数
    if (!state.arrowAnimationFrame) {
        state.arrowAnimationFrame = 0;
    }
    state.arrowAnimationFrame++;

    // ========== 线路连接线绘制（20px粗线） ==========
    g.setStroke(new BasicStroke(20));

    for (let i = 0; i < platformCount - 1; i++) {
        let stationX1 = x + i * stationSpacing;
        let stationX2 = x + (i + 1) * stationSpacing;

        // 已过车站用灰色，未到车站用线路颜色
        if (i < nextStationIndex - 1) {
            g.setColor(Color.GRAY);
        } else {
            g.setColor(routeInfo.routeColor);
        }

        g.drawLine(stationX1, y + 25, stationX2, y + 25);

        // 当前段（下一站前一区间）绘制移动箭头动画
        if (i === nextStationIndex - 1) {
            let arrowCount = 8;
            let arrowSpacing = (stationX2 - stationX1) / (arrowCount + 1);
            let animSpeed = 0.5;
            let progress = (state.arrowAnimationFrame * animSpeed) % (arrowCount + 1);
            let currentArrowIndex = Math.floor(progress);

            for (let j = 1; j <= arrowCount; j++) {
                let arrowX = stationX1 + j * arrowSpacing;
                if (j <= currentArrowIndex) {
                    g.setColor(Color.WHITE);
                    drawArrow(g, arrowX, y + 25);
                }
            }
        }
    }

    g.setStroke(new BasicStroke(1));

    // ========== 各车站圆圈与站名绘制 ==========
    for (let i = 0; i < platformCount; i++) {
        let stationX = x + i * stationSpacing;

        // 绘制车站圆圈
        drawStationCircle(g, stationX, y, i, nextStationIndex, routeInfo.routeColor, routeInfo.stationInfoList[i], state);

        let stationInfo = routeInfo.stationInfoList[i];
        let stationNameCjk = TextUtil.getCjkParts(stationInfo.stationName);
        let stationNameNonCjk = TextUtil.getNonCjkParts(stationInfo.stationName);
        let hasInterchange = stationInfo.interchangeInfo != null && stationInfo.interchangeInfo.length > 0;

        // 奇偶交错：奇数索引站名在上方，偶数索引在下方
        let nameAbove = (i % 2 === 1);

        if (nameAbove) {
            // 站名在线上方
            let nameY = y - 35;

            font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(18.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(i < nextStationIndex ? Color.GRAY : Color.BLACK);
            g.drawString(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk, stationX - fm.stringWidth(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk) / 2, nameY);

            font = ROBOTO_REGULAR.deriveFont(14.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.drawString(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk, stationX - fm.stringWidth(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk) / 2, nameY + 22);

            // 换乘信息在线路下方
            if (hasInterchange) {
                drawInterchangeInfo(g, stationX, y + 65, stationInfo.interchangeInfo, i < nextStationIndex, "up");
            }
        } else {
            // 站名在线路下方
            let nameY = y + 75;

            font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(18.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(i < nextStationIndex ? Color.GRAY : Color.BLACK);
            g.drawString(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk, stationX - fm.stringWidth(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk) / 2, nameY);

            font = ROBOTO_REGULAR.deriveFont(14.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.drawString(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk, stationX - fm.stringWidth(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk) / 2, nameY + 22);

            // 换乘信息在线路上方
            if (hasInterchange) {
                drawInterchangeInfo(g, stationX, y - 15, stationInfo.interchangeInfo, i < nextStationIndex, "down");
            }
        }
    }
}

/**
 * 区间线路图绘制函数：仅显示当前站前后共3站的局部线路
 * @param {number} width - 屏幕宽度
 * @param {number} height - 屏幕高度
 * @param {Graphics2D} g - 绘图上下文
 * @param {object} routeInfo - 路线信息
 * @param {number} nextStationIndex - 下一站索引
 * @param {object} state - 动画状态
 */
function drawSectionRouteMap(width, height, g, routeInfo, nextStationIndex, state) {
    let stationsToShow = [];
    let indices = [];

    // 取当前站前最多2站作为显示范围
    let startIndex = Math.max(0, nextStationIndex - 2);
    
    for (let i = startIndex; i <= nextStationIndex && i < routeInfo.stationInfoList.length; i++) {
        stationsToShow.push(routeInfo.stationInfoList[i]);
        indices.push(i);
    }

    // 不足3站时向前补齐
    if (stationsToShow.length < 3) {
        for (let i = stationsToShow.length - 1; i >= 0 && stationsToShow.length < 3; i--) {
            if (i - 1 >= 0 && !indices.includes(i - 1)) {
                stationsToShow.unshift(routeInfo.stationInfoList[i - 1]);
                indices.unshift(i - 1);
            }
        }
    }

    let platformCount = stationsToShow.length;
    let totalWidth = (width - 240) / 2;
    let x = (width - totalWidth) / 2;
    let y = height / 2 + 30;
    let stationSpacing = totalWidth / (platformCount - 1);

    // 箭头动画帧计数
    if (!state.arrowAnimationFrame) {
        state.arrowAnimationFrame = 0;
    }
    state.arrowAnimationFrame++;

    // ========== 线路连接线绘制 ==========
    g.setStroke(new BasicStroke(20));

    for (let i = 0; i < platformCount - 1; i++) {
        let stationX1 = x + i * stationSpacing;
        let stationX2 = x + (i + 1) * stationSpacing;

        let currentIndex = indices[i];

        // 已过区间灰色，未来区间线路色
        if (currentIndex < nextStationIndex - 1) {
            g.setColor(Color.GRAY);
        } else {
            g.setColor(routeInfo.routeColor);
        }

        g.drawLine(stationX1, y + 25, stationX2, y + 25);

        // 当前区间箭头动画
        if (currentIndex === nextStationIndex - 1) {
            let arrowCount = Math.max(3, Math.floor(stationSpacing / 60));
            let arrowSpacing = (stationX2 - stationX1) / (arrowCount + 1);
            let animSpeed = 0.5;
            let progress = (state.arrowAnimationFrame * animSpeed) % (arrowCount + 1);
            let currentArrowIndex = Math.floor(progress);

            for (let j = 1; j <= arrowCount; j++) {
                let arrowX = stationX1 + j * arrowSpacing;
                if (j <= currentArrowIndex) {
                    g.setColor(Color.WHITE);
                    drawArrow(g, arrowX, y + 25);
                }
            }
        }
    }

    g.setStroke(new BasicStroke(1));

    // ========== 车站圆圈与站名绘制 ==========
    for (let i = 0; i < platformCount; i++) {
        let stationX = x + i * stationSpacing;
        let index = indices[i];

        drawStationCircle(g, stationX, y, index, nextStationIndex, routeInfo.routeColor, stationsToShow[i], state);

        let stationInfo = stationsToShow[i];
        let stationNameCjk = TextUtil.getCjkParts(stationInfo.stationName);
        let stationNameNonCjk = TextUtil.getNonCjkParts(stationInfo.stationName);
        let hasInterchange = stationInfo.interchangeInfo != null && stationInfo.interchangeInfo.length > 0;

        // 奇偶交错排列站名
        let nameAbove = (index % 2 === 1);

        if (nameAbove) {
            let nameY = y - 35;

            font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(20.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(index < nextStationIndex ? Color.GRAY : Color.BLACK);
            g.drawString(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk, stationX - fm.stringWidth(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk) / 2, nameY);

            font = ROBOTO_REGULAR.deriveFont(16.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.drawString(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk, stationX - fm.stringWidth(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk) / 2, nameY + 24);

            if (hasInterchange) {
                drawInterchangeInfo(g, stationX, y + 65, stationInfo.interchangeInfo, index < nextStationIndex, "up");
            }
        } else {
            let nameY = y + 75;

            font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(20.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(index < nextStationIndex ? Color.GRAY : Color.BLACK);
            g.drawString(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk, stationX - fm.stringWidth(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk) / 2, nameY);

            font = ROBOTO_REGULAR.deriveFont(16.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.drawString(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk, stationX - fm.stringWidth(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk) / 2, nameY + 24);

            if (hasInterchange) {
                drawInterchangeInfo(g, stationX, y - 15, stationInfo.interchangeInfo, index < nextStationIndex, "down");
            }
        }
    }
}

/**
 * 车站圆圈绘制函数：包含外圈、内圈和换乘环
 * @param {Graphics2D} g - 绘图上下文
 * @param {number} x - 圆圈X坐标
 * @param {number} y - 圆圈Y坐标
 * @param {number} index - 车站索引
 * @param {number} nextIndex - 下一站索引
 * @param {Color} routeColor - 线路颜色
 * @param {object} stationInfo - 车站信息
 * @param {object} state - 动画状态（含闪烁相位）
 */
function drawStationCircle(g, x, y, index, nextIndex, routeColor, stationInfo, state) {
    // 闪烁动画相位（频率减半：120帧周期，60帧可见/60帧不可见）
    if (!state.flashPhase) {
        state.flashPhase = 0;
    }
    state.flashPhase++;

    let outerRadius = 28;
    let innerRadius = 22;
    let hasInterchange = stationInfo.interchangeInfo && stationInfo.interchangeInfo.length > 0;

    // 当前站是否处于闪烁可见期（60帧可见 / 60帧不可见）
    let flashVisible = state.flashPhase % 120 < 60;

    // --- 外圈绘制 ---
    if (index < nextIndex) {
        // 已过站：灰色实心圆
        g.setColor(Color.GRAY);
        g.fillOval(x - outerRadius, y + 25 - outerRadius, outerRadius * 2, outerRadius * 2);
    } else if (index === nextIndex) {
        // 当前站闪烁：可见60帧→不可见60帧，闪烁用绿色
        if (flashVisible) {
            g.setColor(Color.GREEN);
            g.fillOval(x - outerRadius, y + 25 - outerRadius, outerRadius * 2, outerRadius * 2);
        }
    } else {
        // 未来站：线路色实心圆
        g.setColor(routeColor);
        g.fillOval(x - outerRadius, y + 25 - outerRadius, outerRadius * 2, outerRadius * 2);
    }

    // --- 换乘站内圈白色填充（保持原设计） ---
    if (hasInterchange) {
        g.setColor(Color.WHITE);
        g.fillOval(x - innerRadius, y + 25 - innerRadius, innerRadius * 2, innerRadius * 2);
    }

    // --- 换乘环绘制（已过站全部灰色） ---
    if (hasInterchange) {
        drawInterchangeCircle(g, x, y + 25, routeColor, stationInfo.interchangeInfo, index < nextIndex ? Color.GRAY : routeColor, state, index === nextIndex);
    }
}

/**
 * 换乘环绘制函数：多层彩色圆弧叠加
 * @param {Graphics2D} g - 绘图上下文
 * @param {number} cx - 圆心X
 * @param {number} cy - 圆心Y
 * @param {Color} currentRouteColor - 当前线路颜色
 * @param {Array} interchangeInfo - 换乘线路信息数组
 * @param {Color} baseColor - 基础颜色（灰色或线路色）
 * @param {object} state - 动画状态（含旋转角度）
 * @param {boolean} isCurrentStation - 是否为当前站（控制旋转动画）
 */
function drawInterchangeCircle(g, cx, cy, currentRouteColor, interchangeInfo, baseColor, state, isCurrentStation) {
    // 收集所有换乘线路颜色（含当前线路色）
    let allColors = [currentRouteColor];
    for (let i = 0; i < interchangeInfo.length; i++) {
        if (interchangeInfo[i]) {
            allColors.push(interchangeInfo[i].color);
        }
    }
    let lineCount = allColors.length;

    // 计算各段角度（含间隙）
    let totalAngle = 2 * Math.PI;
    let gapAngle = 0.2;
    let eachAngle = (totalAngle - gapAngle * lineCount) / lineCount;
    let outerR = 20;
    let innerR = 12;

    // 当前站换乘环旋转动画
    let rotationOffset = 0;
    if (isCurrentStation) {
        if (!state.interchangeRotation) {
            state.interchangeRotation = 0;
        }
        state.interchangeRotation += 0.02;
        rotationOffset = state.interchangeRotation % (2 * Math.PI);
    }

    // 逐段绘制弧形（已过站统一用灰色）
    for (let i = 0; i < lineCount; i++) {
        let startAngle = rotationOffset + gapAngle / 2 + i * (eachAngle + gapAngle);
        let endAngle = startAngle + eachAngle;

        g.setColor(baseColor === Color.GRAY ? Color.GRAY : allColors[i]);

        // 用多个小梯形逼近弧形
        let steps = 20;
        for (let s = 0; s < steps; s++) {
            let a1 = startAngle + (endAngle - startAngle) * s / steps;
            let a2 = startAngle + (endAngle - startAngle) * (s + 1) / steps;

            let x1 = cx + Math.cos(a1) * outerR;
            let y1 = cy + Math.sin(a1) * outerR;
            let x2 = cx + Math.cos(a2) * outerR;
            let y2 = cy + Math.sin(a2) * outerR;
            let x3 = cx + Math.cos(a2) * innerR;
            let y3 = cy + Math.sin(a2) * innerR;
            let x4 = cx + Math.cos(a1) * innerR;
            let y4 = cy + Math.sin(a1) * innerR;

            g.fillPolygon([x1, x2, x3, x4], [y1, y2, y3, y4], 4);
        }
    }
}

/**
 * 换乘信息标签绘制函数
 * @param {Graphics2D} g - 绘图上下文
 * @param {number} stationX - 车站X坐标
 * @param {number} y - 标签起始Y坐标
 * @param {Array} interchangeInfo - 换乘线路信息
 * @param {boolean} isGray - 是否已过站（灰色显示）
 * @param {string} direction - 标签方向（"up"或"down"）
 */
function drawInterchangeInfo(g, stationX, y, interchangeInfo, isGray, direction) {
    let font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(12.0);
    let fm = g.getFontMetrics(font);

    let routeNameHeight = 28;
    let labelSpacing = 4;

    // 计算总高度，以便向上或向下排列
    let totalHeight = interchangeInfo.length * routeNameHeight + (interchangeInfo.length - 1) * labelSpacing;

    let startY;
    if (direction === "down") {
        // 向下排列：从y - totalHeight开始
        startY = y - totalHeight;
    } else {
        // 向上排列：从y开始
        startY = y;
    }

    // 逐条绘制换乘标签
    for (let i = 0; i < interchangeInfo.length; i++) {
        let interchangeRoute = interchangeInfo[i];
        if (interchangeRoute) {
            let routeName = TextUtil.getCjkParts(interchangeRoute.name);
            let label = "换乘" + routeName;
            let routeNameWidth = fm.stringWidth(label) + 15;
            let currentY = startY + i * (routeNameHeight + labelSpacing);

            // 换乘标签背景色
            let bgColor = isGray ? Color.GRAY : interchangeRoute.color;
            g.setColor(bgColor);
            g.fillRoundRect(stationX - routeNameWidth / 2, currentY - 5, routeNameWidth, routeNameHeight, 5, 5);

            // 换乘标签文字
            g.setColor(Color.WHITE);
            g.setFont(font);
            g.drawString(label, stationX - routeNameWidth / 2 + 8, currentY + 8);
        }
    }
}

/**
 * 方向箭头绘制函数（前进方向三角形）
 * @param {Graphics2D} g - 绘图上下文
 * @param {number} x - 箭头尖端X坐标
 * @param {number} y - 箭头尖端Y坐标
 */
function drawArrow(g, x, y) {
    let size = 10;
    let xPoints = [x, x - size / 2, x - size / 2];
    let yPoints = [y, y - size / 2, y + size / 2];
    g.fillPolygon(xPoints, yPoints, 3);
}

/**
 * 蓝屏"No Signal"绘制函数
 * @param {Graphics2D} g - 绘图上下文
 * @param {number} width - 屏幕宽度
 * @param {number} height - 屏幕高度
 */
function drawBlueScreen(g, width, height) {
    // 黑色背景
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, width, height);

    // "NO SIGNAL" 大字居中
    let font = ROBOTO_BOLD.deriveFont(66.0);
    let fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.WHITE);
    let x = (width - fm.stringWidth("NO SIGNAL")) / 2;
    let y = (height - fm.getHeight()) / 2 + fm.getAscent();
    g.drawString("NO SIGNAL", x, y);

    // 右上角HDMI输入源标识
    font = ROBOTO_REGULAR.deriveFont(32.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    x = width - 30 - fm.stringWidth("HDMI 1");
    y = 30 + fm.getAscent();
    g.drawString("HDMI 1", x, y);

    // HDMI分辨率刷新率信息
    font = ROBOTO_REGULAR.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    x = width - 30 - fm.stringWidth("1920x1080 60Hz");
    y += fm.getAscent() + 5;
    g.drawString("1920x1080 60Hz", x, y);
}
