importPackage(java.awt);

const ft = Resources.idr("fonts/source-han-sans-cn/source-han-sans-cn-regular.otf");

function getSidingName(train) {
    let siding = train.siding();
    if (!siding) {
        return "";
    }
    let sidingName = siding.name;
    return sidingName && sidingName !== "" ? sidingName : "";
}

function getCarNumber(train, carIndex) {
    let sidingName = getSidingName(train);
    if (!sidingName) {
        return null;
    }
    return sidingName + (carIndex + 1);
}

function create(ctx, state, train) {
    state.carNumberModelsFirst = null;
    state.carNumberModelsLast = null;
    state.carNumberModelsMiddle = [];
    state.sidingNumberModelFirst = null;
    state.sidingNumberModelLast = null;
    state.lastCarCount = 0;
    state.lastSidingName = "";
}

function dispose(ctx, state, train) {
    if (state.carNumberModelsFirst) {
        state.carNumberModelsFirst.left.texture.close();
        state.carNumberModelsFirst.right.texture.close();
    }
    if (state.carNumberModelsLast) {
        state.carNumberModelsLast.left.texture.close();
        state.carNumberModelsLast.right.texture.close();
    }
    for (let i = 0; i < state.carNumberModelsMiddle.length; i++) {
        let model = state.carNumberModelsMiddle[i];
        if (model) {
            model.left.texture.close();
            model.right.texture.close();
        }
    }
    state.carNumberModelsMiddle = [];
    if (state.sidingNumberModelFirst) {
        state.sidingNumberModelFirst.texture.close();
    }
    if (state.sidingNumberModelLast) {
        state.sidingNumberModelLast.texture.close();
    }
}

function createCarNumberModel(carNumber) {
    if (!carNumber) {
        return null;
    }
    let fontSize = 12;

    let font = Resources.readFont(ft).deriveFont(fontSize);
    let tempG = new GraphicsTexture(1, 1).graphics;
    let fm = tempG.getFontMetrics(font);
    let textWidth = fm.stringWidth(carNumber);
    let texWidth = Math.max(textWidth + 10, 50);
    let texHeight = 30;
    let modelWidth = texWidth / 100;
    let modelHeight = texHeight / 100;
    let texture = new GraphicsTexture(texWidth, texHeight);
    let g = texture.graphics;
    g.setFont(font);
    g.setColor(Color.WHITE);
    let textY = (texHeight + fm.getAscent() - fm.getDescent()) / 2;
    g.drawString(carNumber, 5, textY);
    texture.upload();
    let rawModel = new RawModel();
    let rawModelBuilder = new RawMeshBuilder(4, "exterior", texture.identifier);
    let uvWidth = texWidth / texWidth;
    let uvHeight = texHeight / texHeight;
    rawModelBuilder.vertex(modelWidth / 2, modelHeight / 2, 0).normal(0, 0, 1).uv(uvWidth, 0).endVertex()
        .vertex(-modelWidth / 2, modelHeight / 2, 0).normal(0, 0, 1).uv(0, 0).endVertex()
        .vertex(-modelWidth / 2, -modelHeight / 2, 0).normal(0, 0, 1).uv(0, uvHeight).endVertex()
        .vertex(modelWidth / 2, -modelHeight / 2, 0).normal(0, 0, 1).uv(uvWidth, uvHeight).endVertex();
    rawModel.append(rawModelBuilder.getMesh());
    rawModel.generateNormals();
    let dynamicModelHolder = new DynamicModelHolder();
    dynamicModelHolder.uploadLater(rawModel);
    return { texture: texture, model: dynamicModelHolder };
}

