// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.modules.projectile.system;

import com.hypixel.hytale.server.core.modules.collision.EntityContactData;
import com.hypixel.hytale.server.core.modules.collision.BlockTracker;
import com.hypixel.hytale.server.core.modules.collision.BlockCollisionProvider;
import com.hypixel.hytale.server.core.modules.collision.EntityRefCollisionProvider;
import com.hypixel.hytale.server.core.modules.physics.util.ForceProvider;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsBodyStateUpdater;
import com.hypixel.hytale.server.core.modules.physics.util.ForceProviderEntity;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsBodyState;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.modules.physics.RestingSupport;
import com.hypixel.hytale.server.core.modules.physics.util.ForceProviderStandardState;
import com.hypixel.hytale.server.core.modules.projectile.config.StandardPhysicsConfig;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.modules.physics.SimplePhysicsProvider;
import com.hypixel.hytale.server.core.modules.collision.IBlockTracker;
import com.hypixel.hytale.server.core.modules.collision.IBlockCollisionConsumer;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.modules.entity.system.TransformSystems;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.OrderPriority;
import com.hypixel.hytale.server.core.modules.collision.TangiableEntitySpatialSystem;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.modules.physics.component.Velocity;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.projectile.config.StandardPhysicsProvider;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;

public class StandardPhysicsTickSystem extends EntityTickingSystem<EntityStore>
{
    @Nonnull
    private final Query<EntityStore> query;
    @Nonnull
    private final Set<Dependency<EntityStore>> dependencies;
    
