// =====================================================
// BARRIER RELEASE BOSS AI (CustomNPCs 1.12.2)
// Uses ShinobiNPCScriptAPI for all Barrier Release abilities.
// Canopy Method Formation activated at fight start as a
// passive protective dome. Four Violet Flames traps the
// enemy inside a barrier, triggering a combo. Four Red Yang
// is the high-cost Kage-calibre finisher in phase 2.
// =====================================================

// ================= CONFIG =================
var BASE_COOLDOWN       = 25;
var WINDUP_TICKS        = 16;
var AGGRESSIVE_HP_RATIO = 0.50;

var CANOPY_REFRESH     = 20 * 28;   // re-cast canopy just under its ~30 s duration
var VIOLET_COMBO_CD    = 12;        // ticks between combo hits inside Violet Flames barrier
var RED_YANG_CD_TICKS  = 20 * 35;  // Four Red Yang on a 35 s cooldown

var AIM_EVERY_CAST = true;
var AIM_PITCH      = true;

// ---- Normal phase weights ----
var WEIGHTS_BASE = {
    VIOLET : 50,
    REDYANG: 20,
    CANOPY : 30
};

// ---- Aggressive phase (below 50% HP) ----
var WEIGHTS_AGGRO = {
    VIOLET : 40,
    REDYANG: 45,
    CANOPY : 15
};

var POWERS = {
    VIOLET : 5.0,
    REDYANG: 6.0,
    CANOPY : 5.0
};

// ================= STATE =================
var lastPhase = -1;

// ================= IMPORTS =================
var Shinobi = Java.type("com.leolifeless.shinobiaddon.compat.customnpcs.ShinobiNPCScriptAPI");
var Math    = Java.type("java.lang.Math");

// ================= HELPERS =================
function getStored(npc, key, defVal) {
    var d = npc.getStoreddata();
    if (!d.has(key)) { if (defVal !== undefined) d.put(key, defVal); return defVal; }
    return d.get(key);
}
function setStored(npc, key, val) { npc.getStoreddata().put(key, val); }

function clamp(v, a, b) { return v < a ? a : (v > b ? b : v); }

function copyWeights(obj) {
    var out = {};
    for (var k in obj) out[k] = obj[k];
    return out;
}

function weightedPick(weights) {
    var keys = [], sum = 0;
    for (var k in weights) { keys.push(k); sum += weights[k]; }
    if (!keys.length || sum <= 0) return null;
    var r = Math.random() * sum, acc = 0;
    for (var i = 0; i < keys.length; i++) {
        acc += weights[keys[i]];
        if (r <= acc) return keys[i];
    }
    return keys[keys.length - 1];
}

function getHp(mc) {
    try { if (mc && mc.getHealth) return mc.getHealth(); } catch(e) {}
    try { if (mc && mc.health != null) return mc.health; } catch(e) {}
    return 1;
}
function getMaxHp(mc) {
    try { if (mc && mc.getMaxHealth) return mc.getMaxHealth(); } catch(e) {}
    return 20;
}

function distToTarget(npc, t) {
    if (!t) return 9999;
    var me = npc.getMCEntity();
    var dx = me.posX - t.posX, dy = me.posY - t.posY, dz = me.posZ - t.posZ;
    return Math.sqrt(dx*dx + dy*dy + dz*dz);
}

function faceTarget(me, t) {
    try {
        var dx = t.posX - me.posX, dz = t.posZ - me.posZ;
        var dy = (t.posY + t.height * 0.6) - (me.posY + me.getEyeHeight());
        me.rotationYaw = Math.atan2(dz, dx) * 180 / Math.PI - 90;
        me.rotationYawHead = me.rotationYaw;
        me.renderYawOffset = me.rotationYaw;
        if (AIM_PITCH)
            me.rotationPitch = -(Math.atan2(dy, Math.sqrt(dx*dx + dz*dz)) * 180 / Math.PI);
    } catch(e) {}
}

function castPick(pick, npc) {
    if (pick === "VIOLET")  return Shinobi.cast(npc, "barrier_four_violet_flames_formation", POWERS.VIOLET,  true);
    if (pick === "REDYANG") return Shinobi.cast(npc, "barrier_four_red_yang_formation",      POWERS.REDYANG, true);
    if (pick === "CANOPY")  return Shinobi.cast(npc, "barrier_canopy_method_formation",      POWERS.CANOPY,  true);
    return false;
}

var NEEDS_WINDUP = { REDYANG: true, VIOLET: true };
var WINDUP_OVERRIDE = { VIOLET: 10 };

