package org.allaymc.api.world.physics;

import org.allaymc.api.entity.Entity;
import org.allaymc.api.math.voxelshape.VoxelShape;
import org.joml.primitives.AABBdc;

import java.util.List;

/**
 * EntityPhysicsEngine is responsible for handling entity physics.
 *
 * @author daoge_cmd
 */
public interface EntityPhysicsEngine {

    /**
     * Compute the entities that are colliding with the specified entity.
     *
     * @param entity The entity to compute the colliding entities
     * @return The entities that are colliding with the specified entity
     */
    default List<Entity> computeCollidingEntities(Entity entity) {
        return computeCollidingEntities(entity.getOffsetAABB(), other -> other.getRuntimeId() != entity.getRuntimeId());
    }

    /**
     * Compute the entities that are colliding with the specified aabb.
     *
     * @param aabb The aabb to compute the colliding entities
     * @return The entities that are colliding with the specified aabb
     */
    default List<Entity> computeCollidingEntities(AABBdc aabb) {
        return computeCollidingEntities(aabb, entity -> true);
    }

    /**
     * Compute the entities that are colliding with the specified entity.
     *
     * @param entity                   The entity to compute the colliding entities
     * @param ignoreEntityHasCollision Whether to ignore the entity's "hasCollision" property
     * @return The entities that are colliding with the specified entity
     */
    default List<Entity> computeCollidingEntities(Entity entity, boolean ignoreEntityHasCollision) {
        return computeCollidingEntities(entity.getOffsetAABB(), other -> other.getRuntimeId() != entity.getRuntimeId() && (ignoreEntityHasCollision || entity.hasEntityCollision()));
    }

    /**
     * Compute the entities that are colliding with the specified aabb.
     *
     * @param aabb                     The entity to compute the colliding entities
     * @param ignoreEntityHasCollision Whether to ignore the entity's "hasCollision" property
     * @return The entities that are colliding with the specified aabb
     */
    default List<Entity> computeCollidingEntities(AABBdc aabb, boolean ignoreEntityHasCollision) {
        return computeCollidingEntities(aabb, entity -> ignoreEntityHasCollision || entity.hasEntityCollision());
    }

    /**
     * Compute the entities that are colliding with the specified aabb.
     *
     * @param aabb      The entity to compute the colliding entities
     * @param predicate The predicate to filter the entities
     * @return The entities that are colliding with the specified aabb and pass the predicate
     */
    List<Entity> computeCollidingEntities(AABBdc aabb, AABBOverlapFilter<Entity> predicate);

    /**
     * @see #computeCollidingEntities(VoxelShape, boolean)
     */
    default List<Entity> computeCollidingEntities(VoxelShape voxelShape) {
        return computeCollidingEntities(voxelShape, false);
    }

    /**
     * Compute the entities that are colliding with the specified voxel shape.
     *
     * @param voxelShape               The voxel shape to compute the colliding entities
     * @param ignoreEntityHasCollision Whether to ignore the entity's has collision
     * @return The entities that are colliding with the specified voxel shape
     */
    List<Entity> computeCollidingEntities(VoxelShape voxelShape, boolean ignoreEntityHasCollision);

    /**
     * Get the cached entity colliding result of the specified entity.
     *
     * @param entity The entity to get the cached entity colliding result
     * @return The cached entity colliding result of the specified entity
     */
    default List<Entity> getCachedEntityCollidingResult(Entity entity) {
        return getCachedEntityCollidingResult(entity, false);
    }

    /**
     * Get the cached entity colliding result of the specified entity.
     *
     * @param entity                   The entity to get the cached entity colliding result
     * @param ignoreEntityHasCollision Whether to ignore the entity's "hasCollision" property
     * @return The cached entity colliding result of the specified entity
     */
    List<Entity> getCachedEntityCollidingResult(Entity entity, boolean ignoreEntityHasCollision);
}
