var renderClass = "jp.ngt.rtm.render.MachinePartsRenderer";
importPackage(Packages.org.lwjgl.opengl);
importPackage(Packages.jp.ngt.ngtlib.renderer);
importPackage(Packages.jp.ngt.rtm.render);

importPackage(Packages.net.minecraft.tileentity);
importPackage(Packages.jp.ngt.ngtlib.io);
importPackage(Packages.jp.ngt.ngtlib.util);
importPackage(Packages.net.minecraft.command.server);
importPackage(Packages.net.minecraft.init);
importClass(java.util.HashMap);
importPackage(Packages.jp.ngt.rtm.electric);

importPackage(Packages.jp.ngt.rtm);
importPackage(Packages.jp.ngt.rtm.modelpack);
importPackage(Packages.net.minecraft.util.math);  // BlockPos (1.12.2)

// --- Java NIO 導入 (用於優化檔案讀取) ---
var Files = Java.type("java.nio.file.Files");
var Paths = Java.type("java.nio.file.Paths");
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets");
var FileTime = Java.type("java.nio.file.attribute.FileTime");
var NBTTagCompound = Java.type("net.minecraft.nbt.NBTTagCompound");
var Base64 = Java.type("java.util.Base64");

var tessellator = NGTTessellator.instance;

// --- 全域變數 (改為 HashMap 緩存，避免變數污染) ---
var EntityCache = new HashMap(); // 用於儲存每個方塊的獨立數據
// 以下舊的全域變數保留定義但不使用，以防有其他依賴，實際上已被 Cache 取代
var ErrorData = new HashMap(); 
var lastFilePath = null; 
var lastModifiedTime = null; 
var Timetable = null; 

function init(par1, par2) {
    base = renderer.registerParts(new Parts("body", "time_led_0"));
    trianLed = renderer.registerParts(new Parts("train_led"));
    timeColon = renderer.registerParts(new Parts("time_led_1"));
    mat2 = renderer.registerParts(new Parts("mat2"));

    hour_tens = registerDigitParts("hour_tens");
    hour_units = registerDigitParts("hour_units");
    minute_tens = registerDigitParts("minute_tens");
    minute_units = registerDigitParts("minute_units");

    approachingLight = [
        [renderer.registerParts(new Parts("approachingLight_1_0")), renderer.registerParts(new Parts("approachingLight_1_1"))],
        [renderer.registerParts(new Parts("approachingLight_2_0")), renderer.registerParts(new Parts("approachingLight_2_1"))],
        [renderer.registerParts(new Parts("approachingLight_3_0")), renderer.registerParts(new Parts("approachingLight_3_1"))]
    ];

    platforms = {};
    var pList = ["1", "1a", "1b", "2", "2a", "2b", "3", "3a", "3b", "4", "4a", "4b"];
    for (var i in pList) {
        platforms[pList[i]] = renderer.registerParts(new Parts("Plat_" + pList[i]));
    }

    displayBoardPorperty = {
        size : renderer.getModelName().indexOf("small") !== -1 ? "Small" : "Large",
        side : renderer.getModelName().indexOf("_L") !== -1 ? "Left" : "Right",
        doubleSide : renderer.getModelName().indexOf("_Double") !== -1 ? true : false
    };
}

// 輔助註冊函數
function registerDigitParts(name) {
    var arr = [];
    for (var i = 0; i <= 9; i++) {
        arr.push(renderer.registerParts(new Parts(name + "_" + i)));
    }
    arr.push(renderer.registerParts(new Parts(name))); 
    return arr;
}

// 取得該 Entity 的專屬緩存
function getCache(entity) {
    var data = EntityCache.get(entity);
    if (data === null) {
        data = {
            lastCheckTime: 0,
            settings: null,         // JSON 解析後的設定
            timetable: [],          // JSON 解析後的時刻表
            timetableTimes: [],
            lastFilePath: null,     // 上次讀取的檔案路徑
            lastFileModified: null, // 上次檔案修改時間
            commandStr: null,       // 上次讀取的指令字串
            lastTimeRenderUpdate: 0,
            digits: null,
            digitsMC: null,
            totalSec: 0,
            visibleRows: [],
            visibleRowsLastSec: -1,
            visibleRowsDelayTime: -1,
            // 邏輯狀態
            isError: false,
            errorInfo: "",
            errorDrawn: false,
            
            platform: "1",
            timeZone: 0,
            isRealTime: false,
            
            approachingState: [false, false, false],
            isApproaching: false,
            approachLevel: [0, 0, 0],
            
            isDelaying: false,
            delayTime: 0
        };
        EntityCache.put(entity, data);
    }
    return data;
}

function updateTimeRenderCache(cache, currentTime) {
    if (cache.digits === null || currentTime - cache.lastTimeRenderUpdate > 200) {
        var MCTime = (renderer.getMCTime() + 6000) % 24000 * 3.6;
        cache.digits = getTimeDigits(cache.timeZone);
        cache.digitsMC = convertSecondsToHMS(MCTime);
        cache.totalSec = cache.isRealTime ? cache.digits.totalSec : MCTime;
        cache.lastTimeRenderUpdate = currentTime;
    }
}

function clearTimetable(cache) {
    cache.timetable = [];
    cache.timetableTimes = [];
    cache.visibleRows = [];
    cache.visibleRowsLastSec = -1;
    cache.visibleRowsDelayTime = -1;
}

function setTimetable(cache, list) {
    clearTimetable(cache);
    if (!list) return;

    for (var i = 0; i < list.length; i++) {
        var entry = list[i];
        if (!isValidTimetableEntry(entry)) continue;

        cache.timetable.push(entry);
        cache.timetableTimes.push(decordTime(entry[4]));
    }
}

function updateVisibleRowsCache(cache, totalSec) {
    var totalSecInt = Math.floor(totalSec);
    if (cache.visibleRowsLastSec === totalSecInt && cache.visibleRowsDelayTime === cache.delayTime) return;

    cache.visibleRows = [];
    cache.visibleRowsLastSec = totalSecInt;
    cache.visibleRowsDelayTime = cache.delayTime;

    if (!cache.timetable || cache.timetable.length <= 0) return;

    var offsetTime = 20 * 3600;
    var currentOffsetSec = (totalSec + offsetTime) % 86400;

    for (var tableIdx = 0; tableIdx < cache.timetable.length; tableIdx++) {
        var entry = cache.timetable[tableIdx];
        var trainTime = cache.timetableTimes[tableIdx];
        var trainOffsetSec = (trainTime + offsetTime) % 86400;

        var isFutureTrain = (trainOffsetSec >= currentOffsetSec);
        var isDelayedTrain = (trainOffsetSec < currentOffsetSec) && !isPassedFlag(entry[6]);

        if (isFutureTrain || isDelayedTrain) {
            var delaySec = isDelayedTrain ? currentOffsetSec - trainOffsetSec : 0;
            cache.visibleRows.push({
                entry: entry,
                isDelayed: isDelayedTrain,
                delayMin: Math.floor(delaySec / 60) + 1,
                trainTime: trainTime
            });

            if (cache.visibleRows.length >= 3) break;
        }
    }
}

