const RU = Resources;
const MM = ModelManager;
const MCU = MinecraftClient;
include(Resources.id("aphrodite:library/code/util/error_supplier.js"));
include(Resources.id("aphrodite:library/code/gui/orderly_responder.js"));

// Load models
const MODELS = MM.loadPartedRawModel(RU.manager(), RU.id("mtrsteamloco:eyecandies/signals/mari.obj"), null);

const BASE = ModelManager.uploadVertArrays(MODELS.get("base"));
const BOTTOM = ModelManager.uploadVertArrays(MODELS.get("bottom"));
const RI_BASE = ModelManager.uploadVertArrays(MODELS.get("ri_base"));
const PROCEED = MM.uploadVertArrays(MODELS.get("repeater_proceed"));
const DANGER = MM.uploadVertArrays(MODELS.get("repeater_danger"));
const KCR_PROCEED = MM.uploadVertArrays(MODELS.get("repeater_proceed_hk"));
const OFF = MM.uploadVertArrays(MODELS.get("off"));
const SPACE = MM.uploadVertArrays(MODELS.get("space"));

const modelRefs = {
    a: MM.uploadVertArrays(MODELS.get("a")),
    b: MM.uploadVertArrays(MODELS.get("b")),
    c: MM.uploadVertArrays(MODELS.get("c")),
    d: MM.uploadVertArrays(MODELS.get("d")),
    e: MM.uploadVertArrays(MODELS.get("e")),
    f: MM.uploadVertArrays(MODELS.get("f")),
    g: MM.uploadVertArrays(MODELS.get("g")),
    h: MM.uploadVertArrays(MODELS.get("h")),
    i: MM.uploadVertArrays(MODELS.get("i")),
    j: MM.uploadVertArrays(MODELS.get("j")),
    k: MM.uploadVertArrays(MODELS.get("k")),
    l: MM.uploadVertArrays(MODELS.get("l")),
    m: MM.uploadVertArrays(MODELS.get("m")),
    n: MM.uploadVertArrays(MODELS.get("n")),
    o: MM.uploadVertArrays(MODELS.get("o")),
    p: MM.uploadVertArrays(MODELS.get("p")),
    q: MM.uploadVertArrays(MODELS.get("q")),
    r: MM.uploadVertArrays(MODELS.get("r")),
    s: MM.uploadVertArrays(MODELS.get("s")),
    t: MM.uploadVertArrays(MODELS.get("t")),
    u: MM.uploadVertArrays(MODELS.get("u")),
    v: MM.uploadVertArrays(MODELS.get("v")),
    w: MM.uploadVertArrays(MODELS.get("w")),
    x: MM.uploadVertArrays(MODELS.get("x")),
    y: MM.uploadVertArrays(MODELS.get("y")),
    z: MM.uploadVertArrays(MODELS.get("z")),
};

// Handle numbers (num0-num19)
for (let i = 0; i <= 9; i++) {
    modelRefs[`num${i}`] = ModelManager.uploadVertArrays(MODELS.get(i.toString()));
}

// Handle alphabets (a-z)



const pos0 = MM.uploadVertArrays(MODELS.get("pos0"));
const pos1 = MM.uploadVertArrays(MODELS.get("pos1"));
const pos2 = MM.uploadVertArrays(MODELS.get("pos2"));
const pos3 = MM.uploadVertArrays(MODELS.get("pos3"));
const pos4 = MM.uploadVertArrays(MODELS.get("pos4"));
const pos5 = MM.uploadVertArrays(MODELS.get("pos5"));
const pos6 = MM.uploadVertArrays(MODELS.get("pos6"));

const slash = MM.uploadVertArrays(MODELS.get("slash"));
const invslash = MM.uploadVertArrays(MODELS.get("invslash"));


//custom input setup
const nodePosKey = "custom";
const modeKey = "mode";
const repeaterAspectKey = "aspect";
const repeaterType = "type"
const mariaspect = "route"
const marimode = "rimode"
const repeaterpos = "lr"
const cd_time = "cd";
const ra_time = "ra";
const ri = "routeindicator"


