package jesse.keeblarcraft.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;

// MIXIN CHEAT SHEET:
// https://wiki.fabricmc.net/tutorial:mixin_examples
// Tutorial I referenced for this knowledge (outside fabric wiki): https://www.youtube.com/watch?v=HQUkWjMWTik
// MAKE SURE YOU READ fabric_mod.json and the keeblarcraft.mixin.json!! These injections are not instantiated anywhere else because mixins are special
// because all this code is essentially injected (pasted) into their respective areas during compilation.
//
// PLEASE SEE https://wiki.fabricmc.net/tutorial:mixin_injects FOR ALL THE PARAMETER TYPES OF INJECTION (you will likely have a question about all the types after reading
// bullet point #3 in the below injection explanation because I only explain 'Z' in that case)

@Mixin(PlayerEntity.class)
// *THIS* CLASS MUST BE ABSTRACT - BUT NOT TRUE FOR EVERY MIXIN
public abstract class PlayerMixin {
    // Example 0: Base injection (HEAD)
    // EXPLANATION OF THI MAGIC STRING:
    // This string MUST be written as so; BUT - it's not so bad! It has logic to it - obviously. Some known, some still a bit not
    // 1. 'dropItem' is the method we are overriding, and then we are just specifying exactly (which is required) where that method is at
    // 2. 'Lnet/minecraft/item/ItemStack' -> First argument inside method we are injecting in. In this case, the argument is an object 'ItemStack' so we specify
    //                                       that this is the net/minecraft/item/ItemStack type. WHY THE 'L' AT START? Don't know - but it's used in EVERY example
    //                                       so it can be assumed you always need it. It's consistently there, so not a huge deal.
    // 3. The 'ZZ' here are the other parameters inside the method which are java defaults - 'Z' happens to stand for boolean. It seems semicolons aren't needed
    //    between defaults since it just goes 'ZZ' and the parameter finishes with two booleans. Please CTRL+CLICK into the PlayerEntity class and search for 'dropItem' to
    //    familiarize yourself with the signature of the function.
    // 4. The appending 'Lnet/minecraft/entity/ItemEntity' is the RETURN TYPE of this method
    // 5. WHAT IS THE 'at = @At' syntax? Well; it is VERY Helpful to familiarize yourself with some areas on the fabric cheat sheet (https://wiki.fabricmc.net/tutorial:mixin_examples)
    //    but to give a brief explanation of it - it's just WHERE you are injecting your code at inside this function. 'HEAD' means the very top. 'TAIL' means at the end. There also
    //    is ways to inject by some offset into the function, and whatever a 'slice' is but I'm not looking at those right now. You get the idea
    // 6. WHAT IS THE 'cancellable = true'
    @Inject(method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", at = @At("HEAD"), cancellable = true)
    // THE METHOD CAN BE CALLED WHATEVER - But make sure it takes in the same parameters as the method you are mixing into ('mixin')
    // 1. What's the 'CallbackInfoReturnable<ItemEntity> cir' here? This can be the item we return with inside our injection. You need to picture the code in this
    //    function as literally being copied+pasted into what you are injecting into (at the 'HEAD', in this case).
    // 2. Why is this function 'void' if dropItem is not? -> The last parameter actually covers this case if we need to return but it's not required - remember this is a copy and
    //    paste of code into the function itself so it's not really relevant that our MIXIN function here returns anything
    public void dropItem0(ItemStack stack, boolean throwRandomly, boolean retainOwnership, CallbackInfoReturnable<ItemEntity> cir) {
        // WHY THE CAST LIKE THIS?
        // This is the starting work required to get 'this' from the perspective of PlayerEntity
        // 1. Remember that 'this' is actually NOT the PlayerEntity class, but we want to use the methods and functions from that class
        // 2. Casting (Object) 'this' gets us the 'PlayerMixin' class as a general object that we can cast into anything else
        // 3. Casting '(Object) this' into 'PlayerEntity' gets us to the mixin inject - and NOW we can store the result of this cast into the type we want as a reference
        //    which is the 'PlayerEntity' on the left hand side. Now we can access stuff from that class AS IF WE ARE CODING INSIDE OF THAT CLASS :)))
        PlayerEntity player = (PlayerEntity) (Object) this;
        Keeblarcraft.LOGGER.info("PLAYER MIXIN: " + player.getEntityName());
    }
}