function render(entity, pass, par3) {
    if (entity === null) return;

    // 1. 取得緩存
    var cache = getCache(entity);

    // 2. 邏輯更新 (每 1000ms 執行一次，避免每幀 I/O 造成卡頓)
    var currentTime = java.lang.System.currentTimeMillis();
    if (currentTime - cache.lastCheckTime > 1000) {
        updateEntityLogic(entity, cache);
        cache.lastCheckTime = currentTime;
    }

    // 3. 渲染處理 (每幀執行，但只讀取緩存數據)
    if (0 <= pass && pass <= 2) {
        GL11.glPushMatrix();
        base.render(renderer);
        trianLed.render(renderer);
        GL11.glPopMatrix();

        // 錯誤顯示邏輯
        if (cache.isError) {
            if (!cache.errorDrawn) {
                var player = NGTUtil.getClientPlayer();
                var pos = [Math.floor(entity.field_145851_c), Math.floor(entity.field_145848_d), Math.floor(entity.field_145849_e)];
                NGTLog.sendChatMessage(player, "§b[EditableDepSign] §c錯誤發生");
                NGTLog.sendChatMessage(player, "Pos:" + pos.join(", "));
                NGTLog.sendChatMessage(player, "Info:§e" + cache.errorInfo);
                cache.errorDrawn = true;
            }
        } else {
            cache.errorDrawn = false;
        }

        if (cache.settings === null) return;

        try {
            var PlatSelect = cache.platform;

            updateTimeRenderCache(cache, currentTime);
            var digits = cache.digits;
            var showDigits = cache.isRealTime ? cache.digits : cache.digitsMC;
            var totalSec = cache.totalSec;

            GL11.glPushMatrix();
            if (platforms[PlatSelect]) platforms[PlatSelect].render(renderer);
            
            // 渲染時間
            hour_tens[showDigits.hourTens].render(renderer);
            hour_units[showDigits.hourOnes].render(renderer);
            minute_tens[showDigits.minuteTens].render(renderer);
            minute_units[showDigits.minuteOnes].render(renderer);

            if (digits.secondOnes % 2 == 0) timeColon.render(renderer);
            
            if (cache.isApproaching) {
                var flash = (digits.milliseconds > 500) ? 1 : 0;
                if (cache.approachingState[0]) approachingLight[0][flash].render(renderer);
                if (cache.approachingState[1]) approachingLight[1][flash].render(renderer);
                if (cache.approachingState[2]) approachingLight[2][flash].render(renderer);
            }
            GL11.glPopMatrix();

            // 渲染時刻表列表
            GL11.glPushMatrix();
            mat2.render(renderer);
            
            var matId = renderer.currentMatId;

            if (matId == 1) {
                updateVisibleRowsCache(cache, totalSec);
                for (var rowCount = 0; rowCount < cache.visibleRows.length; rowCount++) {
                    var row = cache.visibleRows[rowCount];
                    panelRender(cache.settings, row.entry, row.isDelayed, row.delayMin, row.trainTime, totalSec, rowCount, matId, renderer, digits);
                }
            }
            GL11.glPopMatrix();

        } catch (e) {
            cache.isError = true;
            cache.errorInfo = "Render Error: " + e;
        }
    }
}

// --- 邏輯更新函數 ---
function toCommandString(value) {
    if (value === null || value === undefined) return null;
    var command = String(value);
    return command === "" ? null : command;
}

function decodeSyncedCommand(value) {
    if (value === null || value === undefined || value === "") return null;
    try {
        var bytes = Base64.getDecoder().decode(String(value));
        return toCommandString(new java.lang.String(bytes, StandardCharsets.UTF_8));
    } catch (e) {
        return null;
    }
}

function readSyncedCommand(entity) {
    try {
        var dataMap = entity.getResourceState().getDataMap();
        return decodeSyncedCommand(dataMap.getString("trapidsCommandB64"));
    } catch (e) {
        return null;
    }
}

function readCommandFromNBT(nbt) {
    var command = null;
    try { command = toCommandString(nbt.func_74779_i("Command")); } catch (e) {}
    if (command !== null) return command;
    try { command = toCommandString(nbt.getString("Command")); } catch (e) {}
    return command;
}

function readCommandBlockCommand(commandBlock) {
    if (commandBlock === null) return null;

    var logic = null;
    try { logic = commandBlock.func_145993_a(); } catch (e) {}
    if (logic === null) return null;

    var command = null;
    try { command = toCommandString(logic.func_145753_i()); } catch (e) {}
    if (command !== null) return command;

    try {
        var version = RTMCore.VERSION;
        if (version.indexOf("1.7.10") != -1){
            command = NGTUtil.getField(CommandBlockLogic.class, logic, ["Command", "field_145763_e"]);
        }else{
            command = NGTUtil.getField(CommandBlockBaseLogic.class, logic, ["Command", "field_145763_e"]);
        }
        command = toCommandString(command);
    } catch (e) {
        command = null;
    }
    if (command !== null) return command;

    try {
        var nbt = new NBTTagCompound();
        try {
            logic.func_145759_b(nbt);
        } catch (e1) {
            commandBlock.func_145841_b(nbt);
        }
        command = readCommandFromNBT(nbt);
    } catch (e) {
        command = null;
    }
    return command;
}

