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

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

import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderBase;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderInRect;

public class BodyMotionWanderInRect extends BodyMotionWanderBase
{
    public static final int LEFT = 1;
    public static final int RIGHT = 2;
    public static final int BOTTOM = 4;
    public static final int TOP = 8;
    public static final int VERTICAL_MASK = 12;
    public static final int HORIZONTAL_MASK = 3;
    protected final double width;
    protected final double depth;
    protected final double halfWidth;
    protected final double halfDepth;
    
    public BodyMotionWanderInRect(@Nonnull final BuilderBodyMotionWanderInRect builder, @Nonnull final BuilderSupport builderSupport) {
        super(builder, builderSupport);
        this.width = builder.getWidth();
        this.halfWidth = this.width / 2.0;
        this.depth = builder.getDepth();
        this.halfDepth = this.depth / 2.0;
    }
    
    @Override
    protected double constrainMove(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nonnull final Vector3d probePosition, @Nonnull final Vector3d targetPosition, final double moveDist, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final NPCEntity npcComponent = componentAccessor.getComponent(ref, NPCEntity.getComponentType());
        assert npcComponent != null;
        final Vector3d leash = npcComponent.getLeashPoint();
        final double leashX = leash.getX();
        final double leashZ = leash.getZ();
        final double endX = targetPosition.x - leashX;
        final double endZ = targetPosition.z - leashZ;
        final int endCode = this.sectorCode(endX, endZ);
        if (endCode == 0) {
            return moveDist;
        }
        final double startX = probePosition.x - leashX;
        final double startZ = probePosition.z - leashZ;
        final int startCode = this.sectorCode(startX, startZ);
        if (startCode != 0) {
            if ((startCode & endCode) != 0x0) {
                if (this.distanceSquared(endX, endZ, endCode) < this.distanceSquared(startX, startZ, startCode)) {
                    return moveDist;
                }
                return 0.0;
            }
            else {
                final int or = startCode | endCode;
                if ((or & 0xC) == 0xC || (or & 0x3) == 0x3) {
                    return 0.0;
                }
                if (this.distanceSquared(endX, endZ, endCode) < this.distanceSquared(startX, startZ, startCode)) {
                    return moveDist;
                }
                return 0.0;
            }
        }
        else {
            final double dx = endX - startX;
            final double dz = endZ - startZ;
            double scaleX;
            if ((endCode & 0x1) == 0x1) {
                scaleX = (-this.halfWidth - startX) / dx;
            }
            else if ((endCode & 0x2) == 0x2) {
                scaleX = (this.halfWidth - startX) / dx;
            }
            else {
                scaleX = 1.0;
            }
            double scaleZ;
            if ((endCode & 0x4) == 0x4) {
                scaleZ = (-this.halfDepth - startZ) / dz;
            }
            else if ((endCode & 0x8) == 0x8) {
                scaleZ = (this.halfDepth - startZ) / dz;
            }
            else {
                scaleZ = 1.0;
            }
            if (scaleX < 0.0 || scaleX > 1.0) {
                throw new IllegalArgumentException("WanderInRect: Constrained X outside of allowed range!");
            }
            if (scaleZ < 0.0 || scaleZ > 1.0) {
                throw new IllegalArgumentException("WanderInRect: Constrained Z outside of allowed range!");
            }
            return moveDist * Math.min(scaleX, scaleZ);
        }
    }
    
    protected int sectorCode(final double x, final double z) {
        int code = 0;
        if (x < -this.halfWidth) {
            code |= 0x1;
        }
        else if (x > this.halfWidth) {
            code |= 0x2;
        }
        if (z < -this.halfDepth) {
            code |= 0x4;
        }
        else if (z > this.halfDepth) {
            code |= 0x8;
        }
        return code;
    }
    
    protected double distanceSquared(final double x, final double z, final int sector) {
        return switch (sector) {
            case 1 -> (x + this.halfWidth) * (x + this.halfWidth);
            case 2 -> (x - this.halfWidth) * (x - this.halfWidth);
            case 4 -> (z + this.halfDepth) * (z + this.halfDepth);
            case 8 -> (z - this.halfDepth) * (z - this.halfDepth);
            case 9 -> (x + this.halfWidth) * (x + this.halfWidth) + (z - this.halfDepth) * (z - this.halfDepth);
            case 5 -> (x + this.halfWidth) * (x + this.halfWidth) + (z + this.halfDepth) * (z + this.halfDepth);
            case 10 -> (x - this.halfWidth) * (x - this.halfWidth) + (z - this.halfDepth) * (z - this.halfDepth);
            case 6 -> (x - this.halfWidth) * (x - this.halfWidth) + (z + this.halfDepth) * (z + this.halfDepth);
            default -> 0.0;
        };
    }
}