function createSidingNumberModel(sidingName) {
    if (!sidingName) {
        return null;
    }
    let fontSize = 40;
    let font = Resources.getSystemFont("Noto Sans").deriveFont(fontSize);
    let tempG = new GraphicsTexture(1, 1).graphics;
    let fm = tempG.getFontMetrics(font);
    let textWidth = fm.stringWidth(sidingName);
    let texWidth = Math.max(textWidth + 10, 40);
    let texHeight = 43;
    let modelWidth = texWidth / 100;
    let modelHeight = texHeight / 100;
    let texture = new GraphicsTexture(texWidth, texHeight);
    let g = texture.graphics;
    g.setFont(font);
    g.setColor(Color.BLUE);
    let textY = ((texHeight + fm.getAscent() - fm.getDescent()) / 2) + 3;
    g.drawString(sidingName, 5, textY);
    texture.upload();
    let rawModel = new RawModel();
    let rawModelBuilder = new RawMeshBuilder(4, "exterior", texture.identifier);
    let uvWidth = texWidth / texWidth;
    let uvHeight = texHeight / texHeight;
    rawModelBuilder.vertex(modelWidth / 2, modelHeight / 2, 0).normal(0, 0, 1).uv(uvWidth, 0).endVertex()
        .vertex(-modelWidth / 2, modelHeight / 2, 0).normal(0, 0, 1).uv(0, 0).endVertex()
        .vertex(-modelWidth / 2, -modelHeight / 2, 0).normal(0, 0, 1).uv(0, uvHeight).endVertex()
        .vertex(modelWidth / 2, -modelHeight / 2, 0).normal(0, 0, 1).uv(uvWidth, uvHeight).endVertex();
    rawModel.append(rawModelBuilder.getMesh());
    rawModel.generateNormals();
    let dynamicModelHolder = new DynamicModelHolder();
    dynamicModelHolder.uploadLater(rawModel);
    return { texture: texture, model: dynamicModelHolder };
}