function updateEntityLogic(entity, cache) {
    try {
        // 1. 獲取指令方塊並解析設定
        var commandStr = readSyncedCommand(entity);
        if (commandStr === null || commandStr === "") {
            var commandBlock = searchTileEntity(entity, TileEntityCommandBlock);
            commandStr = readCommandBlockCommand(commandBlock);
        }

        if (commandStr === null || commandStr === "") {
            cache.settings = null;
            return;
        }

        if (commandStr !== cache.commandStr) {
            cache.commandStr = commandStr;
            try {
                cache.settings = JSON.parse(commandStr);
                cache.isError = false;
                
                if (cache.settings.setting) {
                    cache.timeZone = cache.settings.setting.timeZone !== undefined ? parseInt(cache.settings.setting.timeZone) : 0;
                    cache.isRealTime = !!cache.settings.setting.realTime;
                    var p = cache.settings.setting.platform || "1";
                    cache.platform = (typeof(p) === "string") ? p.toLowerCase() : "1";
                    cache.lastTimeRenderUpdate = 0;
                    cache.visibleRowsLastSec = -1;
                }
            } catch (e) {
                cache.isError = true;
                cache.errorInfo = "JSON Parse Error";
                return;
            }
        }

        if (!cache.settings || !cache.settings.setting) {
            cache.isError = true;
            cache.errorInfo = "No 'setting' in JSON";
            return;
        }

        // 2. 檔案讀取
        var filePath = cache.settings.setting.filePath;
        if (filePath) {
            try {
                var path = Paths.get(filePath);
                if (Files.exists(path)) {
                    var currentModified = Files.getLastModifiedTime(path);
                    
                    if (cache.lastFilePath !== filePath || 
                        cache.lastFileModified === null || 
                        currentModified.compareTo(cache.lastFileModified) > 0) {
                        
                        var jsonString = new java.lang.String(Files.readAllBytes(path), StandardCharsets.UTF_8);
                        var jsonObject = JSON.parse(jsonString);
                        setTimetable(cache, jsonObject.list || []);
                        
                        cache.lastFilePath = filePath;
                        cache.lastFileModified = currentModified;
                        cache.isError = false;
                    }
                } else {
                    clearTimetable(cache);
                    cache.lastFilePath = filePath;
                    cache.lastFileModified = null;
                    cache.isError = true;
                    cache.errorInfo = "File Not Found";
                }
            } catch (e) {
                clearTimetable(cache);
                cache.isError = true;
                cache.errorInfo = "File Load Error";
            }
        } else {
            clearTimetable(cache);
            cache.lastFilePath = null;
            cache.lastFileModified = null;
            cache.isError = false;
        }

        // 3. 搜尋周圍方塊
        var BlockData = Blocks.field_150399_cn;
        var appRes = searchBlockAndMetaTriple(entity, BlockData, null);
        cache.approachingState = [
            (appRes[0][0] !== null && appRes[0][1] >= 1),
            (appRes[1][0] !== null && appRes[1][1] >= 1),
            (appRes[2][0] !== null && appRes[2][1] >= 1)
        ];
        cache.isApproaching = cache.approachingState[0] || cache.approachingState[1] || cache.approachingState[2];

        var delayBlockData = Blocks.field_150325_L;
        var delayRes = searchBlockAndMeta(entity, delayBlockData, null);
        cache.isDelaying = (delayRes[0] !== null);
        
        if (cache.isDelaying) {
            switch (delayRes[1]) {
                case 0: cache.delayTime = 3 * 60; break;
                case 1: cache.delayTime = 5 * 60; break;
                case 2: cache.delayTime = 10 * 60; break;
                case 3: cache.delayTime = 30 * 60; break;
                default: cache.delayTime = 0;
            }
        } else {
            cache.delayTime = 0;
        }

    } catch (e) {
        cache.isError = true;
        cache.errorInfo = "Update Logic Error: " + e;
    }
}

function decordTime(time) {
    var t = String(time).split(":");
    if (t.length !== 3) return 0;
    var hh = parseInt(t[0], 10);
    var mm = parseInt(t[1], 10);
    var ss = parseInt(t[2], 10);
    if (isNaN(hh) || isNaN(mm) || isNaN(ss)) return 0;
    return (hh * 3600 + mm * 60 + ss) % 86400;
}

function isValidTimetableEntry(entry) {
    if (!entry || entry.length < 7) return false;
    if (entry[0] === null || entry[1] === null || entry[2] === null || entry[3] === null || entry[4] === null || entry[5] === null) return false;

    var t = String(entry[4]).split(":");
    if (t.length !== 3) return false;
    var hh = parseInt(t[0], 10);
    var mm = parseInt(t[1], 10);
    var ss = parseInt(t[2], 10);
    return !(isNaN(hh) || isNaN(mm) || isNaN(ss));
}

function isPassedFlag(value) {
    return value === true || String(value).toLowerCase() === "true";
}

