importPackage(java.awt);

// include("font_util.js");
// include("js_util.js");

const SCREEN_COMING_NEXT = "coming_next";
const SCREEN_FULL_ROUTE_MAP = "full_route_map";
const SCREEN_NEXT_TEXT = "next_text";

const THIS_ROUTE_NAME_HEIGHT = 72;
const RIGHT_PART_INTERCHANGE_ROUTE_NAME_HEIGHT = 32;
const HORIZONTAL_SPACING = 15;
const VERTICAL_SPACING = 5;
const ROUTE_BAR_WIDTH = 10;
const ARROW_WIDTH = 30;

var WIDTH;
var HEIGHT;
var LEFT_WIDTH;
var RIGHT_WIDTH;
var START_X;
var START_Y;

const AUTHOR = "Origion Code by Jeffreyg1228";
const LCD_VERSION = "Xiaotang27 Presents";

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"));

// 图像资源（在 drawScreen 中加载）
var bgImage = null;
var doorOpenImage = null;
var doorCloseImage = null;

function drawBlueScreen(g) {
    // 绘制黑屏
    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 1"
    font = ROBOTO_REGULAR.deriveFont(32.0); // 加大字号
    fm = g.getFontMetrics(font);
    g.setFont(font);
    x = WIDTH - HORIZONTAL_SPACING * 2 - fm.stringWidth("HDMI 1");
    y = VERTICAL_SPACING * 2 + fm.getAscent();
    g.drawString("HDMI 1", x, y);

    // 在HDMI 1下方显示 "1920x1080 60Hz"
    font = ROBOTO_REGULAR.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    x = WIDTH - HORIZONTAL_SPACING * 2 - fm.stringWidth("1920x1080 60Hz");
    y += fm.getAscent() + VERTICAL_SPACING;
    g.drawString("1920x1080 60Hz", x, y);
}
function getOpenDirection(doorOpeningSide, rightDoor, isReversed) {
    // 调试信息
    print("getOpenDirection called with:");
    print("  doorOpeningSide: " + doorOpeningSide);
    print("  rightDoor: " + rightDoor);
    print("  isReversed: " + isReversed);
    
    if (doorOpeningSide == null) {
        print("  returning -1 (no open)");
        return -1;
    }
    if (doorOpeningSide == 0) {
        print("  returning -1 (no open)");
        return -1;
    }
    if (doorOpeningSide == 3) {
        print("  returning 1 (both sides, show as this side)");
        return 1; // 两侧都开门，显示为本侧开门
    }
    
    let result;
    if (isReversed) {
        // 列车反转时，左右开门方向互换
        if ((doorOpeningSide == 1 && rightDoor) || (doorOpeningSide == 2 && !rightDoor)) {
            result = 0; // 本侧开门
            print("  returning 1 (this side) - reversed");
        } else {
            result = 1; // 对侧开门
            print("  returning 0 (opposite side) - reversed");
        }
    } else {
        // 列车正常时
        if ((doorOpeningSide == 1 && !rightDoor) || (doorOpeningSide == 2 && rightDoor)) {
            result = 0; // 本侧开门
            print("  returning 1 (this side) - normal");
        } else {
            result = 1; // 对侧开门
            print("  returning 0 (opposite side) - normal");
        }
    }
    return result;
}

function drawGreenTriangle(g, x, y, size) {
    let xPoints = [x, x - size / 2, x + size / 2];
    let yPoints = [y, y + size, y + size];
    g.setColor(Color.GREEN);
    g.fillPolygon(xPoints, yPoints, 3);
}

function drawRedCross(g, x, y, size) {
    g.setColor(Color.RED);
    g.setStroke(new BasicStroke(size / 5));
    g.drawLine(x - size / 2, y - size / 2, x + size / 2, y + size / 2);
    g.drawLine(x + size / 2, y - size / 2, x - size / 2, y + size / 2);
    g.setStroke(new BasicStroke(1));
}

