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

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

import java.util.function.Supplier;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.server.npc.movement.Steering;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderBodyMotionBase;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionTeleport;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.npc.corecomponents.BodyMotionBase;

public class BodyMotionTeleport extends BodyMotionBase
{
    public static final int MAX_TRIES = 10;
    public static final int MIN_MOVE_CHANGE = 1;
    public static final double TELEPORT_COOLDOWN = 0.5;
    protected static final ComponentType<EntityStore, TransformComponent> TRANSFORM_COMPONENT_TYPE;
    protected static final ComponentType<EntityStore, BoundingBox> BOUNDING_BOX_COMPONENT_TYPE;
    protected final double minOffset;
    protected final double maxOffset;
    protected final double maxYOffset;
    protected final float angle;
    protected final Orientation orientation;
    protected final Vector3d target;
    protected final Vector3d offsetVector;
    protected final Vector3d lastTriedTarget;
    protected int tries;
    protected double cooldown;
    
    public BodyMotionTeleport(@Nonnull final BuilderBodyMotionTeleport builder) {
        super(builder);
        this.target = new Vector3d();
        this.offsetVector = new Vector3d();
        this.lastTriedTarget = new Vector3d();
        final double[] offset = builder.getOffsetRadius();
        this.minOffset = offset[0];
        this.maxOffset = offset[1];
        this.maxYOffset = builder.getMaxYOffset();
        this.angle = builder.getSectorRadians() / 2.0f;
        this.orientation = builder.getOrientation();
    }
    
    @Override
    public void activate(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        this.tries = 10;
    }
    
    @Override
    public boolean computeSteering(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nullable final InfoProvider sensorInfo, final double dt, @Nonnull final Steering desiredSteering, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (sensorInfo == null || !sensorInfo.getPositionProvider().providePosition(this.target)) {
            return false;
        }
        final double dist = this.target.distanceSquaredTo(this.lastTriedTarget);
        if ((this.tries <= 0 && dist < 1.0) || this.tickCooldown(dt)) {
            return false;
        }
        if (dist > 1.0) {
            this.tries = 10;
        }
        this.lastTriedTarget.assign(this.target);
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, BodyMotionTeleport.TRANSFORM_COMPONENT_TYPE);
        assert transformComponent != null;
        final Vector3d selfPosition = transformComponent.getPosition();
        final double distance = selfPosition.distanceSquaredTo(this.target);
        final double maxOffset2 = this.maxOffset * this.maxOffset;
        if (distance <= maxOffset2 + 1.0E-5) {
            return false;
        }
        this.offsetVector.assign(selfPosition).subtract(this.target).setY(0.0);
        this.offsetVector.setLength(RandomExtra.randomRange(this.minOffset, this.maxOffset));
        this.offsetVector.rotateY(RandomExtra.randomRange(-this.angle, this.angle));
        this.target.add(this.offsetVector);
        final MotionController motionController = role.getActiveMotionController();
        final BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, BodyMotionTeleport.BOUNDING_BOX_COMPONENT_TYPE);
        if (!motionController.translateToAccessiblePosition(this.target, (boundingBoxComponent != null) ? boundingBoxComponent.getBoundingBox() : null, this.target.y - this.maxYOffset, this.target.y + this.maxYOffset, componentAccessor) || !motionController.isValidPosition(this.target, componentAccessor)) {
            --this.tries;
            return true;
        }
        switch (this.orientation.ordinal()) {
            case 0: {
                final Vector3f bodyRotation = transformComponent.getRotation();
                componentAccessor.addComponent(ref, Teleport.getComponentType(), Teleport.createExact(this.target, bodyRotation));
                break;
            }
            case 1: {
                final double x = this.lastTriedTarget.getX() - this.target.getX();
                final double y = this.lastTriedTarget.getY() - this.target.getY();
                final double z = this.lastTriedTarget.getZ() - this.target.getZ();
                final Vector3f bodyRotation2 = transformComponent.getRotation();
                float yaw;
                float pitch;
                if (x * x + z * z < 1.0E-5) {
                    yaw = bodyRotation2.getYaw();
                    pitch = bodyRotation2.getPitch();
                }
                else {
                    yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z));
                    pitch = PhysicsMath.pitchFromDirection(x, y, z);
                }
                componentAccessor.addComponent(ref, Teleport.getComponentType(), Teleport.createExact(this.target, new Vector3f(yaw, pitch, bodyRotation2.getRoll())));
                break;
            }
            case 2: {
                final Ref<EntityStore> targetRef = sensorInfo.hasPosition() ? sensorInfo.getPositionProvider().getTarget() : null;
                if (targetRef == null) {
                    return false;
                }
                final TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, BodyMotionTeleport.TRANSFORM_COMPONENT_TYPE);
                assert targetTransformComponent != null;
                final Vector3f bodyRotation3 = targetTransformComponent.getRotation();
                componentAccessor.addComponent(ref, Teleport.getComponentType(), Teleport.createExact(this.target, bodyRotation3));
                break;
            }
        }
        this.tries = 10;
        this.cooldown = 0.5;
        desiredSteering.clear();
        return false;
    }
    
    protected boolean tickCooldown(final double dt) {
        if (this.cooldown > 0.0) {
            this.cooldown -= dt;
            return true;
        }
        return false;
    }
    
    static {
        TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType();
        BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType();
    }
    
    public enum Orientation implements Supplier<String>
    {
        Unchanged("Do not change orientation"), 
        TowardsTarget("Face towards the target"), 
        UseTarget("Use the target's orientation");
        
        private final String description;
        
        private Orientation(final String description) {
            this.description = description;
        }
        
        @Override
        public String get() {
            return this.description;
        }
    }
}