// 注意：這裡最後一個參數改為 digits (時間物件)，以避免在內部重複計算
function panelRender(cb, Timetable, isDelaying, delayTime, trainTime, totalSec, rowCount, matId, renderer, digits) {
    
    if(matId !== 1)return;
    var offsetX =           0.0;
    var offsetU =           0;
    var RotateCenterX =     0;    
    // 優化關鍵：直接使用傳入的 digits，不再呼叫 getTimeDigits
    // 這消除了迴圈內大量的 new Date() 開銷
    var isEnglishDisplaying = ( digits.secondOnes > 4 );
    
    var trainInformation = {
        trainNumber :       String(Timetable[0]),
        trainDestination :  String(Timetable[1]),
        trainLine :         String(Timetable[2]),
        trainType :         String(Timetable[3]),
        trainDepTime :      String(Timetable[4]),
        trainArrPlat :      String(Timetable[5])
    }
    var fixedTrainTime =    parseAndRoundTime(trainInformation.trainDepTime);
    var totalUV =           [2500, 2500];
    var trainArrPlat =      trainInformation.trainArrPlat.split("");
    
    if(isEnglishDisplaying) offsetU = 819; else offsetU = 0;
    switch (displayBoardPorperty.side) {
        case "Right":
            offsetX = 0.0;
            RotateCenterX = 0.14;
            break;
        case "Left":
            offsetX = -0.28;
            RotateCenterX = -0.14;
            break;
        default:
            break;
    }
    var 
        trainNumberLength = 13,
        delayNumberLength_English = 10,
        trainDestinationLength = 47,
        trainDestinationLength2 = 14,
        trainLineLength = 1,
        trainTypeLength = 8,
        trainRemarksLength = 3;

    var 
        redNumberUV =       [[0, 149], [24, 786]],
        yellowNumberUV =    [[27, 149], [51, 786]],
        greenNumberUV =     [[54, 149], [77, 786]],
        redDelayNumberUV =  [[0, 786],[24, 1036]],
        trainDestinationUV= [[144 + offsetU, 149], [289 + offsetU, 2452]],
        trainDestinationUV2=[[289 + offsetU, 590], [434 + offsetU, 1276]],
        trainLineUV =       [[289 + offsetU, 149], [434 + offsetU, 198]],
        trainTypeUV =       [[434 + offsetU, 149], [579 + offsetU, 541]],
        trainRemarksUV =    [[793 + offsetU, 149], [962 + offsetU, 296]];
    var 
        oneVertex =         0.001821,
        topY_English =      0.7750 + 0.04464285714285714 + (rowCount * -0.1275),
        topY =              0.8625 + (rowCount * -0.1275),
        bottomY =           0.7750 + (rowCount * -0.1275),
        generalZ =          0.0577;
    
    // 初始化變數
    var trainNumberX, trainDestinationX, trainLineX, trainTypeX, trainDepTimeX, trainArrPlatX, trainRemarksX, trainRemarksNumX, trainRemarksNumENX;

    switch (displayBoardPorperty.size) {
        case "Large":
             trainNumberX =      [-0.6913 + offsetX, -0.6913 + offsetX + (oneVertex * 24)];
             trainDestinationX = [-0.4728 + offsetX, -0.4728 + offsetX + (oneVertex * 145)];
             trainLineX =        [-0.2106 + offsetX, -0.2106 + offsetX + (oneVertex * 145)];
             trainTypeX =        [0.0517 + offsetX, 0.0517 + offsetX + (oneVertex * 145)];
             trainDepTimeX =     [0.3139 + offsetX + (oneVertex * 27), 0.3139 + offsetX + (oneVertex * 50)];
             trainArrPlatX =     [0.5761 + offsetX + (oneVertex * 27), 0.5761 + offsetX + (oneVertex * 51)];
             trainRemarksX =     [0.7072 + offsetX, 0.7072 + offsetX + (oneVertex * 169)];
             trainRemarksNumX =  [0.7072 + offsetX + (oneVertex * 75), 0.7072 + offsetX + (oneVertex * 99)];
             trainRemarksNumENX =[0.7072 + offsetX + (oneVertex * 48), 0.7072 + offsetX + (oneVertex * 72)];
            break;
        case "Small":
             trainNumberX =      [-0.4288 + offsetX, -0.4288 + offsetX + (oneVertex * 24)];
             trainDestinationX = [-0.1720 + offsetX, -0.1720 + offsetX + (oneVertex * 145)];
             trainLineX =        [0.0001, 0.0001];
             trainTypeX =        [0.0001, 0.0001];
             trainDepTimeX =     [0.1715 + offsetX + (oneVertex * 27), 0.1715 + offsetX + (oneVertex * 50)];
             trainArrPlatX =     [0.0001, 0.0001];
             trainRemarksX =     [0.4447 + offsetX, 0.4447 + offsetX + (oneVertex * 169)];
             trainRemarksNumX =  [0.4447 + offsetX + (oneVertex * 75), 0.4447 + offsetX + (oneVertex * 99)];
             trainRemarksNumENX =[0.4447 + offsetX + (oneVertex * 48), 0.4447 + offsetX + (oneVertex * 72)];
            break;
    }
    var
        destinationSelect = returnNumber(trainInformation.trainDestination, "Destination"),
        lineSelect =        returnNumber(trainInformation.trainLine,        "Line"),
        typeSelect =        returnNumber(trainInformation.trainType,        "Type");

    var
        trainDestinationVertexF =[
            trainDestinationX[1], bottomY, generalZ,
            trainDestinationX[1], topY, generalZ,
            trainDestinationX[0], topY, generalZ,
            trainDestinationX[0], bottomY, generalZ
        ],
        trainLineVertexF =       [
            trainLineX[1], bottomY, generalZ,
            trainLineX[1], topY, generalZ,
            trainLineX[0], topY, generalZ,
            trainLineX[0], bottomY, generalZ
        ],
        trainTypeVertexF =       [
            trainTypeX[1], bottomY, generalZ,
            trainTypeX[1], topY, generalZ,
            trainTypeX[0], topY, generalZ,
            trainTypeX[0], bottomY, generalZ
        ],
        trainRemarksVertexF =       [
            trainRemarksX[1], bottomY, generalZ,
            trainRemarksX[1], topY, generalZ,
            trainRemarksX[0], topY, generalZ,
            trainRemarksX[0], bottomY, generalZ
        ]

    var trainNumber = trainInformation.trainNumber.split("").map(function(ch) {
        if (ch == ":") return 10;
        if (ch == "A") return 11;
        if (ch == "B") return 12;
        return parseInt(ch, 10);
    });

    //正面
    tessellator.startDrawingQuads();

    //繪製正面車次
    for (var renderIdx = 0; renderIdx < trainNumber.length; renderIdx++) {
        var renderVertex = [
            trainNumberX[1] + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
            trainNumberX[1] + (renderIdx * (oneVertex * 24)), topY, generalZ,
            trainNumberX[0] + (renderIdx * (oneVertex * 24)), topY, generalZ,
            trainNumberX[0] + (renderIdx * (oneVertex * 24)), bottomY, generalZ
        ];
        AddingQuads(
            trainNumber[renderIdx], renderVertex, yellowNumberUV[0][0] / totalUV[0],
            yellowNumberUV[1][0] / totalUV[0], trainNumberLength, yellowNumberUV[0][1] / totalUV[1],
            yellowNumberUV[1][1] / totalUV[1]
        );

    }
    //繪製發車時間
    if (fixedTrainTime) {
        for (var renderIdx = 0; renderIdx < fixedTrainTime.length; renderIdx++) {
            var renderVertex = [
                trainDepTimeX[1] + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
                trainDepTimeX[1] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainDepTimeX[0] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainDepTimeX[0] + (renderIdx * (oneVertex * 24)), bottomY, generalZ
            ];
            AddingQuads(
                fixedTrainTime[renderIdx], renderVertex, greenNumberUV[0][0] / totalUV[0],
                greenNumberUV[1][0] / totalUV[0], trainNumberLength, greenNumberUV[0][1] / totalUV[1],
                greenNumberUV[1][1] / totalUV[1]
            );
        }
    }
    
    //繪製停靠月台
    for (var renderIdx = 0; renderIdx < trainArrPlat.length; renderIdx++) {
        var offset = 0;
        var pInt = parseInt(trainArrPlat[0], 10);
        var renderArray;
        
        if(typeof(pInt) === "number" && !isNaN(pInt)){
            if(trainArrPlat.length > 1 && typeof(trainArrPlat[1]) === "string"){
                var platSuffix = String(trainArrPlat[1]).toUpperCase();
                renderArray = [pInt, 0];
                if(platSuffix == "A"){
                    renderArray[1] = 11;
                }else if(platSuffix == "B"){
                    renderArray[1] = 12;
                }else{
                    renderArray[1] = 11;
                }
                offset = -11;
            }else{
                renderArray = [pInt];
                offset = 0;
            }
        }else{
            renderArray = [0];
        }
        
        var renderVertex = [
            trainArrPlatX[1] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
            trainArrPlatX[1] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), topY, generalZ,
            trainArrPlatX[0] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), topY, generalZ,
            trainArrPlatX[0] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), bottomY, generalZ
        ];
        AddingQuads(
            renderArray[renderIdx], renderVertex, yellowNumberUV[0][0] / totalUV[0],
            yellowNumberUV[1][0] / totalUV[0], trainNumberLength, yellowNumberUV[0][1] / totalUV[1],
            yellowNumberUV[1][1] / totalUV[1]
        );
    }
    
    //繪製終點站
    if(destinationSelect !== null){
        if(destinationSelect <= 46){
            AddingQuads(
                destinationSelect, trainDestinationVertexF, trainDestinationUV[0][0] / totalUV[0],
                trainDestinationUV[1][0] / totalUV[0], trainDestinationLength, trainDestinationUV[0][1] / totalUV[1],
                trainDestinationUV[1][1] / totalUV[1]
            );
        }else{
            AddingQuads(
                destinationSelect - 47, trainDestinationVertexF, trainDestinationUV2[0][0] / totalUV[0],
                trainDestinationUV2[1][0] / totalUV[0], trainDestinationLength2, trainDestinationUV2[0][1] / totalUV[1],
                trainDestinationUV2[1][1] / totalUV[1]
            );
        }
    }
    //繪製線別
    if(lineSelect !== null){
        AddingQuads(
            lineSelect, trainLineVertexF, trainLineUV[0][0] / totalUV[0],
            trainLineUV[1][0] / totalUV[0], trainLineLength, trainLineUV[0][1] / totalUV[1],
            trainLineUV[1][1] / totalUV[1]
        );
    }
    //繪製車種
    if(typeSelect !== null){
        AddingQuads(
            typeSelect, trainTypeVertexF, trainTypeUV[0][0] / totalUV[0],
            trainTypeUV[1][0] / totalUV[0], trainTypeLength, trainTypeUV[0][1] / totalUV[1],
            trainTypeUV[1][1] / totalUV[1]
        );
    }

    //繪製晚點時間
    if(!isDelaying){
        AddingQuads(
            0, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
            trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
            trainRemarksUV[1][1] / totalUV[1]
        );
    }else{
        if(delayTime <= 120){
            var num = delayTime.toString().split("").map(Number);
            AddingQuads(
                1, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
                trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
                trainRemarksUV[1][1] / totalUV[1]
            );
            if(!isEnglishDisplaying){
                for(var renderIdxCN = 0; renderIdxCN < num.length; renderIdxCN++){
                    var offset = -12 * (num.length - 1);
                    var renderVertex = [
                        trainRemarksNumX[1] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), bottomY, generalZ,
                        trainRemarksNumX[1] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), topY, generalZ,
                        trainRemarksNumX[0] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), topY, generalZ,
                        trainRemarksNumX[0] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), bottomY, generalZ
                    ];
                    AddingQuads(
                        num[renderIdxCN], renderVertex, redNumberUV[0][0] / totalUV[0],
                        redNumberUV[1][0] / totalUV[0], trainNumberLength, redNumberUV[0][1] / totalUV[1],
                        redNumberUV[1][1] / totalUV[1]
                    );
                }
            }else{
                for(var renderIdxEN = 0; renderIdxEN < num.length; renderIdxEN++){
                    var offset = -24 * (num.length - 1);
                    var renderVertex = [
                        trainRemarksNumENX[1] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), bottomY, generalZ + 0.0005,
                        trainRemarksNumENX[1] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), topY_English, generalZ + 0.0005,
                        trainRemarksNumENX[0] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), topY_English, generalZ + 0.0005,
                        trainRemarksNumENX[0] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), bottomY, generalZ + 0.0005
                    ];
                    AddingQuads(
                        num[renderIdxEN], renderVertex, redDelayNumberUV[0][0] / totalUV[0],
                        redDelayNumberUV[1][0] / totalUV[0], delayNumberLength_English, redDelayNumberUV[0][1] / totalUV[1],
                        redDelayNumberUV[1][1] / totalUV[1]
                    );
                }
            }
        }else{
            AddingQuads(
                2, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
                trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
                trainRemarksUV[1][1] / totalUV[1]
            );
        }
    }

    tessellator.draw();

    //反面
    if(displayBoardPorperty.doubleSide){
        GL11.glPushMatrix();

        tessellator.startDrawingQuads();
        
        //繪製正面車次
        for (var renderIdx = 0; renderIdx < trainNumber.length; renderIdx++) {
            var renderVertex = [
                trainNumberX[1] + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
                trainNumberX[1] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainNumberX[0] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainNumberX[0] + (renderIdx * (oneVertex * 24)), bottomY, generalZ
            ];
            AddingQuads(
                trainNumber[renderIdx], renderVertex, yellowNumberUV[0][0] / totalUV[0],
                yellowNumberUV[1][0] / totalUV[0], trainNumberLength, yellowNumberUV[0][1] / totalUV[1],
                yellowNumberUV[1][1] / totalUV[1]
            );

        }
        //繪製發車時間
        if (fixedTrainTime) {
            for (var renderIdx = 0; renderIdx < fixedTrainTime.length; renderIdx++) {
                var renderVertex = [
                    trainDepTimeX[1] + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
                    trainDepTimeX[1] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                    trainDepTimeX[0] + (renderIdx * (oneVertex * 24)), topY, generalZ,
                    trainDepTimeX[0] + (renderIdx * (oneVertex * 24)), bottomY, generalZ
                ];
                AddingQuads(
                    fixedTrainTime[renderIdx], renderVertex, greenNumberUV[0][0] / totalUV[0],
                    greenNumberUV[1][0] / totalUV[0], trainNumberLength, greenNumberUV[0][1] / totalUV[1],
                    greenNumberUV[1][1] / totalUV[1]
                );
            }
        }
        
        //繪製停靠月台
        for (var renderIdx = 0; renderIdx < trainArrPlat.length; renderIdx++) {
            var offset = 0;
            var pInt = parseInt(trainArrPlat[0], 10);
            var renderArray;
            
            if(typeof(pInt) === "number" && !isNaN(pInt)){
                if(trainArrPlat.length > 1 && typeof(trainArrPlat[1]) === "string"){
                    var platSuffix = String(trainArrPlat[1]).toUpperCase();
                    renderArray = [pInt, 0];
                    if(platSuffix == "A"){
                        renderArray[1] = 11;
                    }else if(platSuffix == "B"){
                        renderArray[1] = 12;
                    }else{
                        renderArray[1] = 11;
                    }
                    offset = -12;
                }else{
                    renderArray = [pInt];
                    offset = 0;
                }
            }else{
                renderArray = [0];
            }
            
            var renderVertex = [
                trainArrPlatX[1] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), bottomY, generalZ,
                trainArrPlatX[1] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainArrPlatX[0] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), topY, generalZ,
                trainArrPlatX[0] + (offset * oneVertex) + (renderIdx * (oneVertex * 24)), bottomY, generalZ
            ];
            AddingQuads(
                renderArray[renderIdx], renderVertex, yellowNumberUV[0][0] / totalUV[0],
                yellowNumberUV[1][0] / totalUV[0], trainNumberLength, yellowNumberUV[0][1] / totalUV[1],
                yellowNumberUV[1][1] / totalUV[1]
            );
        }
        
        //繪製終點站
        if(destinationSelect !== null){
            if(destinationSelect <= 46){
                AddingQuads(
                    destinationSelect, trainDestinationVertexF, trainDestinationUV[0][0] / totalUV[0],
                    trainDestinationUV[1][0] / totalUV[0], trainDestinationLength, trainDestinationUV[0][1] / totalUV[1],
                    trainDestinationUV[1][1] / totalUV[1]
                );
            }else{
                AddingQuads(
                    destinationSelect - 47, trainDestinationVertexF, trainDestinationUV2[0][0] / totalUV[0],
                    trainDestinationUV2[1][0] / totalUV[0], trainDestinationLength2, trainDestinationUV2[0][1] / totalUV[1],
                    trainDestinationUV2[1][1] / totalUV[1]
                );
            }
        }
        //繪製線別
        if(lineSelect !== null){
            AddingQuads(
                lineSelect, trainLineVertexF, trainLineUV[0][0] / totalUV[0],
                trainLineUV[1][0] / totalUV[0], trainLineLength, trainLineUV[0][1] / totalUV[1],
                trainLineUV[1][1] / totalUV[1]
            );
        }
        //繪製車種
        if(typeSelect !== null){
            AddingQuads(
                typeSelect, trainTypeVertexF, trainTypeUV[0][0] / totalUV[0],
                trainTypeUV[1][0] / totalUV[0], trainTypeLength, trainTypeUV[0][1] / totalUV[1],
                trainTypeUV[1][1] / totalUV[1]
            );
        }

        //繪製晚點時間
        if(!isDelaying){
            AddingQuads(
                0, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
                trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
                trainRemarksUV[1][1] / totalUV[1]
            );
        }else{
            if(delayTime <= 120){
                var num = delayTime.toString().split("").map(Number);
                AddingQuads(
                    1, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
                    trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
                    trainRemarksUV[1][1] / totalUV[1]
                );
                if(!isEnglishDisplaying){
                    for(var renderIdxCN = 0; renderIdxCN < num.length; renderIdxCN++){
                        var offset = -12 * (num.length - 1);
                        var renderVertex = [
                            trainRemarksNumX[1] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), bottomY, generalZ,
                            trainRemarksNumX[1] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), topY, generalZ,
                            trainRemarksNumX[0] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), topY, generalZ,
                            trainRemarksNumX[0] + (offset * oneVertex) + (renderIdxCN * (oneVertex * 24)), bottomY, generalZ
                        ];
                        AddingQuads(
                            num[renderIdxCN], renderVertex, redNumberUV[0][0] / totalUV[0],
                            redNumberUV[1][0] / totalUV[0], trainNumberLength, redNumberUV[0][1] / totalUV[1],
                            redNumberUV[1][1] / totalUV[1]
                        );
                    }
                }else{
                    for(var renderIdxEN = 0; renderIdxEN < num.length; renderIdxEN++){
                        var offset = -24 * (num.length - 1);
                        var renderVertex = [
                            trainRemarksNumENX[1] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), bottomY, generalZ + 0.0005,
                            trainRemarksNumENX[1] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), topY_English, generalZ + 0.0005,
                            trainRemarksNumENX[0] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), topY_English, generalZ + 0.0005,
                            trainRemarksNumENX[0] + (offset * oneVertex) + (renderIdxEN * (oneVertex * 24)), bottomY, generalZ + 0.0005
                        ];
                        AddingQuads(
                            num[renderIdxEN], renderVertex, redDelayNumberUV[0][0] / totalUV[0],
                            redDelayNumberUV[1][0] / totalUV[0], delayNumberLength_English, redDelayNumberUV[0][1] / totalUV[1],
                            redDelayNumberUV[1][1] / totalUV[1]
                        );
                    }
                }
            }else{
                AddingQuads(
                    2, trainRemarksVertexF, trainRemarksUV[0][0] / totalUV[0],
                    trainRemarksUV[1][0] / totalUV[0], trainRemarksLength, trainRemarksUV[0][1] / totalUV[1],
                    trainRemarksUV[1][1] / totalUV[1]
                );
            }
        }
        renderer.rotate(180.0, 'Y', RotateCenterX, 0, 0);
        tessellator.draw();

        GL11.glPopMatrix();
    }
}