const nodePosINPUT = new ConfigResponder.TextField(
    nodePosKey,
    ComponentUtil.literal("Node Pos (x,y,z)"),
    ""
).setTooltipSupplier(str => Optional.of(asJavaArray([
    ComponentUtil.literal("Leave empty to auto-detect."),
    ComponentUtil.literal("Format: x,y,z)")
], Component)))
    .setErrorSupplier(str => {
        if (!str) return Optional.empty();
        const parts = str.split(",");
        if (parts.length !== 3 || parts.some(p => isNaN(parseFloat(p)))) {
            return Optional.of(ComponentUtil.literal("Invalid format. Use x,y,z."));
        }
        return Optional.empty();
    })

const RI = new ConfigResponder.TextField(
    ri,
    ComponentUtil.translatable("base type"),
    "1"
).setErrorSupplier(str => {
    if (!["0", "1", "2"].includes(str)) {
        return Optional.of(ComponentUtil.translatable("error.aph.invalid_value"));
    }
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("0-none 1-bottom 2-route indicator")], Component)));


const MODE_RESPONDER = new ConfigResponder.TextField(
    modeKey,
    ComponentUtil.translatable("name.raf.mode"),
    "0"
).setErrorSupplier(str => {
    if (!["0", "1"].includes(str)) {
        return Optional.of(ComponentUtil.translatable("error.aph.invalid_value"));
    }
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("0-Repeater 1-MARI 2-CD/RA indicator")], Component)));


const REPEATERPOS = new ConfigResponder.TextField(
    repeaterpos,
    ComponentUtil.translatable("select.cdra.repeater.position"),
    "0"
).setErrorSupplier(str => {
    if (!["0", "1", "2"].includes(str)) {
        return Optional.of(ComponentUtil.translatable("error.aph.invalid_value"));
    }
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("do not use")], Component)));


const ASPECT_RESPONDER = new ConfigResponder.TextField(
    repeaterAspectKey,
    ComponentUtil.translatable("select.repeater.aspect"),
    "0"
).setErrorSupplier(str => {
    if (!["0", "1", "2"].includes(str)) {
        return Optional.of(ComponentUtil.translatable("error.aph.invalid_value"));
    }
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("0-auto 1-danger 2-proceed")], Component)));

const REPEATER_type = new ConfigResponder.TextField(
    repeaterType,
    ComponentUtil.translatable("select.repeater.type"),
    "0"
).setErrorSupplier(str => {
    if (!["0", "1", "2"].includes(str)) {
        return Optional.of(ComponentUtil.translatable("error.aph.invalid_value"));
    }
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("0-HK 1-UK 2-OFF")], Component)));


const MARI_ASPECT = new ConfigResponder.TextField(
    mariaspect,
    ComponentUtil.translatable("MARI aspect"),
    ""
).setErrorSupplier(str => {
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("lowercase,max 2 characters,use - or \" \" for space")], Component)));

const MARI_MODE = new ConfigResponder.TextField(
    marimode,
    ComponentUtil.translatable("MARI display mode"),
    ""
).setErrorSupplier(str => {
    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("0-always 1-when signal clear")], Component)));


const CDTIME = new ConfigResponder.TextField(
    cd_time,
    ComponentUtil.translatable("time.cd"),
    "10"
).setErrorSupplier(str => {

    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("time display CD until departure")], Component)));

const RATIME = new ConfigResponder.TextField(
    ra_time,
    ComponentUtil.translatable("time.ra"),
    "2"
).setErrorSupplier(str => {

    return Optional.empty();
}).setTooltipSupplier(str => Optional.of(asJavaArray([ComponentUtil.translatable("time display RA until departure")], Component)));


const RESS = orderlyResponder("Route Indicator", "Made by Andy_Wongg", [MODE_RESPONDER, nodePosINPUT, ASPECT_RESPONDER, REPEATER_type, RI, MARI_ASPECT, MARI_MODE]);


//setup eyecandy
function create(ctx, state, entity) {
    entity.registerCustomConfig(RESS);
    entity.sendUpdateC2S();
}

