using System;
using SlugBase.Features;
using static SlugBase.Features.FeatureTypes;
using MonoMod.Cil;
using Mono.Cecil.Cil;

namespace JilloSlug.Features;

internal static class ImmuneToDartMaggotsFeature {
    public static readonly PlayerFeature<bool> ImmuneToDartMaggots = PlayerBool("jillo/immune_to_dart_maggots");

    public static void AddHooks() {
        IL.DartMaggot.Update += DartMaggot_Update;
    }

    private static bool DartMaggotCanStun(DartMaggot maggot) {
        if (maggot.stuckInChunk.owner is Player && ImmuneToDartMaggots.TryGet(maggot.stuckInChunk.owner as Player, out var immune) && immune) {
            return false; // skip
        }
        return true; // proceed as normal
    }

    private static void ILInsertHandleDartMaggotStun(ILCursor c, ILLabel skipLabel) {
        c.Emit(OpCodes.Ldarg_0);
        c.EmitDelegate<Func<DartMaggot, bool>>(maggot => {
            return DartMaggotCanStun(maggot);
        });
        c.Emit(OpCodes.Brfalse, skipLabel);
    }

    private static void DartMaggot_Update(ILContext il) {
        ILCursor c = new ILCursor(il);

        // matching for `stuckInChunk.owner is Creature && ...`
        c.GotoNext(MoveType.After,
            i => i.MatchLdarg(0),
            i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
            i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
            i => i.MatchIsinst<Creature>(),
            i => i.Match(OpCodes.Brfalse_S)
        );

        // last instruction will be a brfalse.s; capture the label it goes to
        ILLabel skipLabel = c.Prev.Operand as ILLabel;

        // we're now ready to write another condition
        ILInsertHandleDartMaggotStun(c, skipLabel);

        // comments will be unrepeated; same functionality

        // matching for `stuckInChunk.owner is Player && ...`
        c.GotoNext(MoveType.After,
            i => i.MatchLdarg(0),
            i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
            i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
            i => i.MatchIsinst<Player>(),
            i => i.Match(OpCodes.Brfalse_S)
        );

        skipLabel = c.Prev.Operand as ILLabel;

        ILInsertHandleDartMaggotStun(c, skipLabel);

        // matching for `stuckInChunk.owner is Creature`
        c.GotoNext(MoveType.After,
            i => i.MatchLdarg(0),
            i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
            i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
            i => i.MatchIsinst<Creature>(),
            i => i.Match(OpCodes.Brfalse) // watch out! this one isn't _S
        );

        skipLabel = c.Prev.Operand as ILLabel;

        ILInsertHandleDartMaggotStun(c, skipLabel);
    }
}