function searchBlockAndMeta(entity, blockType, metadata) {
    var version = RTMCore.VERSION;
    var searchMaxCount = 32;
    var world = entity.func_145831_w();

    if (version.indexOf("1.7.10") != -1) {
        var x = Math.floor(entity.field_145851_c);
        var y = Math.floor(entity.field_145848_d);
        var z = Math.floor(entity.field_145849_e);

        for (var i = 1; i <= searchMaxCount; i++) {
            var block = world.func_147439_a(x, y - i, z);
            if (block === blockType) {
                if (metadata !== null) {
                    if (world.func_72805_g(x, y - i, z) === metadata) return [block, world.func_72805_g(x, y - i, z), i];
                }
                else return [block, world.func_72805_g(x, y - i, z), i];
            }
        }
    } else {
        var blockPos = entity.func_174877_v();
        var x = Math.floor(blockPos.func_177958_n());
        var y = Math.floor(blockPos.func_177956_o());
        var z = Math.floor(blockPos.func_177952_p());

        for (var i = 1; i <= searchMaxCount; i++) {
            var searchBlockPos = new BlockPos(x, y - i, z);
            var iBlockState = world.func_180495_p(searchBlockPos);
            var block = iBlockState.func_177230_c();
            
            if (block === blockType) {
                if (blockType == Blocks.field_150359_w || blockType == Blocks.field_150410_aZ || blockType == Blocks.field_150405_ch){
                    return [block, 0, i];
                }else{
                    var meta = block.func_176201_c(iBlockState);
                    if (metadata !== null) {
                        if (meta === metadata) return [block, meta, i];
                    }
                    else return [block, meta, i];
                }
            }
        }
    }
    return [null, 0, 35];
}