function drawScreen(width, height, g, routeInfo, doorOpenInfo, nextStationIndex, trainStatus, leftPartUseCjk, rightPartFullRouteMapUseCjk, rightPartScreenType, rightDoor, manualAllowed, isCurrentlyManual, train, doorOpeningSide, state) {
    WIDTH = width;
    HEIGHT = height;
    LEFT_WIDTH = WIDTH / 3;
    RIGHT_WIDTH = WIDTH - LEFT_WIDTH - ROUTE_BAR_WIDTH;
    START_X = LEFT_WIDTH / 20;
    START_Y = HEIGHT / 10;
    
    // 加载图像资源（只在首次调用时加载）
    bgImage = BGIMAGE;
    doorOpenImage = DOOROPENIMAGE;
    doorCloseImage = DOORCLOSEIMAGE;
    
    // 绘制上屏蓝色背景
    const TOP_SCREEN_HEIGHT = HEIGHT / 4;
    g.setColor(rgbToColor(0, 0, 170));
    g.fillRect(0, 0, WIDTH, TOP_SCREEN_HEIGHT);
    
    // 绘制下屏白色背景
    g.setColor(Color.WHITE);
    g.fillRect(0, TOP_SCREEN_HEIGHT, WIDTH, HEIGHT - TOP_SCREEN_HEIGHT);

    // 绘制 LCD 作者和版本
    let font = ROBOTO_REGULAR.deriveFont(12.0);
    let fm = g.getFontMetrics(font);
    g.setColor(Color.GRAY);
    g.setFont(font);
    g.drawString(AUTHOR, WIDTH - fm.stringWidth(AUTHOR) - 10, HEIGHT - 2 * fm.getHeight());
    g.drawString(LCD_VERSION, WIDTH - fm.stringWidth(LCD_VERSION) - 10, HEIGHT - fm.getHeight());

    if (trainStatus == STATUS_NO_ROUTE || routeInfo == null) {
        drawBlueScreen(g);
        return;
    }

    // 绘制线路名称方框
    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;
    
    // 检查线路名称是否有数字前缀（如 "9号线"）
    let cjkPart = TextUtil.getCjkParts(routeName);
    let match = routeName.match(/^([A-Za-z0-9]+)(.+)$/);
    
    if (match && match[1] && match[2]) {
        // 有数字/字母前缀，按设计稿格式显示
        let prefix = match[1];
        let suffix = match[2];
        
        // 绘制线路号方框（只包含数字/字母前缀）
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(32.0);
        fm = g.getFontMetrics(font);
        let numberWidth = fm.stringWidth(prefix) + 20;
        let numberHeight = 60;
        
        g.setColor(routeInfo.routeColor);
        g.fillRoundRect(10, 10, numberWidth, numberHeight, 5, 5);
        
        // 绘制数字
        g.setColor(Color.WHITE);
        g.setFont(font);
        g.drawString(prefix, 20, 45);
        
        // 绘制线路号英文
        font = ROBOTO_BOLD.deriveFont(12.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.drawString("Line " + prefix, 20, 58);
        
        // 绘制CJK部分（在线路号方框右侧）
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.WHITE);
        g.drawString(suffix, numberWidth + 15, 40);
        
        // 绘制时间（在线路名称右侧）
        font = ROBOTO_REGULAR.deriveFont(14.0);
        fm = g.getFontMetrics(font);
        g.setColor(Color.WHITE);
        g.setFont(font);
        let currentTime = new Date();
        let timeString = currentTime.getFullYear() + "-" + String(currentTime.getMonth() + 1).padStart(2, "0") + "-" + String(currentTime.getDate()).padStart(2, "0") + " " + String(currentTime.getHours()).padStart(2, "0") + ":" + String(currentTime.getMinutes()).padStart(2, "0");
        g.drawString(timeString, numberWidth + fm.stringWidth(suffix) + 35, 40);
    } else {
        // 普通格式
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
        fm = g.getFontMetrics(font);
        let routeNameWidth = fm.stringWidth(routeName) + 20;
        let routeNameHeight = 50;
        
        g.setColor(routeInfo.routeColor);
        g.fillRoundRect(10, 10, routeNameWidth, routeNameHeight, 5, 5);
        
        g.setColor(Color.WHITE);
        g.setFont(font);
        g.drawString(routeName, 20, 35);
        
        font = ROBOTO_BOLD.deriveFont(16.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.drawString(routeNameEn, 20, 55);
        
        // 绘制时间
        font = ROBOTO_REGULAR.deriveFont(14.0);
        fm = g.getFontMetrics(font);
        g.setColor(Color.WHITE);
        g.setFont(font);
        let currentTime = new Date();
        let timeString = currentTime.getFullYear() + "-" + String(currentTime.getMonth() + 1).padStart(2, "0") + "-" + String(currentTime.getDate()).padStart(2, "0") + " " + String(currentTime.getHours()).padStart(2, "0") + ":" + String(currentTime.getMinutes()).padStart(2, "0");
        g.drawString(timeString, routeNameWidth + 20, 40);
    }
    
    // 绘制上屏右上角的圆角矩形
    let nextStationName = "";
    let terminalStationName = "";
    let nextStationCjk = "";
    let nextStationNonCjk = "";
    let terminalStationCjk = "";
    let terminalStationNonCjk = "";
    
    // 列车到站时，圆角矩形内显示到达站，而不是到达站的下一站
    let displayStationIndex = trainStatus == STATUS_ARRIVED ? nextStationIndex : (trainStatus == STATUS_ARRIVED ? nextStationIndex + 1 : 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);
        }
    }
    
    // 绘制下一站圆角矩形
    let boxWidth = 200;
    let boxHeight = TOP_SCREEN_HEIGHT - 20;
    let boxX = WIDTH - boxWidth * 2 - 20;
    let boxY = 10;
    
    g.setColor(Color.WHITE);
    g.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 10, 10);
    g.setColor(Color.BLACK);
    g.drawRoundRect(boxX, boxY, boxWidth, boxHeight, 10, 10);
    
    // 绘制下一站文字
    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(trainStatus == STATUS_ARRIVED ? "本站" : "下一站", boxX + 10, boxY + 20);
    
    font = ROBOTO_REGULAR.deriveFont(12.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(trainStatus == STATUS_ARRIVED ? "This" : "Next", boxX + 10, boxY + 35);
    
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(rgbToColor(0, 0, 170));
    g.drawString(nextStationCjk, boxX + 70, boxY + 25);
    
    font = ROBOTO_BOLD.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(nextStationNonCjk, boxX + 70, boxY + 45);
    
    // 绘制终点站圆角矩形
    boxX += boxWidth + 10;
    
    g.setColor(Color.WHITE);
    g.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 10, 10);
    g.setColor(Color.BLACK);
    g.drawRoundRect(boxX, boxY, boxWidth, boxHeight, 10, 10);
    
    // 绘制终点站文字
    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    g.drawString("终点站", boxX + 10, boxY + 20);
    
    font = ROBOTO_REGULAR.deriveFont(12.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString("Terminal", boxX + 10, boxY + 35);
    
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(rgbToColor(0, 0, 170));
    g.drawString(terminalStationCjk, boxX + 70, boxY + 25);
    
    font = ROBOTO_BOLD.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(terminalStationNonCjk, boxX + 70, boxY + 45);
    
    // 根据列车状态绘制下屏内容
    let showCjk = !TextUtil.getCjkParts(routeInfo.routeName).isEmpty();
    let lastRouteDestination = routeInfo.lastRouteDestination == null ? { stationName: depotName, interchangeInfo: [] } : routeInfo.lastRouteDestination;
    
    // 检查是否到达终点站且即将回厂
    let isTerminus = nextStationIndex >= routeInfo.stationInfoList.length - 1;
    let isReturningToDepot = returningToDepot;
    
    let isReversed = train ? train.isReversed() : false;
    
    if (trainStatus == STATUS_ARRIVED) {
        drawRightPartThisStation(g, showCjk, routeInfo.routeColor, routeInfo.stationInfoList[nextStationIndex], rightDoor, doorOpenInfo, TOP_SCREEN_HEIGHT, isTerminus, isReturningToDepot, doorOpeningSide, isReversed, state, train);
    } else if (trainStatus == STATUS_WAITING_FOR_DEPARTURE) {
        drawRightPartFullRouteMap(g, rightPartFullRouteMapUseCjk, rightDoor, routeInfo.routeColor, nextStationIndex, routeInfo.stationInfoList, TOP_SCREEN_HEIGHT, state);
    } else if (trainStatus == STATUS_RETURNING_TO_DEPOT || returningToDepot) {
        drawRightPartReturningToDepot(g, showCjk, routeInfo.routeColor, TOP_SCREEN_HEIGHT);
    } else {
        if (rightPartScreenType == SCREEN_COMING_NEXT) {
            drawRightPartComingNextScreen(g, routeInfo.routeColor, showCjk, lastRouteDestination, nextStationIndex, routeInfo.stationInfoList, TOP_SCREEN_HEIGHT, rightDoor, doorOpeningSide, isReversed);
        } else if (rightPartScreenType == SCREEN_FULL_ROUTE_MAP) {
            if (state.routeMapType === "section") {
                drawRightPartCurrentSection(g, rightPartFullRouteMapUseCjk, rightDoor, routeInfo.routeColor, nextStationIndex, routeInfo.stationInfoList, TOP_SCREEN_HEIGHT, state);
            } else {
                drawRightPartFullRouteMap(g, rightPartFullRouteMapUseCjk, rightDoor, routeInfo.routeColor, nextStationIndex, routeInfo.stationInfoList, TOP_SCREEN_HEIGHT, state);
            }
        } else if (rightPartScreenType == SCREEN_NEXT_TEXT) {
            drawRightPartNextStationInfo(g, showCjk, routeInfo.routeColor, routeInfo.stationInfoList[nextStationIndex2], TOP_SCREEN_HEIGHT, rightDoor, doorOpeningSide, isReversed);
        } else {
            drawRightPartNextStationInfo(g, showCjk, routeInfo.routeColor, routeInfo.stationInfoList[nextStationIndex2], TOP_SCREEN_HEIGHT, rightDoor, doorOpeningSide, isReversed);
        }
    }
}