// ================= MAIN TICK =================
function tick(event) {
    var npc = event.npc;
    if (!npc) return;

    var me;
    try { me = npc.getMCEntity(); } catch(e) { return; }

    var tgt = npc.getAttackTarget();
    if (!tgt) return;
    var t = tgt.getMCEntity ? tgt.getMCEntity() : tgt;
    if (!t) return;

    if (!npc.getStoreddata().has("br_cd"))        setStored(npc, "br_cd",        0);
    if (!npc.getStoreddata().has("br_wind"))      setStored(npc, "br_wind",      0);
    if (!npc.getStoreddata().has("br_casting"))   setStored(npc, "br_casting",   "");
    if (!npc.getStoreddata().has("br_combo"))     setStored(npc, "br_combo",     "");
    if (!npc.getStoreddata().has("br_combo_cd"))  setStored(npc, "br_combo_cd",  0);
    if (!npc.getStoreddata().has("br_canopycd"))  setStored(npc, "br_canopycd",  0);
    if (!npc.getStoreddata().has("br_redyangcd")) setStored(npc, "br_redyangcd", 0);

    // ---- Canopy periodic refresh ----
    var ccd = getStored(npc, "br_canopycd", 0);
    if (ccd <= 0) {
        Shinobi.cast(npc, "barrier_canopy_method_formation", POWERS.CANOPY, true);
        setStored(npc, "br_canopycd", CANOPY_REFRESH);
    } else {
        setStored(npc, "br_canopycd", ccd - 1);
    }

    // Tick Red Yang cooldown
    var rycd = getStored(npc, "br_redyangcd", 0);
    if (rycd > 0) setStored(npc, "br_redyangcd", rycd - 1);

    // Phase update
    var phase = 0;
    try {
        var ratio = clamp(getHp(me) / getMaxHp(me), 0.0, 1.0);
        phase = ratio <= AGGRESSIVE_HP_RATIO ? 1 : 0;
    } catch(e) {}

    if (phase !== lastPhase) {
        announcePhase(npc, phase);
        lastPhase = phase;
        if (phase === 1) setStored(npc, "br_redyangcd", 0);
    }

    // ---- COMBO MODE (inside Violet Flames barrier) ----
    var combo   = getStored(npc, "br_combo",    "");
    var comboCd = getStored(npc, "br_combo_cd", 0);
    if (combo && combo.length > 0) {
        if (comboCd > 0) { setStored(npc, "br_combo_cd", comboCd - 1); return; }
        var parts = combo.split(",");
        var next  = parts[0];
        var rest  = parts.slice(1).join(",");
        if (AIM_EVERY_CAST) faceTarget(me, t);
        castPick(next, npc);
        setStored(npc, "br_combo",    rest);
        setStored(npc, "br_combo_cd", VIOLET_COMBO_CD);
        return;
    }

    // ---- WINDUP ----
    var wind    = getStored(npc, "br_wind",    0);
    var casting = getStored(npc, "br_casting", "");

    if (wind > 0) {
        if (wind % 4 === 0) {
            try { npc.spawnParticle("SPELL_MOB", npc.getX(), npc.getY() + 1, npc.getZ(), 0.4, 0.6, 0.4, 8, 0); } catch(e) {}
        }
        wind--;
        setStored(npc, "br_wind", wind);
        if (wind === 0 && casting) {
            if (AIM_EVERY_CAST) faceTarget(me, t);
            var used = castPick(casting, npc);
            if (used && casting === "VIOLET") {
                // Barrier is up — unleash a bombardment combo inside it
                setStored(npc, "br_combo",    "REDYANG,VIOLET,REDYANG");
                setStored(npc, "br_combo_cd", 20);
            }
            if (used && casting === "REDYANG")
                setStored(npc, "br_redyangcd", RED_YANG_CD_TICKS);
            setStored(npc, "br_casting", "");
            setStored(npc, "br_cd", used ? BASE_COOLDOWN : 8);
        }
        return;
    }

    // ---- COOLDOWN ----
    var cd = getStored(npc, "br_cd", 0);
    if (cd > 0) { setStored(npc, "br_cd", cd - 1); return; }

    // ---- PICK ATTACK ----
    var weights = copyWeights(phase === 1 ? WEIGHTS_AGGRO : WEIGHTS_BASE);

    if (getStored(npc, "br_redyangcd", 0) <= 0 && phase === 1) {
        weights.REDYANG += 50;
    }
    if (getStored(npc, "br_redyangcd", 0) > 0) {
        weights.REDYANG = 0;
    }

    // Canopy has its own refresh loop — don't pick it from weights
    weights.CANOPY = 0;

    var pick = weightedPick(weights);
    if (!pick) return;

    if (NEEDS_WINDUP[pick]) {
        var windTicks = WINDUP_OVERRIDE[pick] !== undefined ? WINDUP_OVERRIDE[pick] : WINDUP_TICKS;
        if (pick === "REDYANG") npc.say("§e§l...Four Red Yang Formation!");
        setStored(npc, "br_wind",    windTicks);
        setStored(npc, "br_casting", pick);
        return;
    }

    if (AIM_EVERY_CAST) faceTarget(me, t);
    var used2 = castPick(pick, npc);
    setStored(npc, "br_cd", used2 ? BASE_COOLDOWN : 8);
}

// ================= EVENTS =================
function target(event) {
    var npc = event.npc;
    if (!npc) return;
    setStored(npc, "br_cd",        20);
    setStored(npc, "br_wind",      0);
    setStored(npc, "br_casting",   "");
    setStored(npc, "br_combo",     "");
    setStored(npc, "br_canopycd",  0);   // activate canopy immediately
    setStored(npc, "br_redyangcd", 0);
    npc.say("§e§lNothing escapes my barriers.");
}

function damaged(event) {
    var npc = event.npc;
    if (!npc) return;
    try {
        // On heavy hit, reflexively drop a Violet Flames trap
        if (event.damage > 8 && Math.random() < 0.30) {
            var me = npc.getMCEntity();
            var tgt2 = npc.getAttackTarget();
            if (tgt2 != null) {
                var t2 = tgt2.getMCEntity ? tgt2.getMCEntity() : tgt2;
                if (t2 != null) faceTarget(me, t2);
            }
            Shinobi.cast(npc, "barrier_four_violet_flames_formation", POWERS.VIOLET, true);
            setStored(npc, "br_cd", BASE_COOLDOWN + 10);
        } else if (event.damage > 4 && Math.random() < 0.35) {
            var cd = getStored(npc, "br_cd", 0);
            setStored(npc, "br_cd", Math.max(0, cd - 8));
        }
    } catch(e) {}
}

function announcePhase(npc, phase) {
    if (phase === 0) {
        npc.say("§e§oYou cannot break what you cannot touch.");
    } else {
        npc.say("§6§l§oTHE WALLS CLOSE IN — FOUR RED YANG!");
    }
}
