include(Resources.id("mtrsteamloco:scripts/display_helper.js"));
importPackage(java.awt);
importPackage(java.awt.geom);
importPackage(java.awt.image);

// ===== route_type_colors.json 色取得 =====
var __ROUTE_TYPE_COLOR_DB = null;
var __CURRENT_ROUTE_TYPE_JPN = null;

function __loadRouteTypeColorDbOnce() {
    if (__ROUTE_TYPE_COLOR_DB != null) return;
    var path = "mtr:botapinu_lcd/mctn_lcd/route_type_colors.json";
    try {
        var txt = Resources.readString(Resources.id(path));
        __ROUTE_TYPE_COLOR_DB = JSON.parse(String(txt));
        if (__ROUTE_TYPE_COLOR_DB == null || typeof __ROUTE_TYPE_COLOR_DB !== "object") __ROUTE_TYPE_COLOR_DB = {};
        print("route_type_colors.json loaded: " + path);
    } catch (e) {
        __ROUTE_TYPE_COLOR_DB = {};
        print("FAILED route_type_colors.json: " + e);
    }
}

function __normalizeRouteTypeKey(s) {
    if (s == null) return "";
    return String(s).replace(/[\s　|｜]+/g, "").trim();
}

function __parseAnyColor(v) {
    if (v == null) return null;

    // Color.red / Color.lightGray 形式
    if (typeof v === "string" && v.indexOf("Color.") === 0) {
        var name = v.substring(6).trim();
        try { if (Color[name] != null) return Color[name]; } catch (e0) {}
    }

    // 数値形式。16711680 のようなRGB値は alpha なしとして扱う。
    if (typeof v === "number") {
        try { return new Color((v | 0)); } catch (en) {}
    }

    var s = String(v).trim();
    if (s.length == 0) return null;

    try {
        // #RRGGBB / #AARRGGBB
        if (s.indexOf("#") === 0) {
            var hex = s.substring(1);
            if (hex.length === 6) return new Color((java.lang.Integer.parseInt(hex, 16)) | 0);
            if (hex.length === 8) return new Color((java.lang.Long.parseLong(hex, 16)) | 0, true);
        }

        // 0xRRGGBB / 0xAARRGGBB
        // 以前は 0xRRGGBB も alpha ありで読んでいたため、透明色になりJSON色が反映されない原因になり得た。
        if (s.indexOf("0x") === 0 || s.indexOf("0X") === 0) {
            var hex2 = s.substring(2);
            if (hex2.length <= 6) return new Color((java.lang.Integer.parseInt(hex2, 16)) | 0);
            return new Color((java.lang.Long.parseLong(hex2, 16)) | 0, true);
        }

        // 10進数RGB。基本は alpha なし。負数などJava ARGB指定だけ alpha ありに寄せる。
        var n = java.lang.Long.parseLong(s);
        if (n >= 0 && n <= 0xFFFFFF) return new Color((n | 0));
        return new Color((n | 0), true);
    } catch (e) {}
    return null;
}

function __getRouteTypeColorObj(routeTypeJpn) {
    __loadRouteTypeColorDbOnce();
    if (!routeTypeJpn || !__ROUTE_TYPE_COLOR_DB) return null;

    var key = String(routeTypeJpn).trim();
    var obj = __ROUTE_TYPE_COLOR_DB[key];
    if (obj && typeof obj === "object") return obj;

    // 全角/半角スペース差、"快　速" と "快速" の差を吸収
    var normalized = __normalizeRouteTypeKey(key);
    for (var k in __ROUTE_TYPE_COLOR_DB) {
        if (!__ROUTE_TYPE_COLOR_DB.hasOwnProperty(k)) continue;
        if (__normalizeRouteTypeKey(k) === normalized) {
            obj = __ROUTE_TYPE_COLOR_DB[k];
            if (obj && typeof obj === "object") return obj;
        }
    }
    return null;
}

function __getRouteTypeBg(routeTypeJpn, fallbackColor) {
    var obj = __getRouteTypeColorObj(routeTypeJpn);
    if (!obj) return fallbackColor;
    var bg = __parseAnyColor(obj.bg);
    return bg == null ? fallbackColor : bg;
}

function __getRouteTypeFg(routeTypeJpn, fallbackColor) {
    var obj = __getRouteTypeColorObj(routeTypeJpn);
    if (!obj) return fallbackColor;
    var fg = __parseAnyColor(obj.fg);
    return fg == null ? fallbackColor : fg;
}