function searchBlockAndMetaTriple(entity, blockType, metadata)
{
    var version = RTMCore.VERSION;
    var searchMaxCount = 32;
    var world = entity.func_145831_w();
    var Return1 = null, Return2 = null, Return3 = null;

    if (version.indexOf("1.7.10") != -1) {
        var x = Math.floor(entity.field_145851_c);
        var y = Math.floor(entity.field_145848_d);
        var z = Math.floor(entity.field_145849_e);

        for (var i = 1; i <= searchMaxCount; i++){
            var block = world.func_147439_a(x, y - i, z);

            if (block === blockType) {
                if (metadata !== null) {
                    if (world.func_72805_g(x, y - i, z) === metadata)
                    {
                        if(Return1 && Return2){
                            Return3 = [block, world.func_72805_g(x, y - i, z), i];
                            break;
                        }else if (Return1){
                            Return2 = [block, world.func_72805_g(x, y - i, z), i];
                        }else {
                            Return1 = [block, world.func_72805_g(x, y - i, z), i];
                        }
                    }
                }else{
                    if(Return1 && Return2){
                        Return3 = [block, world.func_72805_g(x, y - i, z), i];
                        break;
                    }else if (Return1){
                        Return2 = [block, world.func_72805_g(x, y - i, z), i];
                    }else {
                        Return1 = [block, world.func_72805_g(x, y - i, z), i];
                    }
                }
            }
        }
    }else{
        var blockPos = entity.func_174877_v();
        var x = Math.floor(blockPos.func_177958_n());
        var y = Math.floor(blockPos.func_177956_o());
        var z = Math.floor(blockPos.func_177952_p());

        for (var i = 1; i <= searchMaxCount; i++){
            var searchBlockPos = new BlockPos(x, y - i, z);
            var iBlockState = world.func_180495_p(searchBlockPos);
            var block = iBlockState.func_177230_c();

            if (block === blockType){
                if (blockType == Blocks.field_150359_w || blockType == Blocks.field_150410_aZ || blockType == Blocks.field_150405_ch){
                    if(Return1 && Return2){
                        Return3 = [block, 0, i];
                        break;
                    }else if (Return1){
                        Return2 = [block, 0, i];
                    }else {
                        Return1 = [block, 0, i];
                    }
                }else{
                    var meta = block.func_176201_c(iBlockState);
                    if (metadata !== null) {
                        if (meta === metadata){
                            if(Return1 && Return2){
                                Return3 = [block, meta, i];
                                break;
                            }else if (Return1){
                                Return2 = [block, meta, i];
                            }else {
                                Return1 = [block, meta, i];
                            }
                        }
                    }else{
                        if(Return1 && Return2){
                            Return3 = [block, meta, i];
                            break;
                        }else if (Return1){
                            Return2 = [block, meta, i];
                        }else {
                            Return1 = [block, meta, i];
                        }
                    }
                }
            }
        }
    }
    if (!Return3){Return3 = [null, 0, 35];}
    if (!Return2){Return2 = [null, 0, 35];}
    if (!Return1){Return1 = [null, 0, 35];}
    return [Return1, Return2, Return3];
}