function drawRightPartThisStation(g, showCjk, routeColor, thisStation, rightDoor, doorOpenInfo, topScreenHeight, isTerminus, returningToDepot, doorOpeningSide, isReversed, state, train) {
    let font;
    let fm;
    const startX = 50;
    const startY = topScreenHeight + HEIGHT / 8;
    let x = startX;
    let y = startY;
    
    // 绘制背景
    if (bgImage) {
        g.drawImage(bgImage, 0, topScreenHeight, WIDTH, HEIGHT - topScreenHeight, null);
    }
    
    // 判断开门状态
    let isDoorOpen = train.isDoorOpening();
    
    // 绘制车门
    if (isDoorOpen) {
        // 开门动画
        drawDoorOpenAnimation(g, rightDoor, topScreenHeight, state);
    } else {
        // 关门状态
        drawDoorClosed(g, rightDoor, topScreenHeight, state);
    }
    
    // 绘制到站信息
    x = WIDTH / 2 - 80;
    y = topScreenHeight + HEIGHT / 3;
    
    // 绘制 CJK 站名
    const stationCjk = TextUtil.getCjkParts(thisStation.stationName);
    const stationNonCjk = TextUtil.getNonCjkParts(thisStation.stationName);
    const hasCjk = !stationCjk.isEmpty();
    let strToDraw = hasCjk ? stationCjk : stationNonCjk;
    font = (hasCjk ? SOURCE_HAN_SANS_CN_BOLD : ROBOTO_BOLD).deriveFont(48.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    g.drawString(strToDraw, x, y);
    y += fm.getDescent() + 15;
    
    // 绘制非 CJK 站名
    if (hasCjk && !stationNonCjk.isEmpty()) {
        font = ROBOTO_BOLD.deriveFont(32.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.GRAY);
        g.drawString(stationNonCjk, x, y);
        y += fm.getDescent() + 20;
    }
    
    // 绘制开门方向信息
    let openDirection = getOpenDirection(doorOpeningSide, rightDoor, isReversed);
    
    // 左侧开门提示
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let leftDoorText = openDirection == 1 ? "本侧开门" : "对侧开门";
    g.drawString(leftDoorText, 50, topScreenHeight + HEIGHT - 60);
    
    font = ROBOTO_BOLD.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString(openDirection == 1 ? "Doors open on this side" : "Doors open on the other side", 50, topScreenHeight + HEIGHT - 35);
    
    // 右侧开门提示
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    let rightDoorText = openDirection == 1 ? "本侧开门" : "对侧开门";
    g.drawString(rightDoorText, WIDTH - fm.stringWidth(rightDoorText) - 50, topScreenHeight + HEIGHT - 60);
    
    font = ROBOTO_BOLD.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString(openDirection == 1 ? "Doors open on this side" : "Doors open on the other side", WIDTH - fm.stringWidth(openDirection == 1 ? "Doors open on this side" : "Doors open on the other side") - 50, topScreenHeight + HEIGHT - 35);

    // 如果到达站是终点站，且即将回厂，在屏幕最低端上方显示提示文字
    if (isTerminus && returningToDepot) {
        font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(20.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.BLACK);
        let chineseText = "本次列车已经到达终点站。请全部乘客下车。";
        g.drawString(chineseText, (WIDTH - fm.stringWidth(chineseText)) / 2, HEIGHT - 60);
        
        font = ROBOTO_BOLD.deriveFont(16.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.GRAY);
        let englishText = "This is the terminus. All change please.";
        g.drawString(englishText, (WIDTH - fm.stringWidth(englishText)) / 2, HEIGHT - 30);
    }
}

function drawDoorOpenAnimation(g, rightDoor, topScreenHeight, state) {
    let phase = state.doorAnimationPhase;
    let offset = Math.sin(phase * 0.1) * 20;
    
    let doorY = topScreenHeight + HEIGHT / 4;
    let doorWidth = 80;
    let doorHeight = 100;
    
    // 左侧车门
    let leftDoorX = WIDTH / 2 - 150 + offset;
    if (doorOpenImage) {
        g.drawImage(doorOpenImage, leftDoorX, doorY, doorWidth, doorHeight, null);
    }
    
    // 右侧车门（翻转）
    let rightDoorX = WIDTH / 2 + 70 + offset;
    if (doorOpenImage) {
        g.drawImage(doorOpenImage, rightDoorX + doorWidth, doorY, -doorWidth, doorHeight, null);
    }
    
    state.doorAnimationPhase++;
}

function drawDoorClosed(g, rightDoor, topScreenHeight, state) {
    let doorY = topScreenHeight + HEIGHT / 4;
    let doorWidth = 80;
    let doorHeight = 100;
    
    // 左侧车门
    let leftDoorX = WIDTH / 2 - 150;
    if (doorCloseImage) {
        g.drawImage(doorCloseImage, leftDoorX, doorY, doorWidth, doorHeight, null);
    }
    
    // 右侧车门（翻转）
    let rightDoorX = WIDTH / 2 + 70;
    if (doorCloseImage) {
        g.drawImage(doorCloseImage, rightDoorX + doorWidth, doorY, -doorWidth, doorHeight, null);
    }
}

function drawRightPartComingNextScreen(g, routeColor, showCjk, lastRouteDestination, nextStationIndex, stationInfoList, topScreenHeight, rightDoor, doorOpeningSide, isReversed) {
    let font;
    let fm;
    const startX = 50;
    const startY = topScreenHeight + HEIGHT / 8;
    let x = startX;
    let y = startY;
    
    // 绘制线路颜色部分，延伸到最低端
    g.setColor(routeColor);
    let arrowWidth = 30;
    let arrowX = x;
    let arrowY = y;
    let arrowHeight = HEIGHT - topScreenHeight - 20; // 延伸到最低端
    
    // 绘制主体矩形
    g.fillRect(arrowX, arrowY, arrowWidth, arrowHeight);
    
    // 在最上端添加一个同颜色的正三角形
    let triangleHeight = 20;
    let triangleX1 = arrowX;
    let triangleX2 = arrowX + arrowWidth / 2;
    let triangleX3 = arrowX + arrowWidth;
    let triangleY1 = arrowY;
    let triangleY2 = arrowY - triangleHeight;
    let triangleY3 = arrowY;
    
    let triangleXPoints = [triangleX1, triangleX2, triangleX3];
    let triangleYPoints = [triangleY1, triangleY2, triangleY3];
    g.fillPolygon(triangleXPoints, triangleYPoints, 3);
    
    // 绘制圆圈
    const circleRadius = 12; // 加大圆圈半径
    g.setColor(Color.WHITE);
    // 调整圆圈位置，使其与文字对齐
    g.fillOval(arrowX + arrowWidth / 2 - circleRadius, arrowY + 20, circleRadius * 2, circleRadius * 2); // 下一站
    g.fillOval(arrowX + arrowWidth / 2 - circleRadius, arrowY + 70, circleRadius * 2, circleRadius * 2); // 本站
    g.fillOval(arrowX + arrowWidth / 2 - circleRadius, arrowY + 120, circleRadius * 2, circleRadius * 2); // 上一站
    
    // 绘制车站信息
    x += arrowWidth + 30;
    y = startY;
    
    // 下一站
    let nextStation = nextStationIndex + 1 < stationInfoList.length ? stationInfoList[nextStationIndex + 1] : null;
    if (nextStation) {
        let nextStationCjk = TextUtil.getCjkParts(nextStation.stationName);
        let nextStationNonCjk = TextUtil.getNonCjkParts(nextStation.stationName);
        
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(20.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.GRAY);
        g.drawString(nextStationCjk.isEmpty() ? nextStationNonCjk : nextStationCjk, x, y + 20);
        
        font = ROBOTO_REGULAR.deriveFont(16.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.drawString(nextStationNonCjk.isEmpty() ? nextStationCjk : nextStationNonCjk, x, y + 40);
    }
    
    // 本站
    y += 50; // 加大间距
    let currentStation = stationInfoList[nextStationIndex];
    let currentStationCjk = TextUtil.getCjkParts(currentStation.stationName);
    let currentStationNonCjk = TextUtil.getNonCjkParts(currentStation.stationName);
    
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    g.drawString(currentStationCjk.isEmpty() ? currentStationNonCjk : currentStationCjk, x, y + 20);
    
    font = ROBOTO_BOLD.deriveFont(20.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(currentStationNonCjk.isEmpty() ? currentStationCjk : currentStationNonCjk, x, y + 40);
    
    // 上一站
    y += 50; // 加大间距
    let prevStation = nextStationIndex > 0 ? stationInfoList[nextStationIndex - 1] : lastRouteDestination;
    let prevStationCjk = TextUtil.getCjkParts(prevStation.stationName);
    let prevStationNonCjk = TextUtil.getNonCjkParts(prevStation.stationName);
    
    font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(20.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString(prevStationCjk.isEmpty() ? prevStationNonCjk : prevStationCjk, x, y + 20);
    
    font = ROBOTO_REGULAR.deriveFont(16.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.drawString(prevStationNonCjk.isEmpty() ? prevStationCjk : prevStationNonCjk, x, y + 40);
    
    // 计算最大文字宽度，用于调整左右边距
    let maxTextWidth = 0;
    let totalTextHeight = 0;
    
    // 计算中文名称的宽度和高度
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    maxTextWidth = Math.max(maxTextWidth, fm.stringWidth(currentStationCjk.isEmpty() ? currentStationNonCjk : currentStationCjk));
    totalTextHeight += fm.getHeight();
    
    // 计算英文名称的宽度和高度
    font = ROBOTO_BOLD.deriveFont(20.0);
    fm = g.getFontMetrics(font);
    maxTextWidth = Math.max(maxTextWidth, fm.stringWidth(currentStationNonCjk.isEmpty() ? currentStationCjk : currentStationNonCjk));
    totalTextHeight += fm.getHeight();
    
    // 计算文本总体高度的一半
    let textMiddleY = startY + 20 + totalTextHeight / 2;
    
    // 检查各站是否有换乘线路
    let hasCurrentInterchange = currentStation.interchangeInfo != null && currentStation.interchangeInfo.length > 0;
    let hasPrevInterchange = prevStation.interchangeInfo != null && prevStation.interchangeInfo.length > 0;
    let hasNextInterchange = nextStation && nextStation.interchangeInfo != null && nextStation.interchangeInfo.length > 0;
    
    // 绘制虚线（按照示范格式）
    g.setColor(Color.GRAY);
    
    // 绘制下一站虚线
    if (hasNextInterchange) {
        x = startX + arrowWidth + 30 + maxTextWidth + 20;
        y = startY + 30; // 下一站中文名称下方
        g.drawLine(x, y, x + 150, y);
    }
    
    // 绘制本站虚线
    if (hasCurrentInterchange) {
        x = startX + arrowWidth + 30 + maxTextWidth + 20;
        y = startY + 80; // 本站中文名称下方
        g.drawLine(x, y, x + 150, y);
    }
    
    // 绘制上一站虚线
    if (hasPrevInterchange) {
        x = startX + arrowWidth + 30 + maxTextWidth + 20;
        y = startY + 130; // 上一站中文名称下方
        g.drawLine(x, y, x + 150, y);
    }
    
    // 绘制换乘线路
    let interchangeX = startX + arrowWidth + 30 + maxTextWidth + 200; // 与虚线对齐
    
    // 绘制下一站换乘线路
    interchangeX = startX + arrowWidth + 30 + maxTextWidth + 200; // 与虚线对齐
    y = startY + 25; // 与下一站虚线对齐
    if (nextStation && nextStation.interchangeInfo != null && nextStation.interchangeInfo.length > 0) {
        for (let interchangeRoute of nextStation.interchangeInfo) {
            if (interchangeRoute != null) {
                let routeNumber = TextUtil.getExtraParts(interchangeRoute.name).replaceAll(".*\\{([^}]*)}.*", "$1");
                let routeName = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, true) : routeNumber;
                let routeNameEn = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, false) : "Line " + routeNumber;
                
                // 计算线路号方框大小
                font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(14.0);
                fm = g.getFontMetrics(font);
                let routeNameWidth = fm.stringWidth(routeName) + 20; // 增加左右宽度
                let routeNameHeight = 30; // 增加上下高度
                
                // 绘制线路号背景
                g.setColor(interchangeRoute.color);
                g.fillRoundRect(interchangeX, y - 5, routeNameWidth, routeNameHeight, 5, 5);
                
                // 绘制线路号
                g.setColor(Color.WHITE);
                g.setFont(font);
                g.drawString(routeName, interchangeX + 10, y + 8); // 调整文字位置
                
                // 绘制线路号英文
                font = ROBOTO_BOLD.deriveFont(10.0);
                fm = g.getFontMetrics(font);
                g.setFont(font);
                g.drawString(routeNameEn, interchangeX + 10, y + 22); // 调整文字位置
                
                interchangeX += routeNameWidth + 10; // 增加线路间间距
                
                // 确保内容在屏幕范围内
                if (interchangeX > WIDTH - 50) {
                    break;
                }
            }
        }
    }
    
    // 绘制本站换乘线路
    interchangeX = startX + arrowWidth + 30 + maxTextWidth + 200; // 重置x位置
    y = startY + 75; // 与本站虚线对齐
    if (currentStation.interchangeInfo != null && currentStation.interchangeInfo.length > 0) {
        for (let interchangeRoute of currentStation.interchangeInfo) {
            if (interchangeRoute != null) {
                let routeNumber = TextUtil.getExtraParts(interchangeRoute.name).replaceAll(".*\\{([^}]*)}.*", "$1");
                let routeName = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, true) : routeNumber;
                let routeNameEn = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, false) : "Line " + routeNumber;
                
                // 计算线路号方框大小
                font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(16.0);
                fm = g.getFontMetrics(font);
                let routeNameWidth = fm.stringWidth(routeName) + 20; // 增加左右宽度
                let routeNameHeight = 35; // 增加上下高度
                
                // 绘制线路号背景
                g.setColor(interchangeRoute.color);
                g.fillRoundRect(interchangeX, y - 5, routeNameWidth, routeNameHeight, 5, 5);
                
                // 绘制线路号
                g.setColor(Color.WHITE);
                g.setFont(font);
                g.drawString(routeName, interchangeX + 10, y + 10); // 调整文字位置
                
                // 绘制线路号英文
                font = ROBOTO_BOLD.deriveFont(12.0);
                fm = g.getFontMetrics(font);
                g.setFont(font);
                g.drawString(routeNameEn, interchangeX + 10, y + 28); // 调整文字位置
                
                interchangeX += routeNameWidth + 15; // 增加线路间间距
                
                // 确保内容在屏幕范围内
                if (interchangeX > WIDTH - 50) {
                    break;
                }
            }
        }
    }
    
    // 绘制上一站换乘线路（灰色）
    interchangeX = startX + arrowWidth + 30 + maxTextWidth + 200; // 重置x位置
    y = startY + 125; // 与上一站虚线对齐
    if (prevStation.interchangeInfo != null && prevStation.interchangeInfo.length > 0) {
        for (let interchangeRoute of prevStation.interchangeInfo) {
            if (interchangeRoute != null) {
                let routeNumber = TextUtil.getExtraParts(interchangeRoute.name).replaceAll(".*\\{([^}]*)}.*", "$1");
                let routeName = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, true) : routeNumber;
                let routeNameEn = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, false) : "Line " + routeNumber;
                
                // 计算线路号方框大小
                font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(14.0);
                fm = g.getFontMetrics(font);
                let routeNameWidth = fm.stringWidth(routeName) + 20; // 增加左右宽度
                let routeNameHeight = 30; // 增加上下高度
                
                // 绘制线路号背景（灰色）
                g.setColor(Color.GRAY);
                g.fillRoundRect(interchangeX, y - 5, routeNameWidth, routeNameHeight, 5, 5);
                
                // 绘制线路号
                g.setColor(Color.WHITE);
                g.setFont(font);
                g.drawString(routeName, interchangeX + 10, y + 8); // 调整文字位置
                
                // 绘制线路号英文
                font = ROBOTO_BOLD.deriveFont(10.0);
                fm = g.getFontMetrics(font);
                g.setFont(font);
                g.drawString(routeNameEn, interchangeX + 10, y + 22); // 调整文字位置
                
                interchangeX += routeNameWidth + 10; // 增加线路间间距
                
                // 确保内容在屏幕范围内
                if (interchangeX > WIDTH - 50) {
                    break;
                }
            }
        }
    }
}