// ===== LED化ラッパー設定 =====
// 元の描画を低解像度キャンバスへ描画 → Nearest拡大 → LED網掛け画像を重ねる
var __LED_FONT_SCALE = 1.08; // LED化で潰れにくいように文字だけ少し大きめ

function __drawLedWrapped(g, state, train, outW, outH, rawDrawFunc) {
    var smallW = Math.round(outW / 5);
    var smallH = Math.round(outH / 5);
    var img = new BufferedImage(smallW, smallH, BufferedImage.TYPE_INT_ARGB);
    var gg = img.createGraphics();

    gg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
    gg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    gg.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);

    // 以降の元コードは 640/800 x 160 の座標のまま動かす
    gg.scale(smallW / outW, smallH / outH);
    rawDrawFunc(gg, state, train);
    gg.dispose();

    if (g instanceof java.awt.Graphics2D) {
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    }

    g.drawImage(img, 0, 0, outW, outH, 0, 0, smallW, smallH, null);

    // 既存リソースのLED網掛けを重ねる（読み込めない環境でも本体表示は継続）
    try {
        let ledImage = Resources.readBufferedImage(Resources.id("mtr:botapinu_lcd/mctn_lcd/images/led_front.png"));
        g.drawImage(ledImage, 0, 0, outW + 1, outH + 1, null);
    } catch (e) { }
}




function assyukuDraw(g, text, textSize, drawPos, width, font, textColor, drawPos2) {
    let nextStaScale = 1.0;
    // 文字の幅を測定
    if (g.getFontMetrics(font.deriveFont(Font.PLAIN, textSize * __LED_FONT_SCALE)).stringWidth(text) > width) {
        nextStaScale = (width) / g.getFontMetrics(font.deriveFont(Font.PLAIN, textSize * __LED_FONT_SCALE)).stringWidth(text);
    }
    // 描画
    g.setFont(font.deriveFont(AffineTransform.getScaleInstance(nextStaScale, 1.0)).deriveFont(Font.PLAIN, textSize * __LED_FONT_SCALE));
    g.setColor(textColor);
    g.drawString(text, (((width) - (g.getFontMetrics(font.deriveFont(AffineTransform.getScaleInstance(nextStaScale, 1.0)).deriveFont(Font.PLAIN, textSize * __LED_FONT_SCALE)).stringWidth(text))) / 2) + drawPos2, drawPos);
}


// ===== 種別文字のLED潰れ対策：背景色系の縁取り =====
var __LED_OUTLINE_COLOR = new Color(0.188, 0.188, 0.188);
var __LED_OUTLINE_WIDTH = 5; // 低解像度LEDキャンバス上で上下左右1ドット分の縁取り

function __drawStringOutline(g, text, x, y, mainColor, outlineColor) {
    if (outlineColor == null) outlineColor = __LED_OUTLINE_COLOR;
    if (mainColor == null) mainColor = Color.white;

    let o = (__LED_OUTLINE_WIDTH == null ? 5 : __LED_OUTLINE_WIDTH);

    g.setColor(outlineColor);
    // 上下左右（低解像度化後に1ドット分見えるよう、元座標では5pxずらす）
    g.drawString(text, x - o, y);
    g.drawString(text, x + o, y);
    g.drawString(text, x, y - o);
    g.drawString(text, x, y + o);
    // 角の欠け防止
    g.drawString(text, x - o, y - o);
    g.drawString(text, x + o, y - o);
    g.drawString(text, x - o, y + o);
    g.drawString(text, x + o, y + o);

    g.setColor(mainColor);
    g.drawString(text, x, y);
}

function assyukuDrawOutline(g, text, textSize, drawPos, width, font, textColor, drawPos2) {
    let nextStaScale = 1.0;
    let size = textSize * __LED_FONT_SCALE;
    if (g.getFontMetrics(font.deriveFont(Font.PLAIN, size)).stringWidth(text) > width) {
        nextStaScale = (width) / g.getFontMetrics(font.deriveFont(Font.PLAIN, size)).stringWidth(text);
    }

    let drawFont = font.deriveFont(AffineTransform.getScaleInstance(nextStaScale, 1.0)).deriveFont(Font.PLAIN, size);
    let fm = g.getFontMetrics(drawFont);
    let x = (((width) - fm.stringWidth(text)) / 2) + drawPos2;

    g.setFont(drawFont);

    let o = (__LED_OUTLINE_WIDTH == null ? 5 : __LED_OUTLINE_WIDTH);

    g.setColor(__LED_OUTLINE_COLOR);
    // 上下左右（低解像度化後に1ドット分見えるよう、元座標では5pxずらす）
    g.drawString(text, x - o, drawPos);
    g.drawString(text, x + o, drawPos);
    g.drawString(text, x, drawPos - o);
    g.drawString(text, x, drawPos + o);
    // 角の欠け防止
    g.drawString(text, x - o, drawPos - o);
    g.drawString(text, x + o, drawPos - o);
    g.drawString(text, x - o, drawPos + o);
    g.drawString(text, x + o, drawPos + o);

    g.setColor(textColor);
    g.drawString(text, x, drawPos);
}











