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

package com.hypixel.hytale.server.npc.corecomponents.world;

import java.util.function.Supplier;
import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.npc.util.BlockPlacementHelper;
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderSensorBase;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorCanPlace;
import com.hypixel.hytale.server.npc.sensorinfo.CachedPositionProvider;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.npc.corecomponents.SensorBase;

public class SensorCanPlace extends SensorBase
{
    protected static final ComponentType<EntityStore, BoundingBox> BOUNDING_BOX_COMPONENT_TYPE;
    protected final Direction direction;
    protected final Offset offset;
    protected final double retryDelay;
    protected final boolean allowEmptyMaterials;
    protected final Vector3d transform;
    protected final CachedPositionProvider positionProvider;
    protected final Vector3d cachedPosition;
    protected boolean cachedResult;
    protected double delay;
    
    public SensorCanPlace(@Nonnull final BuilderSensorCanPlace builder, @Nonnull final BuilderSupport support) {
        super(builder);
        this.transform = new Vector3d();
        this.positionProvider = new CachedPositionProvider();
        this.cachedPosition = new Vector3d();
        this.direction = builder.getDirection(support);
        this.offset = builder.getOffset(support);
        this.retryDelay = builder.getRetryDelay(support);
        this.allowEmptyMaterials = builder.isAllowEmptyMaterials(support);
    }
    
    @Override
    public boolean matches(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, final double dt, @Nonnull final Store<EntityStore> store) {
        if (!super.matches(ref, role, dt, store) || role.getWorldSupport().getBlockToPlace() == null) {
            this.positionProvider.clear();
            return false;
        }
        final BlockType placedBlockType = BlockType.getAssetMap().getAsset(role.getWorldSupport().getBlockToPlace());
        if (placedBlockType == null) {
            this.positionProvider.clear();
            return false;
        }
        final TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final World world = store.getExternalData().getWorld();
        float yaw = transformComponent.getRotation().getYaw();
        final float piQuarter = 0.7853982f;
        yaw = Math.round(yaw / piQuarter) * piQuarter;
        this.direction.apply(this.transform, yaw);
        final BoundingBox boundingBoxComponent = store.getComponent(ref, SensorCanPlace.BOUNDING_BOX_COMPONENT_TYPE);
        if (boundingBoxComponent != null) {
            final Box boundingBox = boundingBoxComponent.getBoundingBox();
            final Box blockBox = BlockBoundingBoxes.getAssetMap().getAsset(placedBlockType.getHitboxTypeIndex()).get(0).getBoundingBox();
            final boolean xNegative = this.transform.x < 0.0;
            final boolean zNegative = this.transform.z < 0.0;
            final boolean xPositive = this.transform.x - 1.0E-5 > 0.0;
            final boolean zPositive = this.transform.z - 1.0E-5 > 0.0;
            final double npcX = xNegative ? boundingBox.min.x : boundingBox.max.x;
            final double npcZ = zNegative ? boundingBox.min.z : boundingBox.max.z;
            final double blockX = xNegative ? blockBox.max.x : blockBox.min.x;
            final double blockZ = zNegative ? blockBox.max.z : blockBox.min.z;
            final double transformX = xNegative ? (-this.transform.x) : this.transform.x;
            final double transformZ = zNegative ? (-this.transform.z) : this.transform.z;
            final double magnitude = Math.sqrt(npcX * npcX * transformX + npcZ * npcZ * transformZ) + Math.sqrt(blockX * blockX * transformX + blockZ * blockZ * transformZ);
            this.transform.setLength(magnitude);
            this.transform.add(xPositive ? 1.0 : 0.0, 0.0, zPositive ? 1.0 : 0.0);
        }
        this.offset.apply(this.transform);
        final Vector3d position = transformComponent.getPosition();
        this.transform.add(position.getX(), position.getY(), position.getZ()).floor();
        final int x = (int)this.transform.getX();
        final int y = (int)this.transform.getY();
        final int z = (int)this.transform.getZ();
        if (!this.cachedPosition.equals(this.transform)) {
            this.delay = 0.0;
        }
        final boolean canPlaceUnitBlock = BlockPlacementHelper.canPlaceUnitBlock(world, placedBlockType, this.allowEmptyMaterials, x, y, z);
        if (canPlaceUnitBlock != this.cachedResult) {
            this.delay = 0.0;
        }
        final double delay = this.delay - dt;
        this.delay = delay;
        if (delay > 0.0) {
            if (this.cachedResult) {
                this.positionProvider.setIsFromCache(true);
                this.positionProvider.setTarget(this.transform);
                return true;
            }
            this.positionProvider.clear();
            return false;
        }
        else {
            this.cachedResult = (canPlaceUnitBlock && BlockPlacementHelper.canPlaceBlock(world, placedBlockType, 0, this.allowEmptyMaterials, x, y, z));
            this.cachedPosition.assign(this.transform);
            this.delay = this.retryDelay;
            if (!this.cachedResult) {
                this.positionProvider.clear();
                return false;
            }
            this.positionProvider.setIsFromCache(false);
            this.positionProvider.setTarget(this.transform);
            return true;
        }
    }
    
    @Override
    public void clearOnce() {
        super.clearOnce();
        this.delay = 0.0;
    }
    
    @Override
    public InfoProvider getSensorInfo() {
        return this.positionProvider;
    }
    
    static {
        BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType();
    }
    
    public enum Direction implements Supplier<String>
    {
        Forward(Vector3d.FORWARD), 
        Backward(Vector3d.BACKWARD), 
        Left(Vector3d.LEFT), 
        Right(Vector3d.RIGHT);
        
        private final Vector3d direction;
        
        private Direction(final Vector3d direction) {
            this.direction = direction;
        }
        
        @Nonnull
        public Vector3d apply(@Nonnull final Vector3d target, final float rotation) {
            return target.assign(this.direction).rotateY(rotation);
        }
        
        @Nonnull
        @Override
        public String get() {
            return this.name();
        }
    }
    
    public enum Offset implements Supplier<String>
    {
        HeadPosition(Vector3d.UP), 
        BodyPosition(Vector3d.ZERO), 
        FootPosition(Vector3d.DOWN);
        
        private final Vector3d offset;
        
        private Offset(final Vector3d offset) {
            this.offset = offset;
        }
        
        @Nonnull
        public Vector3d apply(@Nonnull final Vector3d target) {
            return target.add(this.offset);
        }
        
        @Nonnull
        @Override
        public String get() {
            return this.name();
        }
    }
}