function drawRightPartFullRouteMap(g, useCjk, rightDoor, routeColor, nextStationIndex, stationInfoList, topScreenHeight, state) {
    let platformCount = stationInfoList.length;
    let x = 50;
    let y = topScreenHeight + HEIGHT / 8;
    
    const totalWidth = WIDTH - 100;
    const stationSpacing = totalWidth / (platformCount - 1);
    
    // 更新动画状态
    state.stationNameTracker.tick();
    state.flashPhase++;
    
    // 绘制线路
    g.setStroke(new BasicStroke(10));
    
    for (let i = 0; i < platformCount - 1; i++) {
        let stationX1 = x + i * stationSpacing;
        let stationX2 = x + (i + 1) * stationSpacing;
        
        let isStation1Gray = i < nextStationIndex;
        let isStation2Gray = (i + 1) < nextStationIndex;
        
        if (isStation1Gray && isStation2Gray) {
            g.setColor(Color.GRAY);
        } else {
            g.setColor(routeColor);
        }
        
        g.drawLine(stationX1, y + 25, stationX2, y + 25);
        
        // 绘制静态箭头（所有区间）
        let arrowCount = 5;
        let arrowSpacing = (stationX2 - stationX1) / (arrowCount + 1);
        
        g.setColor(Color.WHITE);
        for (let j = 1; j <= arrowCount; j++) {
            let arrowX = stationX1 + j * arrowSpacing;
            drawArrow(g, arrowX, y + 25);
        }
        
        // 当前区间箭头动画
        if (i === nextStationIndex - 1) {
            drawArrowAnimation(g, stationX1, stationX2, y + 25, state);
        }
    }
    
    g.setStroke(new BasicStroke(1));
    
    // 绘制车站
    for (let i = 0; i < platformCount; i++) {
        let stationX = x + i * stationSpacing;
        
        // 绘制车站圆圈
        drawStationCircle(g, stationX, y, i, nextStationIndex, routeColor, stationInfoList[i], state);
        
        // 根据交替状态绘制站名和换乘信息
        let namePosition = state.stationNameTracker.stateNow();
        let nameY, interchangeY;
        
        if (namePosition === "above") {
            nameY = y - 50;
            interchangeY = y + 60;
        } else {
            nameY = y + 60;
            interchangeY = y - 50;
        }
        
        // 绘制站名
        let stationInfo = stationInfoList[i];
        let stationNameCjk = TextUtil.getCjkParts(stationInfo.stationName);
        let stationNameNonCjk = TextUtil.getNonCjkParts(stationInfo.stationName);
        
        let font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(16.0);
        let fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk, stationX - fm.stringWidth(stationNameCjk.isEmpty() ? stationNameNonCjk : stationNameCjk) / 2, nameY);
        
        font = ROBOTO_REGULAR.deriveFont(12.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.drawString(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk, stationX - fm.stringWidth(stationNameNonCjk.isEmpty() ? stationNameCjk : stationNameNonCjk) / 2, nameY + 20);
        
        // 绘制换乘信息
        if (stationInfo.interchangeInfo != null && stationInfo.interchangeInfo.length > 0) {
            drawInterchangeInfo(g, stationX, interchangeY, stationInfo.interchangeInfo, i < nextStationIndex, routeColor);
        }
        
        // 下一站标记
        if (i === nextStationIndex) {
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(16.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.BLACK);
            g.drawString("下一站", stationX - fm.stringWidth("下一站") / 2, nameY + 45);
            
            font = ROBOTO_BOLD.deriveFont(12.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.GRAY);
            g.drawString("Next station", stationX - fm.stringWidth("Next station") / 2, nameY + 60);
        }
    }
}