// ===== 安全取得ヘルパー：終着後/回送/車庫入庫時の空データ対策 =====
function __safePlatformSize(list) {
    if (list == null) return 0;
    try { return list.size(); } catch (e) { }
    try { return list.length; } catch (e2) { }
    return 0;
}

function __safePlatformGet(list, index) {
    if (list == null) return null;
    try { return list.get(index); } catch (e) { }
    try { return list[index]; } catch (e2) { }
    return null;
}


function __isEmptyText(s) {
    return s == null || String(s).trim().length == 0;
}


// ===== JSON色取得（参考.js準拠・state保持・複数キー照合） =====
function __routeTypeColorDbState(state) {
    if (state == null) state = {};
    if (state.__mctnRouteTypeColorDb != null) return state.__mctnRouteTypeColorDb;

    var path = "mtr:botapinu_lcd/mctn_lcd/route_type_colors.json";
    try {
        var txt = Resources.readString(Resources.id(path));
        state.__mctnRouteTypeColorDb = JSON.parse(String(txt));
        if (state.__mctnRouteTypeColorDb == null || typeof state.__mctnRouteTypeColorDb !== "object") state.__mctnRouteTypeColorDb = {};
        try { print("mctn route_type_colors.json loaded: " + path); } catch (ep) {}
    } catch (e) {
        state.__mctnRouteTypeColorDb = {};
        try { print("FAILED mctn route_type_colors.json: " + e); } catch (ep2) {}
    }
    return state.__mctnRouteTypeColorDb;
}

function __normalizeRtKey(v) {
    if (v == null) return "";
    return String(v).replace(/[\s　|｜]+/g, "").trim();
}

function __parseJsonColor(v) {
    if (v == null) return null;
    if (typeof v === "string" && v.indexOf("Color.") === 0) {
        var name = v.substring(6).trim();
        try { if (Color[name] != null) return Color[name]; } catch (e0) {}
    }
    if (typeof v === "number") {
        try { return new Color((v | 0)); } catch (en) {}
    }
    var s = String(v).trim();
    if (s.length == 0) return null;
    try {
        if (s.indexOf("#") === 0) {
            var hex = s.substring(1);
            if (hex.length === 6) return new Color((java.lang.Integer.parseInt(hex, 16)) | 0);
            if (hex.length === 8) return new Color((java.lang.Long.parseLong(hex, 16)) | 0, true);
        }
        if (s.indexOf("0x") === 0 || s.indexOf("0X") === 0) {
            var hex2 = s.substring(2);
            if (hex2.length <= 6) return new Color((java.lang.Integer.parseInt(hex2, 16)) | 0);
            return new Color((java.lang.Long.parseLong(hex2, 16)) | 0, true);
        }
        var n = java.lang.Long.parseLong(s);
        if (n >= 0 && n <= 0xFFFFFF) return new Color((n | 0));
        return new Color((n | 0), true);
    } catch (e) {}
    return null;
}

function __getRouteTypeOverrideFromJson(state, routeTypeJpn, routeTypeEng, routeTypeRaw) {
    var db = __routeTypeColorDbState(state);
    if (db == null) return null;

    var candidates = [];
    if (!__isEmptyText(routeTypeJpn)) candidates.push(String(routeTypeJpn).trim());
    if (!__isEmptyText(routeTypeEng)) candidates.push(String(routeTypeEng).trim());
    if (!__isEmptyText(routeTypeRaw)) {
        candidates.push(String(routeTypeRaw).trim());
        var rawParts = String(routeTypeRaw).split(/[|｜]/);
        for (var rp = 0; rp < rawParts.length; rp++) {
            if (!__isEmptyText(rawParts[rp])) candidates.push(String(rawParts[rp]).trim());
        }
    }

    for (var i = 0; i < candidates.length; i++) {
        var obj = db[candidates[i]];
        if (obj && typeof obj === "object") return obj;
    }

    for (var k in db) {
        if (!db.hasOwnProperty(k)) continue;
        var nk = __normalizeRtKey(k);
        for (var j = 0; j < candidates.length; j++) {
            if (nk === __normalizeRtKey(candidates[j])) {
                var obj2 = db[k];
                if (obj2 && typeof obj2 === "object") return obj2;
            }
        }
    }
    try { print("route_type_colors miss: jp=" + routeTypeJpn + ", en=" + routeTypeEng + ", raw=" + routeTypeRaw); } catch (em) {}
    return null;
}