function MCVersionChecker() {
    var varsion = RTMCore.VERSION;
    if (varsion.indexOf("1.7.10") >= 0) return "1.7.10";
    else if (varsion.indexOf("2.0") >= 0) return "1.8.9";
    else if (varsion.indexOf("2.1") >= 0) return "1.9.4";
    else if (varsion.indexOf("2.2") >= 0) return "1.10.2";
    else if (varsion.indexOf("2.4") >= 0) return "1.12.2";
    else return "unknown";
}

function searchTileEntity(entity, typeName) {
    if(entity === null) return null;
    var searchMaxCount = 32;
    var world = entity.func_145831_w();
    if(world === null) return null;
    for (var i = 1; i <= searchMaxCount; i++) {
        var version = MCVersionChecker();
        if (version === "1.7.10"){
            var x = Math.floor(entity.field_145851_c);
            var y = Math.floor(entity.field_145848_d);
            var z = Math.floor(entity.field_145849_e);
            var block = world.func_147438_o(x, y - i, z);
            if (block instanceof typeName) return block;
        }else{
            var cmdPos = entity.func_174877_v();
            var x = Math.floor(cmdPos.func_177958_n());
            var y = Math.floor(cmdPos.func_177956_o());
            var z = Math.floor(cmdPos.func_177952_p());
            var cmdPosA = new BlockPos(x, y - i, z);
            var block = world.func_175625_s(cmdPosA);
            if (block instanceof typeName) return block;
        }
        
    }
    return null;
}

function getTimeDigits(timezoneOffset) {
    var now = new Date();
    var systemOffsetInMs = now.getTimezoneOffset() * 60 * 1000;
    var utcTimeInMs = now.getTime() + systemOffsetInMs;
    var targetTimeInMs = utcTimeInMs + (timezoneOffset * 60 * 60 * 1000);
    var targetDate = new Date(targetTimeInMs);

    var hours   = targetDate.getHours();
    var minutes = targetDate.getMinutes();
    var seconds = targetDate.getSeconds();
    var milliseconds = targetDate.getMilliseconds();
    var totalSec = hours * 3600 + minutes * 60 + seconds;

    var hourTens = Math.floor(hours / 10);
    var hourOnes = hours % 10;
    var minuteTens = Math.floor(minutes / 10);
    var minuteOnes = minutes % 10;
    var secondTens = Math.floor(seconds / 10);
    var secondOnes = seconds % 10;

    return {
        hourTens:      hourTens,
        hourOnes:      hourOnes,
        minuteTens:    minuteTens,
        minuteOnes:    minuteOnes,
        secondTens:    secondTens,
        secondOnes:    secondOnes,
        milliseconds:  milliseconds,
        totalSec: totalSec
    };
}

function convertSecondsToHMS(totalSeconds) {
    var hours = Math.floor(totalSeconds / 3600);
    var remainSecAfterHours = totalSeconds % 3600;
    var minutes = Math.floor(remainSecAfterHours / 60);
    var seconds = remainSecAfterHours % 60;

    var hourTens = Math.floor(hours / 10);
    var hourOnes = hours % 10;
    var minuteTens = Math.floor(minutes / 10);
    var minuteOnes = minutes % 10;
    var secondTens = Math.floor(seconds / 10);
    var secondOnes = seconds % 10;

    return {
        hourTens:   hourTens,
        hourOnes:   hourOnes,
        minuteTens: minuteTens,
        minuteOnes: minuteOnes,
        secondTens: secondTens,
        secondOnes: secondOnes
    };
}

function AddingQuads(signPos, signL, u1, u2, signLength, signStartv, signEndv) {
    var v1 = signStartv + ((signEndv - signStartv) / (signLength) * (1 + signPos));
    var v2 = signStartv + ((signEndv - signStartv) / (signLength) * (0 + signPos));
    
    tessellator.addVertexWithUV(signL[0], signL[1], signL[2], u2, v1);
    tessellator.addVertexWithUV(signL[3], signL[4], signL[5], u2, v2);
    tessellator.addVertexWithUV(signL[6], signL[7], signL[8], u1, v2);
    tessellator.addVertexWithUV(signL[9], signL[10], signL[11], u1, v1);
}