function drawStationCircle(g, x, y, index, nextIndex, routeColor, stationInfo, state) {
    // 外圈
    if (index < nextIndex) {
        g.setColor(Color.GRAY);
    } else if (index === nextIndex) {
        let flashColor = state.flashPhase % 60 < 30 ? Color.GREEN : routeColor;
        g.setColor(flashColor);
    } else {
        g.setColor(routeColor);
    }
    g.fillOval(x - 15, y + 15, 30, 30);
    
    // 内圈
    g.setColor(Color.WHITE);
    g.fillOval(x - 12, y + 18, 24, 24);
    
    // 换乘站圆环效果
    if (stationInfo.interchangeInfo && stationInfo.interchangeInfo.length > 0) {
        drawInterchangeCircle(g, x, y, stationInfo.interchangeInfo);
    }
}

function drawInterchangeCircle(g, x, y, interchangeInfo) {
    let lineCount = interchangeInfo.length;
    let totalAngle = 2 * Math.PI;
    let gapAngle = 0.15;
    let eachAngle = (totalAngle - gapAngle * (lineCount + 1)) / lineCount;
    
    for (let i = 0; i < lineCount; i++) {
        let startAngle = gapAngle + i * (eachAngle + gapAngle);
        let endAngle = startAngle + eachAngle;
        
        g.setColor(interchangeInfo[i].color);
        g.fillArc(x - 10, y + 20, 20, 20, Math.toDegrees(startAngle), Math.toDegrees(eachAngle));
        
        // 绘制三角形标记
        let midAngle = startAngle + eachAngle / 2;
        let triX = x + Math.cos(midAngle) * 8;
        let triY = y + 25 + Math.sin(midAngle) * 8;
        
        let triSize = 4;
        let xPoints = [triX, triX - triSize / 2, triX + triSize / 2];
        let yPoints = [triY - triSize, triY + triSize / 2, triY + triSize / 2];
        g.fillPolygon(xPoints, yPoints, 3);
    }
}

