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

package com.hypixel.hytale.server.npc.util;

import com.hypixel.hytale.server.core.modules.collision.WorldUtil;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.modules.collision.CollisionModule;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.modules.collision.BoxBlockIntersectionEvaluator;
import com.hypixel.hytale.server.core.modules.collision.CollisionFilter;
import com.hypixel.hytale.server.core.modules.collision.CollisionResult;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.shape.Box;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;

public class PositionProbeBase
{
    private static final int lastWaterCheckDistanceMinSquared = 25;
    protected boolean touchCeil;
    protected boolean onGround;
    protected boolean inWater;
    protected boolean validPosition;
    protected int groundLevel;
    protected int waterLevel;
    protected int heightOverGround;
    protected int heightOverWater;
    protected int heightOverSurface;
    protected int depthBelowSurface;
    private int lastWaterCheckX;
    private int lastWaterCheckZ;
    private int lastWaterCheckLevel;
    
    public PositionProbeBase() {
        this.validPosition = true;
        this.groundLevel = -1;
        this.waterLevel = -1;
        this.heightOverGround = -1;
        this.heightOverWater = -1;
        this.heightOverSurface = -1;
        this.depthBelowSurface = -1;
        this.lastWaterCheckX = Integer.MAX_VALUE;
        this.lastWaterCheckZ = Integer.MAX_VALUE;
        this.lastWaterCheckLevel = -2;
    }
    
    protected <T> boolean probePosition(@Nonnull final Ref<EntityStore> ref, @Nonnull final Box boundingBox, @Nonnull final Vector3d position, @Nonnull final CollisionResult collisionResult, @Nonnull final T t, @Nonnull final CollisionFilter<BoxBlockIntersectionEvaluator, T> blockTest, final int materialSet, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final World world = componentAccessor.getExternalData().getWorld();
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final ChunkStore chunkStore = world.getChunkStore();
        final long chunkIndex = ChunkUtil.indexChunkFromBlock(position.x, position.z);
        final Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex);
        this.reset();
        if (chunkRef == null || !chunkRef.isValid()) {
            this.waterLevel = -1;
            this.groundLevel = -1;
            this.lastWaterCheckLevel = -2;
            return false;
        }
        final int x = MathUtil.floor(position.x);
        final int y = MathUtil.floor(position.y);
        final int z = MathUtil.floor(position.z);
        this.validPosition = (CollisionModule.get().validatePosition(world, boundingBox, position, materialSet, t, blockTest, collisionResult) != -1);
        final Store<ChunkStore> chunkStoreAccessor = chunkStore.getStore();
        final ChunkColumn chunkColumnComponent = chunkStoreAccessor.getComponent(chunkRef, ChunkColumn.getComponentType());
        final BlockChunk blockChunkComponent = chunkStoreAccessor.getComponent(chunkRef, BlockChunk.getComponentType());
        if (chunkColumnComponent == null || blockChunkComponent == null) {
            this.groundLevel = -1;
            this.waterLevel = -1;
            this.lastWaterCheckLevel = -2;
        }
        else {
            this.groundLevel = blockChunkComponent.getHeight(x, z);
            this.waterLevel = this.updateWaterLevel(chunkStoreAccessor, chunkColumnComponent, blockChunkComponent, x, z);
        }
        this.heightOverGround = y - this.groundLevel;
        if (this.waterLevel < this.groundLevel) {
            this.heightOverSurface = this.heightOverGround;
        }
        else if (y > this.waterLevel) {
            this.heightOverSurface = y - this.waterLevel;
        }
        else {
            this.depthBelowSurface = this.waterLevel - y;
        }
        return this.validPosition;
    }
    
    protected int updateWaterLevel(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumn, @Nonnull final BlockChunk blockChunk, final int x, final int z) {
        if (this.lastWaterCheckLevel < -1 || this.movedFarEnough(x, z)) {
            this.lastWaterCheckX = x;
            this.lastWaterCheckZ = z;
            this.lastWaterCheckLevel = WorldUtil.getWaterLevel(chunkStore, chunkColumn, blockChunk, x, z, this.groundLevel);
        }
        return this.lastWaterCheckLevel;
    }
    
    private boolean movedFarEnough(int x, int z) {
        x -= this.lastWaterCheckX;
        z -= this.lastWaterCheckZ;
        return x * x + z * z > 25;
    }
    
    protected void reset() {
        this.touchCeil = false;
        this.onGround = false;
        this.inWater = false;
        this.validPosition = true;
        this.heightOverGround = -1;
        this.heightOverSurface = -1;
        this.heightOverWater = -1;
        this.depthBelowSurface = -1;
    }
    
    public boolean isValidPosition() {
        return this.validPosition;
    }
    
    public boolean isTouchCeil() {
        return this.touchCeil;
    }
    
    public boolean isOnGround() {
        return this.onGround;
    }
    
    public boolean isInWater() {
        return this.inWater;
    }
    
    public int getGroundLevel() {
        return this.groundLevel;
    }
    
    public int getWaterLevel() {
        return this.waterLevel;
    }
    
    public int getHeightOverGround() {
        return this.heightOverGround;
    }
    
    public int getHeightOverSurface() {
        return this.heightOverSurface;
    }
    
    public int getDepthBelowSurface() {
        return this.depthBelowSurface;
    }
    
    public int getHeightOverWater() {
        return this.heightOverWater;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "PositionProbeBase{touchCeil=" + this.touchCeil + ", onGround=" + this.onGround + ", inWater=" + this.inWater + ", validPosition=" + this.validPosition + ", groundLevel=" + this.groundLevel + ", waterLevel=" + this.waterLevel + ", heightOverGround=" + this.heightOverGround + ", heightOverSurface=" + this.heightOverSurface + ", depthBelowSurface=" + this.depthBelowSurface + ", heightOverWater=" + this.heightOverWater + ", lastWaterCheckX=" + this.lastWaterCheckX + ", lastWaterCheckZ=" + this.lastWaterCheckZ + ", lastWaterCheckLevel=" + this.lastWaterCheckLevel;
    }
}