function returnNumber(str, type) {
    var mapConfig = {
        "Line": {
            "YilanLine": 0,     "yilanline": 0,     "YL": 0,
            "NorthLinkLine": 1, "northLinkline": 1, "NL": 1,
            "MountainLine": 2,  "mountainline": 2,  "ML": 2,
            "CoastLine": 3,     "coastline": 3,     "CL": 3,
            "SouthLinkLine": 4, "southlinkline": 4, "SL": 4
        },
        "Destination": {
            "Keelung": 0, "keelung": 0, "0900": 0, "MN01": 0,
            "Badu": 1, "badu": 1, "0920": 1, "YL01": 1,
            "Qidu": 2, "qidu": 2, "0930": 2, "MN04": 2,
            "Xizhi": 3, "xizhi": 3, "0960": 3, "MN07": 3,
            "Nangang": 4, "nangang": 4, "0980": 4, "MN09": 4,
            "Songshan": 5, "songshan": 5, "0990": 5, "MN10": 5,
            "Taipei": 6, "taipei": 6, "1000": 6, "MN11": 6,
            "Shulin": 7, "shulin": 7, "1040": 7, "MN15": 7,
            "Yingge": 8, "yingge": 8, "1070": 8, "MN18": 8,
            "Taoyuan": 9, "taoyuan": 9, "1080": 9, "MN20": 9,
            "Zhongli": 10, "zhongli": 10, "1100": 10, "MN22": 10,
            "Yangmei": 11, "yangmei": 11, "1120": 11, "MN25": 11,
            "Beihu": 12, "beihu": 12, "1150": 12, "MN28": 12,
            "Hukou": 13, "hukou": 13, "1160": 13, "MN29": 13,
            "Zhubei": 14, "zhubei": 14, "1180": 14, "MN31": 14,
            "Hsinchu": 15, "hsinchu": 15, "1210": 15, "MN33": 15,
            "Zhuzhong": 16, "zhuzhong": 16, "1193": 16, "NW05": 16,
            "Liujia": 17, "liujia": 17, "1194": 17, "NW50": 17,
            "Zhunan": 18, "zhunan": 18, "1250": 18, "MN37": 18,
            "Miaoli": 19, "miaoli": 19, "3160": 19, "ML04": 19,
            "Sanyi": 20, "sanyi": 20, "3190": 20, "ML07": 20,
            "Houli": 21, "houli": 21, "3220": 21, "ML09": 21,
            "Fengyuan": 22, "fengyuan": 22, "3230": 22, "ML10": 22,
            "Taichung": 23, "taichung": 23, "3300": 23, "ML17": 23,
            "Xinwuri": 24, "xinwuri": 24, "3340": 24, "ML21": 24,
            "Tongxiao": 25, "tongxiao": 25, "2170": 25, "CL08": 25,
            "Dajia": 26, "dajia": 26, "2200": 26, "CL11": 26,
            "Changhua": 27, "changhua": 27, "3360": 27, "MC01": 27,
            "Yuanlin": 28, "yuanlin": 28, "3390": 28, "MC04": 28,
            "Ershui": 29, "ershui": 29, "3430": 29, "MC08": 29,
            "Douliu": 30, "douliu": 30, "3470": 30, "MC11": 30,
            "Dounan": 31, "dounan": 31, "3480": 31, "MC12": 31,
            "Chiayi": 32, "chiayi": 32, "4080": 32, "MS01": 32,
            "Xinying": 33, "xinying": 33, "4120": 33, "MS06": 33,
            "Shanhua": 34, "shanhua": 34, "4170": 34, "MS11": 34,
            "Yongkang": 35, "yongkang": 35, "4200": 35, "MS14": 35,
            "Tainan": 36, "tainan": 36, "4220": 36, "MS16": 36,
            "Shalun": 37, "shalun": 37, "4272": 37, "SH13": 37,
            "Xinzuoying": 38, "xinzuoying": 38, "4340": 38, "MS27": 38,
            "Kaohsiung": 39, "kaohsiung": 39, "4400": 39, "PT01": 39,
            "Pingtung": 40, "pingtung": 40, "5000": 40, "PT09": 40,
            "Chaozhou": 41, "chaozhou": 41, "5050": 41, "PT14": 41,
            "Fangliao": 42, "fangliao": 42, "5120": 42, "PT21": 42,
            "Zhiben": 43, "zhiben": 43, "5230": 43, "SL09": 43,
            "Taitung": 44, "taitung": 44, "6000": 44, "HT27": 44,
            "Guanshan": 45, "guanshan": 45, "6050": 45, "HT22": 45,
            "Chishang": 46, "chishang": 46, "6070": 46, "HT20": 46,
            "Yuli": 47, "yuli": 47, "6110": 47, "HT16": 47,
            "Ruisui": 48, "ruisui": 48, "6130": 48, "HT14": 48,
            "Shoufeng": 49, "shoufeng": 49, "6220": 49, "HT05": 49,
            "Hualien": 50, "hualien": 50, "7000": 50, "HT01": 50,
            "Heping": 51, "heping": 51, "7060": 51, "NL07": 51,
            "Nanao": 52, "nanao": 52, "7090": 52, "NL04": 52,
            "Suao": 53, "suao": 53, "7120": 53, "YL27": 53,
            "XinSuao": 54, "xinsuao": 54, "7130": 54, "YL26": 54,
            "Yilan": 55, "yilan": 55, "7190": 55, "YL20": 55,
            "Toucheng": 56, "toucheng": 56, "7230": 56, "YL16": 56,
            "Fulong": 57, "fulong": 57, "7290": 57, "YL10": 57,
            "Ruifang": 58, "ruifang": 58, "7360": 58, "YL04": 58,
            "Badouzi": 59, "badouzi": 59, "7362": 59, "TS01": 59,
            "JR": 60, "jr": 60, "1145": 60, "JY01": 60
        },
        "Type": {
            "Local": 0,
            "Ordinary": 1,
            "FastLocal" : 2,
            "ChuKuang": 3,
            "TzeChiang": 4,
            "XinTzeChiang": 5,
            "Taroko": 6,
            "Puyuma": 7
        }
    };
    
    if (!str) {
      return null;
    }

    if (mapConfig[type] && mapConfig[type].hasOwnProperty(str)) {
      return mapConfig[type][str];
    } else {
      return null;
    }
}

function parseAndRoundTime(timeStr) {
    if (!timeStr) return null; // 防止空值報錯
    var parts = String(timeStr).split(":");
    if (parts.length !== 3) {
        return null;
    }
    var hh = parseInt(parts[0], 10);
    var mm = parseInt(parts[1], 10);
    var ss = parseInt(parts[2], 10);
    if (isNaN(hh) || isNaN(mm) || isNaN(ss)) {
        return null;
    }
    if (ss >= 30) {
        mm++;
        if (mm >= 60) {
            mm = 0;
            hh++;
            if (hh >= 24) {
                hh = 0;
            }
        }
    }
    var hhStr = (hh < 10 ? "0" : "") + hh;
    var mmStr = (mm < 10 ? "0" : "") + mm;
    var hhDigits = hhStr.split("").map(function (d) { return parseInt(d, 10); });
    var mmDigits = mmStr.split("").map(function (d) { return parseInt(d, 10); });
    var result = hhDigits.concat([10], mmDigits);
    return result;
}

function loadTimetable(filePath) {
    return true; 
}