function drawInterchangeInfo(g, stationX, y, interchangeInfo, isGray, routeColor) {
    let font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(12.0);
    let fm = g.getFontMetrics(font);
    
    // 计算总宽度
    let totalWidth = 0;
    for (let interchangeRoute of interchangeInfo) {
        if (interchangeRoute) {
            let routeName = TextUtil.getCjkParts(interchangeRoute.name);
            totalWidth += fm.stringWidth(routeName) + 25;
        }
    }
    
    let startX = stationX - totalWidth / 2;
    let currentX = startX;
    
    for (let interchangeRoute of interchangeInfo) {
        if (interchangeRoute) {
            let routeName = TextUtil.getCjkParts(interchangeRoute.name);
            let routeNameWidth = fm.stringWidth(routeName) + 15;
            let routeNameHeight = 28;
            
            // 绘制线路号背景
            let bgColor = isGray ? Color.GRAY : interchangeRoute.color;
            g.setColor(bgColor);
            g.fillRoundRect(currentX, y - 5, routeNameWidth, routeNameHeight, 5, 5);
            
            // 绘制线路号
            g.setColor(Color.WHITE);
            g.setFont(font);
            g.drawString(routeName, currentX + 8, y + 8);
            
            // 绘制下方箭头（最靠近圆圈的线路）
            g.setColor(bgColor);
            drawArrow(g, currentX + routeNameWidth / 2, y + routeNameHeight + 10);
            
            currentX += routeNameWidth + 8;
        }
    }
}