function render(ctx, state, train) {
    let carCount = train.trainCars();
    let sidingName = getSidingName(train);
    
    if (state.lastCarCount !== carCount || state.lastSidingName !== sidingName) {
        dispose(ctx, state, train);
        
        let firstCarNumber = getCarNumber(train, 0);
        state.carNumberModelsFirst = {
            left: createCarNumberModel(firstCarNumber),
            right: createCarNumberModel(firstCarNumber)
        };
        
        if (carCount > 1) {
            let lastCarNumber = getCarNumber(train, carCount - 1);
            state.carNumberModelsLast = {
                left: createCarNumberModel(lastCarNumber),
                right: createCarNumberModel(lastCarNumber)
            };
        }
        
        if (carCount > 2) {
            for (let i = 1; i < carCount - 1; i++) {
                let middleCarNumber = getCarNumber(train, i);
                state.carNumberModelsMiddle[i - 1] = {
                    left: createCarNumberModel(middleCarNumber),
                    right: createCarNumberModel(middleCarNumber)
                };
            }
        }
        
        state.sidingNumberModelFirst = createSidingNumberModel(sidingName);
        state.sidingNumberModelLast = createSidingNumberModel(sidingName);
        
        state.lastCarCount = carCount;
        state.lastSidingName = sidingName;
    }
    
    let matricesLeftFront = new Matrices();
    matricesLeftFront.translate(-1.43, 0.756, 7.8);
    matricesLeftFront.rotateY(3 * Math.PI / 2);

    let matricesLeftHeadFront = new Matrices();
    matricesLeftHeadFront.translate(-1.43, 0.756, 4.8);
    matricesLeftHeadFront.rotateY(3 * Math.PI / 2);
    
    let matricesLeftBack = new Matrices();
    matricesLeftBack.translate(-1.43, 0.756, -7.8);
    matricesLeftBack.rotateY(3 * Math.PI / 2);

    let matricesLeftHeadBack = new Matrices();
    matricesLeftHeadBack.translate(-1.43, 0.756, -4.8);
    matricesLeftHeadBack.rotateY(3 * Math.PI / 2);
    
    let matricesRightFront = new Matrices();
    matricesRightFront.translate(1.43, 0.756, 7.8);
    matricesRightFront.rotateY(-3 * Math.PI / 2);

    let matricesRightHeadFront = new Matrices();
    matricesRightHeadFront.translate(1.43, 0.756, 4.8);
    matricesRightHeadFront.rotateY(-3 * Math.PI / 2);
    
    let matricesRightBack = new Matrices();
    matricesRightBack.translate(1.43, 0.756, -7.8);
    matricesRightBack.rotateY(-3 * Math.PI / 2);

    let matricesRightHeadBack = new Matrices();
    matricesRightHeadBack.translate(1.43, 0.756, -4.8);
    matricesRightHeadBack.rotateY(-3 * Math.PI / 2);
    
    let matricesSiding = new Matrices();
    matricesSiding.translate(0, 0.5, 9.86);
    matricesSiding.rotateX(Math.PI / -7.2);
    
    let matricesSidingFlipped = new Matrices();
    matricesSidingFlipped.translate(0, 0.5, -9.86);
    matricesSidingFlipped.rotateY(Math.PI);
    matricesSidingFlipped.rotateX(Math.PI / -7.2);
    
    // === 第一节车厢（车头）===
    if (state.carNumberModelsFirst) {
        // 左侧两个车厢号（前后排列，逆时针旋转270度）
        if (state.carNumberModelsFirst.left && state.carNumberModelsFirst.left.model) {
            ctx.drawCarModel(state.carNumberModelsFirst.left.model, 0, matricesLeftHeadFront);
            ctx.drawCarModel(state.carNumberModelsFirst.left.model, 0, matricesLeftBack);
            
        }
        // 右侧两个车厢号（前后排列，顺时针旋转270度）
        if (state.carNumberModelsFirst.right && state.carNumberModelsFirst.right.model) {
            ctx.drawCarModel(state.carNumberModelsFirst.right.model, 0, matricesRightHeadFront);
            ctx.drawCarModel(state.carNumberModelsFirst.right.model, 0, matricesRightBack);
        }
    }
    
    // 第一节车厢侧线号（不旋转，蓝色大号字体）
    if (state.sidingNumberModelFirst && state.sidingNumberModelFirst.model) {
        ctx.drawCarModel(state.sidingNumberModelFirst.model, 0, matricesSiding);
    }
    
    // === 中间车厢 ===
    if (carCount > 2) {
        for (let i = 1; i < carCount - 1; i++) {
            let model = state.carNumberModelsMiddle[i - 1];
            if (model) {
                // 左侧两个车厢号（前后排列，逆时针旋转270度）
                if (model.left && model.left.model) {
                    ctx.drawCarModel(model.left.model, i, matricesLeftFront);
                    ctx.drawCarModel(model.left.model, i, matricesLeftBack);
                }
                // 右侧两个车厢号（前后排列，顺时针旋转270度）
                if (model.right && model.right.model) {
                    ctx.drawCarModel(model.right.model, i, matricesRightFront);
                    ctx.drawCarModel(model.right.model, i, matricesRightBack);
                }
            }
        }
    }
    
    // === 最后一节车厢（车尾）===
    if (state.carNumberModelsLast && carCount > 1) {
        let lastCarIndex = carCount - 1;
        // 左侧两个车厢号（前后排列，逆时针旋转270度）
        if (state.carNumberModelsLast.left && state.carNumberModelsLast.left.model) {
            ctx.drawCarModel(state.carNumberModelsLast.left.model, lastCarIndex, matricesLeftFront);
            ctx.drawCarModel(state.carNumberModelsLast.left.model, lastCarIndex, matricesLeftHeadBack);
        }
        // 右侧两个车厢号（前后排列，顺时针旋转270度）
        if (state.carNumberModelsLast.right && state.carNumberModelsLast.right.model) {
            ctx.drawCarModel(state.carNumberModelsLast.right.model, lastCarIndex, matricesRightFront);
            ctx.drawCarModel(state.carNumberModelsLast.right.model, lastCarIndex, matricesRightHeadBack);
        }
        
        // 最后一节车厢侧线号（翻转显示，蓝色大号字体）
        if (state.sidingNumberModelLast && state.sidingNumberModelLast.model) {
            ctx.drawCarModel(state.sidingNumberModelLast.model, lastCarIndex, matricesSidingFlipped);
        }
    }
}