    public StandardPhysicsTickSystem() {
        this.query = (Query<EntityStore>)Query.and(StandardPhysicsProvider.getComponentType(), TransformComponent.getComponentType(), HeadRotation.getComponentType(), Velocity.getComponentType(), BoundingBox.getComponentType());
        this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, TangiableEntitySpatialSystem.class, OrderPriority.CLOSEST), new SystemDependency(Order.BEFORE, TransformSystems.EntityTrackerUpdate.class));
    }
    
    @Nonnull
    @Override
    public Set<Dependency<EntityStore>> getDependencies() {
        return this.dependencies;
    }
    
    @Nonnull
    @Override
    public Query<EntityStore> getQuery() {
        return this.query;
    }
    
    @Override
    public void tick(float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        final TimeResource timeResource = store.getResource(TimeResource.getResourceType());
        final float timeDilationModifier = timeResource.getTimeDilationModifier();
        final World world = store.getExternalData().getWorld();
        dt = 1.0f / world.getTps();
        dt *= timeDilationModifier;
        final StandardPhysicsProvider physicsComponent = archetypeChunk.getComponent(index, StandardPhysicsProvider.getComponentType());
        assert physicsComponent != null;
        final Velocity velocityComponent = archetypeChunk.getComponent(index, Velocity.getComponentType());
        assert velocityComponent != null;
        final TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType());
        assert transformComponent != null;
        final BoundingBox boundingBoxComponent = archetypeChunk.getComponent(index, BoundingBox.getComponentType());
        assert boundingBoxComponent != null;
        final StandardPhysicsConfig physicsConfig = physicsComponent.getPhysicsConfig();
        final Ref<EntityStore> selfRef = archetypeChunk.getReferenceTo(index);
        if (physicsComponent.getState() == StandardPhysicsProvider.STATE.INACTIVE) {
            velocityComponent.setZero();
            return;
        }
        final ForceProviderStandardState forceState = physicsComponent.getForceProviderStandardState();
        final RestingSupport restingSupport = physicsComponent.getRestingSupport();
        if (physicsComponent.getState() == StandardPhysicsProvider.STATE.RESTING) {
            if (forceState.externalForce.squaredLength() == 0.0 && !restingSupport.hasChanged(world)) {
                return;
            }
            physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE);
        }
        final Vector3d position = physicsComponent.getPosition();
        final Vector3d velocity = physicsComponent.getVelocity();
        final Vector3d movement = physicsComponent.getMovement();
        final Box boundingBox = boundingBoxComponent.getBoundingBox();
        final PhysicsBodyState stateBefore = physicsComponent.getStateBefore();
        final PhysicsBodyState stateAfter = physicsComponent.getStateAfter();
        final ForceProviderEntity forceProviderEntity = physicsComponent.getForceProviderEntity();
        final PhysicsBodyStateUpdater stateUpdater = physicsComponent.getStateUpdater();
        final ForceProvider[] forceProviders = physicsComponent.getForceProviders();
        final Vector3d moveOutOfSolidVelocity = physicsComponent.getMoveOutOfSolidVelocity();
        final double gravity = physicsConfig.getGravity();
        final int bounceCount = physicsConfig.getBounceCount();
        final boolean allowRolling = physicsConfig.isAllowRolling();
        physicsComponent.setWorld(world);
        position.assign(transformComponent.getPosition());
        velocityComponent.assignVelocityTo(velocity);
        final double mass = forceProviderEntity.getMass(boundingBox.getVolume());
        forceState.convertToForces(dt, mass);
        forceState.updateVelocity(velocity);
        if (velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10 || forceState.externalForce.squaredLength() >= 0.0) {
            physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE);
        }
        else {
            velocity.assign(Vector3d.ZERO);
        }
        if (physicsComponent.getState() == StandardPhysicsProvider.STATE.RESTING && restingSupport.hasChanged(world)) {
            physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE);
        }
        stateBefore.position.assign(position);
        stateBefore.velocity.assign(velocity);
        forceProviderEntity.setForceProviderStandardState(forceState);
        stateUpdater.update(stateBefore, stateAfter, mass, dt, physicsComponent.isOnGround(), forceProviders);
        velocity.assign(stateAfter.velocity);
        movement.assign(velocity).scale(dt);
        forceState.clear();
        if (velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10) {
            physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE);
        }
        else {
            velocity.assign(Vector3d.ZERO);
        }
        final EntityRefCollisionProvider entityCollisionProvider = physicsComponent.getEntityCollisionProvider();
        final BlockCollisionProvider blockCollisionProvider = physicsComponent.getBlockCollisionProvider();
        final BlockTracker triggerTracker = physicsComponent.getTriggerTracker();
        final Vector3d contactPosition = physicsComponent.getContactPosition();
        final Vector3d contactNormal = physicsComponent.getContactNormal();
        final Vector3d nextMovement = physicsComponent.getNextMovement();
        double maxRelativeDistance = 1.0;
        if (physicsComponent.isProvidesCharacterCollisions()) {
            Ref<EntityStore> creatorReference = null;
            if (physicsComponent.getCreatorUuid() != null) {
                creatorReference = store.getExternalData().getRefFromUUID(physicsComponent.getCreatorUuid());
            }
            maxRelativeDistance = entityCollisionProvider.computeNearest(commandBuffer, boundingBox, position, movement, selfRef, creatorReference);
            if (maxRelativeDistance < 0.0 || maxRelativeDistance > 1.0) {
                maxRelativeDistance = 1.0;
            }
        }
        physicsComponent.setBounced(false);
        physicsComponent.setOnGround(false);
        moveOutOfSolidVelocity.assign(Vector3d.ZERO);
        physicsComponent.setMovedInsideSolid(false);
        physicsComponent.setDisplacedMass(0.0);
        physicsComponent.setSubSurfaceVolume(0.0);
        physicsComponent.setEnterFluid(Double.MAX_VALUE);
        physicsComponent.setLeaveFluid(-1.7976931348623157E308);
        physicsComponent.setCollisionStart(maxRelativeDistance);
        contactPosition.assign(position).addScaled(movement, physicsComponent.getCollisionStart());
        contactNormal.assign(Vector3d.ZERO);
        physicsComponent.setSliding(true);
        final Vector3d tmpPosition = position.clone();
        nextMovement.assign(Vector3d.ZERO);
        while (physicsComponent.isSliding() && !movement.equals(Vector3d.ZERO)) {
            contactPosition.assign(tmpPosition).addScaled(movement, physicsComponent.getCollisionStart());
            physicsComponent.setSliding(false);
            blockCollisionProvider.cast(world, boundingBox, tmpPosition, movement, physicsComponent, triggerTracker, maxRelativeDistance);
            movement.assign(nextMovement);
            tmpPosition.assign(contactPosition);
        }
        movement.assign(tmpPosition).add(nextMovement).subtract(position);
        physicsComponent.getFluidTracker().reset();
        final double density = (physicsComponent.getDisplacedMass() > 0.0) ? (physicsComponent.getDisplacedMass() / physicsComponent.getSubSurfaceVolume()) : 1.2;
        if (physicsComponent.isMovedInsideSolid()) {
            position.addScaled(moveOutOfSolidVelocity, dt);
            velocity.assign(moveOutOfSolidVelocity);
            forceState.dragCoefficient = physicsComponent.getDragCoefficient(density);
            forceState.displacedMass = physicsComponent.getDisplacedMass();
            forceState.gravity = gravity;
            physicsComponent.finishTick(transformComponent, velocityComponent);
            return;
        }
        double velocityClip = physicsComponent.isBounced() ? physicsComponent.getCollisionStart() : 1.0;
        boolean enteringWater = false;
        if (!physicsComponent.isInFluid() && physicsComponent.getEnterFluid() < physicsComponent.getCollisionStart()) {
            physicsComponent.setInFluid(true);
            velocityClip = physicsComponent.getEnterFluid();
            physicsComponent.setVelocityExtremaCount(2);
            enteringWater = true;
        }
        else if (physicsComponent.isInFluid() && physicsComponent.getLeaveFluid() < physicsComponent.getCollisionStart()) {
            physicsComponent.setInFluid(false);
            velocityClip = physicsComponent.getLeaveFluid();
            physicsComponent.setVelocityExtremaCount(2);
        }
        if (velocityClip > 0.0 && velocityClip < 1.0) {
            stateUpdater.update(stateBefore, stateAfter, mass, dt * velocityClip, physicsComponent.isOnGround(), forceProviders);
            velocity.assign(stateAfter.velocity);
        }
        if (physicsComponent.isInFluid() && physicsComponent.getSubSurfaceVolume() < boundingBox.getVolume() && physicsComponent.getVelocityExtremaCount() > 0) {
            final double speedBefore = stateBefore.velocity.y;
            final double speedAfter = stateAfter.velocity.y;
            if (speedBefore * speedAfter <= 0.0) {
                physicsComponent.decrementVelocityExtremaCount();
            }
        }
        if (physicsComponent.isSwimming()) {
            final Vector3d externalForce = forceState.externalForce;
            externalForce.y -= stateAfter.velocity.y * (physicsConfig.getSwimmingDampingFactor() / mass);
        }
        if (enteringWater) {
            forceState.externalImpulse.addScaled(stateAfter.velocity, -physicsConfig.getHitWaterImpulseLoss() * mass);
        }
        forceState.displacedMass = physicsComponent.getDisplacedMass();
        forceState.dragCoefficient = physicsComponent.getDragCoefficient(density);
        forceState.gravity = gravity;
        if (entityCollisionProvider.getCount() > 0) {
            final EntityContactData contact = entityCollisionProvider.getContact(0);
            final Ref<EntityStore> contactRef = contact.getEntityReference();
            position.assign(contact.getCollisionPoint());
            physicsComponent.setState(StandardPhysicsProvider.STATE.INACTIVE);
            if (physicsComponent.getImpactConsumer() != null) {
                physicsComponent.getImpactConsumer().onImpact(selfRef, position, contactRef, contact.getCollisionDetailName(), commandBuffer);
            }
            physicsComponent.rotateBody(dt, transformComponent.getRotation());
            physicsComponent.finishTick(transformComponent, velocityComponent);
            return;
        }
        if (physicsComponent.isBounced()) {
            position.assign(contactPosition);
            physicsComponent.incrementBounces();
            SimplePhysicsProvider.computeReflectedVector(velocity, contactNormal, velocity);
            if (bounceCount == -1 || physicsComponent.getBounces() <= bounceCount) {
                velocity.scale(physicsConfig.getBounciness());
            }
            if ((bounceCount != -1 && physicsComponent.getBounces() > bounceCount) || velocity.squaredLength() * dt * dt < physicsConfig.getBounceLimit() * physicsConfig.getBounceLimit()) {
                final boolean hitGround = contactNormal.equals(Vector3d.UP);
                if (!allowRolling && (physicsConfig.isSticksVertically() || hitGround)) {
                    physicsComponent.setState(StandardPhysicsProvider.STATE.RESTING);
                    restingSupport.rest(world, boundingBox, position);
                    physicsComponent.setOnGround(hitGround);
                    if (physicsComponent.getImpactConsumer() != null) {
                        physicsComponent.getImpactConsumer().onImpact(selfRef, position, null, null, commandBuffer);
                    }
                }
                if (allowRolling) {
                    velocity.y = 0.0;
                    velocity.scale(physicsConfig.getRollingFrictionFactor());
                    physicsComponent.setOnGround(hitGround);
                }
                else {
                    velocity.assign(Vector3d.ZERO);
                }
            }
            else if (physicsComponent.getBounceConsumer() != null) {
                physicsComponent.getBounceConsumer().onBounce(selfRef, position, commandBuffer);
            }
            physicsComponent.rotateBody(dt, transformComponent.getRotation());
            physicsComponent.finishTick(transformComponent, velocityComponent);
            return;
        }
        position.add(movement);
        physicsComponent.rotateBody(dt, transformComponent.getRotation());
        physicsComponent.finishTick(transformComponent, velocityComponent);
    }
}