function drawArrow(g, x, y) {
    let size = 6;
    let xPoints = [x, x - size / 2, x - size / 2];
    let yPoints = [y, y - size / 2, y + size / 2];
    g.fillPolygon(xPoints, yPoints, 3);
}

function drawArrowAnimation(g, startX, endX, y, state) {
    let frame = state.arrowAnimationFrame;
    let arrowCount = 8;
    let spacing = (endX - startX) / (arrowCount + 1);
    
    g.setColor(Color.WHITE);
    for (let i = 1; i <= arrowCount; i++) {
        let arrowX = startX + i * spacing;
        if (i <= frame % (arrowCount + 1)) {
            drawArrow(g, arrowX, y);
        }
    }
    
    state.arrowAnimationFrame++;
}

function drawRightPartCurrentSection(g, useCjk, rightDoor, routeColor, nextStationIndex, stationInfoList, topScreenHeight, state) {
    let stationsToShow = [];
    let indices = [];
    
    if (nextStationIndex > 0) {
        stationsToShow.push(stationInfoList[nextStationIndex - 1]);
        indices.push(nextStationIndex - 1);
    }
    stationsToShow.push(stationInfoList[nextStationIndex]);
    indices.push(nextStationIndex);
    if (nextStationIndex + 1 < stationInfoList.length) {
        stationsToShow.push(stationInfoList[nextStationIndex + 1]);
        indices.push(nextStationIndex + 1);
    }
    
    let platformCount = stationsToShow.length;
    let x = WIDTH / 2 - 150;
    let y = topScreenHeight + HEIGHT / 8;
    let totalWidth = 300;
    let stationSpacing = totalWidth / (platformCount - 1);
    
    state.stationNameTracker.tick();
    state.flashPhase++;
    
    g.setStroke(new BasicStroke(10));
    
    for (let i = 0; i < platformCount - 1; i++) {
        let stationX1 = x + i * stationSpacing;
        let stationX2 = x + (i + 1) * stationSpacing;
        
        let currentIndex = indices[i];
        let nextIndex = indices[i + 1];
        
        if (currentIndex < nextStationIndex && nextIndex < nextStationIndex) {
            g.setColor(Color.GRAY);
        } else {
            g.setColor(routeColor);
        }
        
        g.drawLine(stationX1, y + 25, stationX2, y + 25);
        
        let arrowCount = 3;
        let arrowSpacing = (stationX2 - stationX1) / (arrowCount + 1);
        
        g.setColor(Color.WHITE);
        for (let j = 1; j <= arrowCount; j++) {
            let arrowX = stationX1 + j * arrowSpacing;
            drawArrow(g, arrowX, y + 25);
        }
        
        if (currentIndex === nextStationIndex - 1) {
            drawArrowAnimation(g, stationX1, stationX2, y + 25, state);
        }
    }
    
    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, routeColor, stationsToShow[i], state);
        
        let namePosition = state.stationNameTracker.stateNow();
        let nameY = namePosition === "above" ? y - 50 : y + 60;
        let interchangeY = namePosition === "above" ? y + 60 : y - 50;
        
        let stationInfo = stationsToShow[i];
        let stationNameCjk = TextUtil.getCjkParts(stationInfo.stationName);
        let stationNameNonCjk = TextUtil.getNonCjkParts(stationInfo.stationName);
        
        let font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(18.0);
        let fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(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 (stationInfo.interchangeInfo != null && stationInfo.interchangeInfo.length > 0) {
            drawInterchangeInfo(g, stationX, interchangeY, stationInfo.interchangeInfo, index < nextStationIndex, routeColor);
        }
        
        if (index === nextStationIndex) {
            font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(18.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.BLACK);
            g.drawString("下一站", stationX - fm.stringWidth("下一站") / 2, nameY + 45);
            
            font = ROBOTO_BOLD.deriveFont(14.0);
            fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.GRAY);
            g.drawString("Next station", stationX - fm.stringWidth("Next station") / 2, nameY + 62);
        }
    }
}