function __jsonBg(state, routeTypeJpn, routeTypeEng, routeTypeRaw, fallbackColor) {
    var obj = __getRouteTypeOverrideFromJson(state, routeTypeJpn, routeTypeEng, routeTypeRaw);
    if (!obj) return fallbackColor;
    var c = __parseJsonColor(obj.bg);
    return c == null ? fallbackColor : c;
}

function __jsonFg(state, routeTypeJpn, routeTypeEng, routeTypeRaw, fallbackColor) {
    var obj = __getRouteTypeOverrideFromJson(state, routeTypeJpn, routeTypeEng, routeTypeRaw);
    if (!obj) return fallbackColor;
    var c = __parseJsonColor(obj.fg);
    return c == null ? fallbackColor : c;
}

function __jsonOutline(state, routeTypeJpn, routeTypeEng, routeTypeRaw, fallbackValue) {
    var obj = __getRouteTypeOverrideFromJson(state, routeTypeJpn, routeTypeEng, routeTypeRaw);
    if (!obj || obj.outline == null) return fallbackValue;
    if (obj.outline === true) return true;
    if (obj.outline === false) return false;
    var s = String(obj.outline).trim().toLowerCase();
    if (s === "true" || s === "1" || s === "yes" || s === "on") return true;
    if (s === "false" || s === "0" || s === "no" || s === "off") return false;
    return fallbackValue;
}

function __drawRouteTypeText(g, text, textSize, y, drawWidth, font, fg, xOffset, outlineEnabled) {
    if (__isEmptyText(text)) return;
    var drawColor = fg == null ? Color.white : fg;
    var dx = xOffset == null ? 0 : xOffset;
    if (outlineEnabled === false) {
        assyukuDraw(g, String(text), textSize, y, drawWidth, font, drawColor, dx);
    } else {
        assyukuDrawOutline(g, String(text), textSize, y, drawWidth, font, drawColor, dx);
    }
}

function __drawNisFallbackRaw(g, font, width, height) {
    g.setColor(Color.red);
    g.fillRect(0, 0, width, height);
    assyukuDraw(g, "回　 送", 110, 120, width, font, Color.white, 0);
}