function render(ctx, state, entity) {
    let repleft = new Matrices();
    let repright = new Matrices();
    let char2mat = new Matrices();
    let mat = new Matrices();
    repright.translate(0.1125, 0, 0);
    repleft.translate(-0.1125, 0, 0);
    char2mat.translate(0.15625, 0, 0);
    mat.translate(0, 0, 0);
    //draw base
    ctx.drawModel(BASE, mat);

    let mode = entity.getCustomConfig(modeKey) + "";
    let mari_aspect = entity.getCustomConfig(mariaspect) + "";
    let mari_mode = entity.getCustomConfig(marimode) + "";
    let type = entity.getCustomConfig(repeaterType) + "";
    let aspect = entity.getCustomConfig(repeaterAspectKey) + "";
    let repeater_pos = entity.getCustomConfig(repeaterpos) + "";
    let hasri = entity.getCustomConfig(ri) + "";

    let oa = -1;
    try {
        let pos = entity.getWorldPosVector3f();
        let facing = entity.getBlockYRot() + entity.rotateY / Math.PI * 180 + 90;
        let nodePosStr = entity.getCustomConfig(nodePosKey);
        let nodePos;

        if (nodePosStr && nodePosStr.trim() !== "") {
            const parts = nodePosStr.split(",").map(Number);
            if (parts.length === 3 && parts.every(n => !isNaN(n))) {
                nodePos = new Vector3f(parts[0], parts[1], parts[2]);
            }
        }

        if (!nodePos) {
            nodePos = MCU.getNodeAt(pos, facing);
        }
        oa = MCU.getOccupiedAspect(nodePos, facing, 4); // 4 possible aspects
    } catch (e) { }

    //draw ri base
    if (hasri == 1) ctx.drawModel(BOTTOM, mat);
    if (hasri == 2){
        ctx.drawModel(RI_BASE, mat);
        ctx.drawModel(BOTTOM, mat);
    }
    // Decide which model to draw
    let modelToDraw = null;
    let char1 = null;
    let char2 = null;
    //repeater
    if (mode == 0) {
        if (aspect == 0) {
            if (oa == -1) { }
            else if (oa == 1) {
                if (type == 1) modelToDraw = DANGER;
            } else if (oa != 1) {
                if (type == 0) modelToDraw = KCR_PROCEED;
                else if (type == 1) modelToDraw = PROCEED;
                else if (type == 2) modelToDraw = OFF
            }
        }
        else if (aspect == 1) {
            if (type == 1) modelToDraw = DANGER;
        } else if (aspect == 2) {
            if (type == 0) modelToDraw = KCR_PROCEED;
            else if (type == 1) modelToDraw = PROCEED;
            else if (type == 2) modelToDraw = OFF;
        }
        if (modelToDraw != null) {
            ctx.drawModel(modelToDraw, mat);

        }
    }
    //MARI
    else if (mode == 1) {
        if (mari_aspect == "pos0") modelToDraw = pos0;
        else if (mari_aspect == "pos1") modelToDraw = pos1;
        else if (mari_aspect == "pos2") modelToDraw = pos2;
        else if (mari_aspect == "pos3") modelToDraw = pos3;
        else if (mari_aspect == "pos4") modelToDraw = pos4;
        else if (mari_aspect == "pos5") modelToDraw = pos5;
        else if (mari_aspect == "pos6") modelToDraw = pos6;
        else {
            //character 1
            if (mari_aspect[0] == "-" ||mari_aspect[0] == " ") char1 = null
            else if (mari_aspect[0] == "/") char1 = slash;
            else if (mari_aspect[0] == "\\") char1 = invslash;
            else if (/^\d+$/.test(mari_aspect[0])) char1 = modelRefs[`num${mari_aspect[0]}`];
            else char1 = modelRefs[mari_aspect[0]];
            //character 2
            if (mari_aspect[1] == "/") char2 = slash;
            else if (mari_aspect[1] == "\\") char2 = invslash;
            else if (/^\d+$/.test(mari_aspect[1])) char2 = modelRefs[`num${mari_aspect[1]}`];
            else char2 = modelRefs[mari_aspect[1]];
        }
        if (mari_mode == 0) {
            if (modelToDraw != null) ctx.drawModel(modelToDraw, mat);
            if (char1 != null) ctx.drawModel(char1, mat);
            if (char2 != null) ctx.drawModel(char2, char2mat);
        } else if (mari_mode == 1) {
            if (oa != 1 && oa != -1)
            if (modelToDraw != null) ctx.drawModel(modelToDraw, mat);
            if (char1 != null) ctx.drawModel(char1, mat);
            if (char2 != null) ctx.drawModel(char2, char2mat);

        }
    }
}

