package codechicken.lib.raytracer;

import codechicken.lib.vec.Vector3;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ForgeMod;

public class RayTracer {

    public static BlockHitResult retraceBlock(LevelReader world, Player player, BlockPos pos) {
        Vec3 startVec = getStartVec(player);
        Vec3 endVec = getEndVec(player);
        BlockState state = world.getBlockState(pos);
        VoxelShape baseShape = state.getShape(world, pos);
        BlockHitResult baseTraceResult = baseShape.clip(startVec, endVec, pos);
        if (baseTraceResult != null) {
            BlockHitResult raytraceTraceShape = state.getVisualShape(world, pos, CollisionContext.of(player)).clip(startVec, endVec, pos);
            if (raytraceTraceShape != null) {
                return raytraceTraceShape;
            }
        }
        return baseTraceResult;
    }

    private static double getBlockReachDistance_server(ServerPlayer player) {
        return player.getAttribute(ForgeMod.REACH_DISTANCE.get()).getValue();
    }

    @OnlyIn (Dist.CLIENT)
    private static double getBlockReachDistance_client() {
        return Minecraft.getInstance().gameMode.getPickRange();
    }

    public static BlockHitResult retrace(Player player) {
        return retrace(player, ClipContext.Block.OUTLINE);
    }

    public static BlockHitResult retrace(Player player, ClipContext.Block blockMode) {
        return retrace(player, getBlockReachDistance(player), blockMode, ClipContext.Fluid.NONE);
    }

    public static BlockHitResult retrace(Player player, ClipContext.Block blockMode, ClipContext.Fluid fluidMode) {
        return retrace(player, getBlockReachDistance(player), blockMode, fluidMode);
    }

    public static BlockHitResult retrace(Player player, double reach, ClipContext.Block blockMode) {
        return retrace(player, reach, blockMode, ClipContext.Fluid.NONE);
    }

    public static BlockHitResult retrace(Player player, double reach, ClipContext.Block blockMode, ClipContext.Fluid fluidMode) {
        Vec3 startVec = getStartVec(player);
        Vec3 endVec = getEndVec(player, reach);
        return player.level.clip(new ClipContext(startVec, endVec, blockMode, fluidMode, player));
    }

    public static Vec3 getCorrectedHeadVec(Player player) {
        Vector3 v = Vector3.fromEntity(player).add(0, player.getEyeHeight(), 0);
        return v.vec3();
    }

    public static Vec3 getStartVec(Player player) {
        return getCorrectedHeadVec(player);
    }

    @Deprecated // Use attribute directly? avoid all this nonsense.
    public static double getBlockReachDistance(Player player) {
        return player.level.isClientSide ? getBlockReachDistance_client() : player instanceof ServerPlayer ? getBlockReachDistance_server((ServerPlayer) player) : 5D;
    }

    public static Vec3 getEndVec(Player player) {
        Vec3 headVec = getCorrectedHeadVec(player);
        Vec3 lookVec = player.getViewVector(1.0F);
        double reach = getBlockReachDistance(player);
        return headVec.add(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach);
    }

    public static Vec3 getEndVec(Player player, double reach) {
        Vec3 headVec = getCorrectedHeadVec(player);
        Vec3 lookVec = player.getViewVector(1.0F);
        return headVec.add(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach);
    }
}

@@ -65,16 +65,68 @@
 ector3;%0A
+import net.covers1624.quack.annotation.ReplaceWith;%0A
 import n
@@ -281,32 +281,78 @@
 .player.Player;%0A
+import net.minecraft.world.level.BlockGetter;%0A
 import net.minec
@@ -848,71 +848,330 @@
  %7B%0A%0A
-    public static BlockHitResult retraceBlock(LevelReader world
+%0A    @Deprecated (forRemoval = true, since = %221.18.2%22) // Unnecessarily specific level type.%0A    public static BlockHitResult retraceBlock(LevelReader level, Player player, BlockPos pos) %7B%0A        return retraceBlock((BlockGetter) level, player, pos);%0A    %7D%0A%0A    public static BlockHitResult retraceBlock(BlockGetter level
 , Pl
@@ -1312,21 +1312,21 @@
 state = 
-world
+level
 .getBloc
@@ -1380,29 +1380,29 @@
 te.getShape(
-world
+level
 , pos);%0A    
@@ -1585,21 +1585,21 @@
 alShape(
-world
+level
 , pos, C

Fix overly specific type on RayTracer.retraceBlock