package net.minecraft.world.phys.shapes;

import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.CollisionGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;

public interface CollisionContext {
    static CollisionContext empty() {
        return EntityCollisionContext.EMPTY;
    }

    static CollisionContext of(Entity entity) {
        return (CollisionContext)(switch (entity) {
            case AbstractMinecart abstractMinecart -> AbstractMinecart.useExperimentalMovement(abstractMinecart.level())
                ? new MinecartCollisionContext(abstractMinecart, false)
                : new EntityCollisionContext(entity, false, false);
            default -> new EntityCollisionContext(entity, false, false);
        });
    }

    static CollisionContext of(Entity entity, boolean canStandOnFluid) {
        return new EntityCollisionContext(entity, canStandOnFluid, false);
    }

    static CollisionContext placementContext(@Nullable Player player) {
        return new EntityCollisionContext(
            player != null && player.isDescending(),
            true,
            player != null ? player.getY() : -Double.MAX_VALUE,
            player instanceof LivingEntity ? player.getMainHandItem() : ItemStack.EMPTY,
            player instanceof LivingEntity ? fluidState -> player.canStandOnFluid(fluidState) : fluidState -> false,
            player
        );
    }

    static CollisionContext withPosition(@Nullable Entity entity, double bottom) {
        return new EntityCollisionContext(
            entity != null && entity.isDescending(),
            true,
            entity != null ? bottom : -Double.MAX_VALUE,
            entity instanceof LivingEntity livingEntityx ? livingEntityx.getMainHandItem() : ItemStack.EMPTY,
            entity instanceof LivingEntity livingEntity ? fluidState -> livingEntity.canStandOnFluid(fluidState) : fluidState -> false,
            entity
        );
    }

    boolean isDescending();

    boolean isAbove(VoxelShape shape, BlockPos pos, boolean canAscend);

    boolean isHoldingItem(Item item);

    boolean canStandOnFluid(FluidState fluid1, FluidState fluid2);

    VoxelShape getCollisionShape(BlockState state, CollisionGetter collisionGetter, BlockPos pos);

    default boolean isPlacement() {
        return false;
    }
}
