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

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

import java.util.function.Supplier;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.math.iterator.BlockIterator;

public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure
{
    public static final ThreadLocal<RayBlockHitTest> THREAD_LOCAL;
    @Nullable
    private World world;
    @Nullable
    private WorldChunk chunk;
    private final Vector3d origin;
    private final Vector3d direction;
    private int blockSet;
    private final Vector3d hitPosition;
    private short lastBlockRevision;
    
    public RayBlockHitTest() {
        this.origin = new Vector3d();
        this.direction = new Vector3d();
        this.hitPosition = new Vector3d(Vector3d.MIN);
    }
    
    @Override
    public boolean accept(final int x, final int y, final int z, final double px, final double py, final double pz, final double qx, final double qy, final double qz) {
        if (!ChunkUtil.isInsideChunk(this.chunk.getX(), this.chunk.getZ(), x, z)) {
            this.chunk = this.world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z));
            if (this.chunk == null) {
                this.hitPosition.assign(Vector3d.MIN);
                return false;
            }
        }
        final int blockId = this.chunk.getBlock(x, y, z);
        if (blockId == 0) {
            return true;
        }
        if (blockId == 1) {
            return false;
        }
        this.lastBlockRevision = this.chunk.getBlockChunk().getSectionAtBlockY(y).getLocalChangeCounter();
        if (BlockSetModule.getInstance().blockInSet(this.blockSet, blockId)) {
            this.hitPosition.assign(x, y, z);
        }
        return false;
    }
    
    @Nonnull
    public Vector3d getHitPosition() {
        return this.hitPosition;
    }
    
    public short getLastBlockRevision() {
        return this.lastBlockRevision;
    }
    
    public boolean init(@Nonnull final Ref<EntityStore> ref, final int blockSet, final float pitch, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final ModelComponent modelComponent = componentAccessor.getComponent(ref, ModelComponent.getComponentType());
        assert modelComponent != null;
        final HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType());
        assert headRotationComponent != null;
        final World world = componentAccessor.getExternalData().getWorld();
        this.blockSet = blockSet;
        this.origin.assign(transformComponent.getPosition());
        final Vector3d origin = this.origin;
        origin.y += modelComponent.getModel().getEyeHeight();
        this.world = world;
        this.chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(this.origin.x, this.origin.z));
        if (this.chunk == null) {
            return false;
        }
        final float yaw = headRotationComponent.getRotation().getYaw();
        this.direction.assign(yaw, pitch);
        return true;
    }
    
    public boolean run(final double range) {
        BlockIterator.iterate(this.origin, this.direction, range, this);
        return !this.hitPosition.equals(Vector3d.MIN);
    }
    
    public void clear() {
        this.world = null;
        this.chunk = null;
        this.hitPosition.assign(Vector3d.MIN);
    }
    
    static {
        THREAD_LOCAL = ThreadLocal.withInitial((Supplier<? extends RayBlockHitTest>)RayBlockHitTest::new);
    }
}