function drawRightPartNextStationInfo(g, showCjk, routeColor, nextStation, topScreenHeight, rightDoor, doorOpeningSide, isReversed) {
    let font;
    let fm;
    const startX = 50;
    const startY = topScreenHeight + HEIGHT / 8;
    let x = startX;
    let y = startY;
    
    // 绘制圆圈
    g.setColor(Color.GRAY);
    g.fillOval(x, y, 40, 40);
    g.setColor(Color.YELLOW);
    g.fillOval(x + 5, y + 5, 30, 30);
    
    // 绘制下一站信息
    x += 60;
    y += 15;
    
    // 拆分站名的中文和英文部分
    const stationCjk = TextUtil.getCjkParts(nextStation.stationName);
    const stationNonCjk = TextUtil.getNonCjkParts(nextStation.stationName);
    
    // 绘制 CJK 文字
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(36.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    g.drawString("下一站是" + (stationCjk.isEmpty() ? stationNonCjk : stationCjk) + "。", x, y);
    y += fm.getDescent() + 20; // 调整行间距，使文字靠近线路框中心
    
    // 绘制非 CJK 文字
    font = ROBOTO_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString("Next station is " + (stationNonCjk.isEmpty() ? stationCjk : stationNonCjk) + ".", x, y);
    y += fm.getDescent() + 25; // 调整行间距，使文字靠近线路框中心
    
    // 绘制换乘信息
    if (nextStation.interchangeInfo != null && nextStation.interchangeInfo.length > 0) {
        // 第一行：可换乘[带颜色方框的线路名称]
        font = SOURCE_HAN_SANS_CN_REGULAR.deriveFont(20.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString("可换乘", x, y);
        let cjkX = x + fm.stringWidth("可换乘") + 40; // 增加间距，远离英文文字
        
        for (let i = 0; i < nextStation.interchangeInfo.length; i++) {
            let interchangeRoute = nextStation.interchangeInfo[i];
            if (interchangeRoute != null) {
                let routeNumber = TextUtil.getExtraParts(interchangeRoute.name).replaceAll(".*\\{([^}]*)}.*", "$1");
                let routeName = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, true) : routeNumber;
                let routeNameEn = (routeNumber == "" || routeNumber == TextUtil.getExtraParts(interchangeRoute.name)) ? getMatching(interchangeRoute.name, false) : "Line " + routeNumber;
                
                // 计算线路号方框大小
                font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(16.0);
                fm = g.getFontMetrics(font);
                let routeNameWidth = fm.stringWidth(routeName) + 25; // 增加左右宽度
                let routeNameHeight = 40; // 增加上下高度
                
                // 绘制线路号背景
                g.setColor(interchangeRoute.color);
                g.fillRoundRect(cjkX, y - 5, routeNameWidth, routeNameHeight, 5, 5);
                
                // 绘制线路号
                g.setColor(Color.WHITE);
                g.setFont(font);
                g.drawString(routeName, cjkX + 12, y + 10); // 调整文字位置
                
                // 绘制线路号英文
                font = ROBOTO_BOLD.deriveFont(12.0);
                fm = g.getFontMetrics(font);
                g.setFont(font);
                g.drawString(routeNameEn, cjkX + 12, y + 30); // 调整文字位置
                
                cjkX += routeNameWidth + 15; // 增加线路间间距
                
                // 确保内容在屏幕范围内
                if (cjkX > WIDTH - 50) {
                    break;
                }
            }
        }
        y += 20; // 减小行间距，使"可换乘"更靠近"Transfer to"
        
        // 第二行：Transfer to
        font = ROBOTO_REGULAR.deriveFont(16.0);
        fm = g.getFontMetrics(font);
        g.setFont(font);
        g.setColor(Color.GRAY);
        g.drawString("Transfer to", startX + 60, y);
}

function drawRightPartReturningToDepot(g, showCjk, routeColor, topScreenHeight) {
    let font;
    let fm;
    const startX = 50;
    const startY = topScreenHeight + HEIGHT / 8;
    let x = startX;
    let y = startY;
    
    // 绘制黄色圆圈
    g.setColor(Color.GRAY);
    g.fillOval(x, y, 60, 60);
    g.setColor(Color.YELLOW);
    g.fillOval(x + 5, y + 5, 50, 50);
    
    // 绘制回库信息
    x += 80;
    y += 20;
    
    // 绘制 CJK 文字
    font = SOURCE_HAN_SANS_CN_BOLD.deriveFont(36.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.BLACK);
    g.drawString("本次列车退出服务，请全部下车。", x, y);
    y += fm.getDescent() + 15;
    
    // 绘制非 CJK 文字
    font = ROBOTO_BOLD.deriveFont(24.0);
    fm = g.getFontMetrics(font);
    g.setFont(font);
    g.setColor(Color.GRAY);
    g.drawString("This train is out of service. Please get off.", x, y);
}


/**
 * 绘制路线名称。
 *
 * @param x            当 vertical 为 false 时，表示路线框顶点的 x 坐标；当 vertical 为 true 时，表示路线框上边沿中点的 x 坐标。当 right 为 false 时，表示左上顶点；当 right 为 true 时，表示右上顶点。
 * @param y            路线框顶点的 y 坐标。当 right 为 false 时，表示左上顶点；当 right 为 true 时，表示右下顶点。
 * @param width        路线框的最大宽度。传入 0 则不限制。
 * @param height       路线框的最大高度。传入 0 则不限制。
 * @param criticalX    当 right 为 false 时，如果 x 与本路线框宽度之和大于此值，则不绘制并返回 0；当 right 为 true 时，如果 x 与本路线框宽度之差小于此值，则不绘制并返回 0。仅在 vertical 为 false 时有效。
 * @param vertical     是否处于纵向排版模式。主要影响形参 x 的处理和返回值。
 * @param right        是否处于右对齐排版模式。主要影响形参 x、y 和 criticalX 的处理。
 * @param outline      是否绘制空心矩形。
 * @param outlineColor 空心矩形的路线字体颜色。
 * @return 当 vertical 为 false 时，返回路线框的宽度（可能为 0，表示已绘制不下更多路线信息）；当 vertical 为 true 时，返回路线框的高度。
 */
function drawRouteName(g, x, y, width, height, criticalX, routeName, routeColor, vertical, right, outline, outlineColor) {
    let font = (TextUtil.isCjk(routeName) ? SOURCE_HAN_SANS_CN_BOLD : ROBOTO_BOLD).deriveFont(calculateMaxFontSize(g, TextUtil.isCjk(routeName) ? SOURCE_HAN_SANS_CN_BOLD : ROBOTO_BOLD, routeName, (!vertical && width > HORIZONTAL_SPACING) ? width - HORIZONTAL_SPACING : width, (!vertical && height > HORIZONTAL_SPACING) ? height - VERTICAL_SPACING : height, 0, 0, false));
    let fm = g.getFontMetrics(font);
    height = height == 0 ? fm.getHeight() : height;
    width = width == 0 ? Math.max(fm.stringWidth(routeName) + 10, height) : Math.min(Math.max(fm.stringWidth(routeName) + 10, height), width);

    if (!vertical && (right ? x - width < criticalX : x + width > criticalX)) {
        return 0;
    }

    x = vertical ? x - width / 2 : x;
    x = right ? x - width : x;
    y = right ? y - height : y;

    // 画矩形
    g.setColor(routeColor);
    if (outline) {
        g.drawRoundRect(x, y, width, height, 5, 5);
    } else {
        g.fillRoundRect(x, y, width, height, 5, 5);
    }

    // 在矩形中央画文字
    g.setColor(outline ? outlineColor : isLightColor(routeColor) ? Color.BLACK : Color.WHITE);
    g.setFont(font);
    x += (width - fm.stringWidth(routeName)) / 2;
    y += (height - fm.getHeight()) / 2 + fm.getAscent();
    g.drawString(routeName, x, y);

    return vertical ? height : width;
}
}