function ikisakiHeadRaw(g, state, train) {
    //変数
    let font = Resources.readFont(Resources.id("mtr:fonts/notosanscjkjp-medium.otf"));
    let width = 800;
    let height = 160;
    let backGround = new Color(0.188, 0.188, 0.188);
    let purple = new Color(0.537, 0.0, 1.0);

    // 終着駅から車庫回送へ切り替わる瞬間など、platformList[next] が未定義になる場合があるため先に安全確認
    let next = -1;
    let platformList = null;
    try { next = train.getThisRoutePlatformsNextIndex(); } catch (eNext) { next = -1; }
    try { platformList = train.getThisRoutePlatforms(); } catch (eList) { platformList = null; }
    let platformSize = __safePlatformSize(platformList);
    let currentPlatform = __safePlatformGet(platformList, next);

    if (!train || !train.isOnRoute() || platformList == null || platformSize <= 0 || next < 0 || next >= platformSize || currentPlatform == null || currentPlatform.route == null) {
        __CURRENT_ROUTE_TYPE_JPN = null;
        __drawNisFallbackRaw(g, font, width, height);
        return;
    }

    let finalSta = currentPlatform.destinationName;
    let finalStaJpn = TextUtil.getCjkParts(finalSta);
    let finalStaEng = TextUtil.getNonCjkParts(finalSta);
    let routeType = currentPlatform.route.lightRailRouteNumber;
    let routeTypeRaw = String(routeType == null ? "" : routeType).trim();
    let routeTypeParts = routeTypeRaw.split(/[|｜]/);
    let routeTypeJpn = "";
    let routeTypeEng = "";
    if (routeTypeParts.length >= 2) {
        routeTypeJpn = String(routeTypeParts[0]).trim();
        routeTypeEng = String(routeTypeParts[1]).trim();
    } else {
        routeTypeJpn = TextUtil.getCjkParts(routeTypeRaw);
        routeTypeEng = TextUtil.getNonCjkParts(routeTypeRaw);
    }
    if (__isEmptyText(routeTypeJpn)) routeTypeJpn = routeTypeRaw;
    if (__isEmptyText(routeTypeEng)) routeTypeEng = routeTypeJpn;
    __CURRENT_ROUTE_TYPE_JPN = routeTypeJpn;
    let routeColor = new Color(currentPlatform.route.color);
    let routeTypeColor;
    let nisW = g.getFontMetrics(font.deriveFont(Font.PLAIN, 100 * __LED_FONT_SCALE)).stringWidth("回　 送");
    let nisEngW = g.getFontMetrics(font.deriveFont(Font.PLAIN, 50 * __LED_FONT_SCALE)).stringWidth("Not In Service");
    let ledImage = Resources.readBufferedImage(Resources.id("mtr:botapinu_lcd/mctn_lcd/images/led_front.png"));
    let route = currentPlatform.route.name;
    let routeExtra = TextUtil.getExtraParts(route);
    //回送
    if (train.isOnRoute()) {
        if (Date.now() % 8000 < 4000) {
            if (routeTypeJpn == "回送" || finalStaJpn == "回送") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "回　 送", 110, 120, width, font, Color.white, 0);
            } else if (routeTypeJpn == "試運転" || finalStaJpn == "試運転") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "試 運 転", 110, 120, width, font, Color.white, 0);
            } else if (finalStaJpn == "臨時") {
                g.setColor(backGround);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "臨　 時", 110, 120, width, font, Color.white, 0);
            } else if (finalStaJpn == "団体専用") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "団体専用", 110, 120, width, font, Color.white, 0);
            } else {
                g.setColor(backGround);
                g.fillRect(0, 0, width, height);
            }
        } else {
            if (routeTypeJpn == "回送" || finalStaJpn == "回送") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "Not In Service", 110, 120, width, font, Color.white, 0);
            } else if (routeTypeJpn == "試運転" || finalStaJpn == "試運転") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "Test Run", 110, 120, width, font, Color.white, 0);
            } else if (finalStaJpn == "臨時") {
                g.setColor(backGround);
                g.fillRect(0, 0, width, height);
                assyukuDraw(g, "Extra", 110, 120, width, font, Color.white, 0);
            } else if (finalStaJpn == "団体専用") {
                g.setColor(Color.red);
                g.fillRect(0, 0, width, height);
                assyukuDrawOutline(g, "Group", 110, 120, width, font, Color.white, 0);
            } else {
                g.setColor(backGround);
                g.fillRect(0, 0, width, height);
            }
        }
        routeTypeColor = __jsonBg(state, routeTypeJpn, routeTypeEng, routeTypeRaw, routeColor);
        let routeTypeFg = __jsonFg(state, routeTypeJpn, routeTypeEng, routeTypeRaw, Color.white);
        let routeTypeOutline = __jsonOutline(state, routeTypeJpn, routeTypeEng, routeTypeRaw, true);

        if (Date.now() % 8000 < 4000) {
            // 英語表示：上半分をJSON bgで塗り、種別英字をJSON fgで描画
            g.setColor(routeTypeColor);
            g.fillRect(0, 0, width, height / 2);
            __drawRouteTypeText(g, routeTypeEng, 65, 65, width, font, routeTypeFg, 0, routeTypeOutline);
            assyukuDraw(g, finalStaEng, 65, 145, width, font, Color.white, 0);
        } else {
            // 日本語表示：左側をJSON bgで塗り、種別日本語をJSON fgで描画
            g.setColor(routeTypeColor);
            g.fillRect(0, 0, width / 12 * 5, height);
            __drawRouteTypeText(g, routeTypeJpn, 100, 120, width / 12 * 5, font, routeTypeFg, 0, routeTypeOutline);
            assyukuDraw(g, finalStaJpn, 110, 120, width / 12 * 7, font, Color.white, width / 12 * 5);
        }
    } else {
        g.setColor(Color.red);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.white);
        g.setFont(font.deriveFont(Font.PLAIN, 100 * __LED_FONT_SCALE));
        g.drawString("回　 送", (width / 2) - (nisW / 2), 100);
        g.setColor(Color.white);
        g.setFont(font.deriveFont(Font.PLAIN, 50 * __LED_FONT_SCALE));
        g.drawString("Not In Service", (width / 2) - (nisEngW / 2), 160);
    }
}

// 公開関数：新旧両方の呼び出し形式に対応
// main.js が ikisakiHead(g, state, train) で呼んでも、旧式の ikisakiHead(g, train) で呼んでも同じ表示にする
var __ikisakiHead_fallback_state = {};
function ikisakiHead(g, state, train) {
    if (train == null) {
        train = state;
        state = __ikisakiHead_fallback_state;
    }
    __drawLedWrapped(g, state, train, 800, 160, ikisakiHeadRaw);
}
