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

package com.hypixel.hytale.server.flock.commands;

import com.hypixel.hytale.server.flock.FlockMembershipSystems;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.flock.FlockMembership;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
import java.util.function.Predicate;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectList;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import java.util.List;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import java.util.function.BiPredicate;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;

public class NPCFlockCommand extends AbstractCommandCollection
{
    private static final double ENTITY_IN_VIEW_DISTANCE = 8.0;
    private static final float ENTITY_IN_VIEW_ANGLE = 30.0f;
    private static final int ENTITY_IN_VIEW_HEIGHT = 2;
    
    public NPCFlockCommand() {
        super("flock", "server.commands.npc.flock.desc");
        this.addSubCommand(new LeaveCommand());
        this.addSubCommand(new GrabCommand());
        this.addSubCommand(new JoinCommand());
        this.addSubCommand(new PlayerLeaveCommand());
    }
    
    public static int forNpcEntitiesInViewCone(@Nonnull final Ref<EntityStore> playerReference, @Nonnull final Store<EntityStore> store, @Nonnull final BiPredicate<Ref<EntityStore>, NPCEntity> predicate) {
        final ComponentType<EntityStore, TransformComponent> transformComponentType = TransformComponent.getComponentType();
        final TransformComponent transformComponent = store.getComponent(playerReference, transformComponentType);
        assert transformComponent != null;
        final Vector3d position = transformComponent.getPosition();
        final HeadRotation headRotationComponent = store.getComponent(playerReference, HeadRotation.getComponentType());
        assert headRotationComponent != null;
        final Vector3f headRotation = headRotationComponent.getRotation();
        final float lookYaw = headRotation.getYaw();
        final double x = position.getX();
        final double y = position.getY();
        final double z = position.getZ();
        final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource());
        final ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
        spatialResource.getSpatialStructure().collect(position, 8.0, results);
        final ComponentType<EntityStore, NPCEntity> npcComponentType = NPCEntity.getComponentType();
        assert npcComponentType != null;
        int count = 0;
        for (final Ref<EntityStore> targetRef : results) {
            final NPCEntity targetNpcComponent = store.getComponent(targetRef, npcComponentType);
            assert targetNpcComponent != null;
            final TransformComponent entityTransformComponent = store.getComponent(targetRef, transformComponentType);
            assert entityTransformComponent != null;
            final Vector3d entityPosition = entityTransformComponent.getPosition();
            if (Math.abs(entityPosition.getY() - y) >= 2.0 || !NPCPhysicsMath.inViewSector(x, z, lookYaw, 0.5235988f, entityPosition.getX(), entityPosition.getZ()) || !predicate.test(targetRef, targetNpcComponent)) {
                continue;
            }
            ++count;
        }
        return count;
    }
    
    public static boolean anyEntityInViewCone(@Nonnull final Ref<EntityStore> playerReference, @Nonnull final Store<EntityStore> store, @Nonnull final Predicate<Ref<EntityStore>> predicate) {
        final ComponentType<EntityStore, TransformComponent> transformComponentType = TransformComponent.getComponentType();
        final TransformComponent transformComponent = store.getComponent(playerReference, transformComponentType);
        assert transformComponent != null;
        final Vector3d position = transformComponent.getPosition();
        final HeadRotation headRotationComponent = store.getComponent(playerReference, HeadRotation.getComponentType());
        assert headRotationComponent != null;
        final Vector3f headRotation = headRotationComponent.getRotation();
        final float lookYaw = headRotation.getYaw();
        final double x = position.getX();
        final double y = position.getY();
        final double z = position.getZ();
        final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource());
        final ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
        spatialResource.getSpatialStructure().ordered(position, 8.0, results);
        for (final Ref<EntityStore> entityRef : results) {
            final TransformComponent entityTransformComponent = store.getComponent(entityRef, transformComponentType);
            assert entityTransformComponent != null;
            final Vector3d entityPosition = entityTransformComponent.getPosition();
            if (Math.abs(entityPosition.getY() - y) < 2.0 && NPCPhysicsMath.inViewSector(x, z, lookYaw, 0.5235988f, entityPosition.getX(), entityPosition.getZ()) && predicate.test(entityRef)) {
                return true;
            }
        }
        return false;
    }
    
    public static class LeaveCommand extends AbstractPlayerCommand
    {
        public LeaveCommand() {
            super("leave", "server.commands.npc.flock.leave.desc");
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final int count = NPCFlockCommand.forNpcEntitiesInViewCone(ref, store, (targetRef, targetNpcComponent) -> {
                store.tryRemoveComponent(targetRef, FlockMembership.getComponentType());
                return true;
            });
            context.sendMessage(Message.translation("server.commands.npc.flock.removedFromFlock").param("count", count));
        }
    }
    
    public static class GrabCommand extends AbstractPlayerCommand
    {
        public GrabCommand() {
            super("grab", "server.commands.npc.flock.grab.desc");
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final int count = NPCFlockCommand.forNpcEntitiesInViewCone(ref, store, (targetRef, targetNpcComponent) -> {
                final FlockMembership membership = store.getComponent(targetRef, FlockMembership.getComponentType());
                if (membership == null) {
                    Ref<EntityStore> flockReference = FlockPlugin.getFlockReference(ref, store);
                    if (flockReference == null) {
                        flockReference = FlockPlugin.createFlock(store, targetNpcComponent.getRole());
                        FlockMembershipSystems.join(ref, flockReference, store);
                    }
                    FlockMembershipSystems.join(targetRef, flockReference, store);
                    return true;
                }
                else {
                    return false;
                }
            });
            context.sendMessage(Message.translation("server.commands.npc.flock.addedToFlock").param("count", count));
        }
    }
    
    public static class JoinCommand extends AbstractPlayerCommand
    {
        public JoinCommand() {
            super("join", "server.commands.npc.flock.join.desc");
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final boolean success = NPCFlockCommand.anyEntityInViewCone(ref, store, npcReference -> {
                final Ref<EntityStore> flockReference = FlockPlugin.getFlockReference(npcReference, store);
                if (flockReference != null) {
                    FlockMembershipSystems.join(ref, flockReference, store);
                    return true;
                }
                else {
                    return false;
                }
            });
            if (!success) {
                context.sendMessage(Message.translation("server.commands.npc.flock.resultJoinFlock").param("status", "Failed"));
            }
            else {
                world.execute(() -> {
                    final String status = FlockPlugin.isFlockMember(ref, store) ? "Succeeded" : "Failed";
                    context.sendMessage(Message.translation("server.commands.npc.flock.resultJoinFlock").param("status", status));
                });
            }
        }
    }
    
    public static class PlayerLeaveCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private static final Message MESSAGE_COMMANDS_NPC_FLOCK_LEFT_FLOCK;
        @Nonnull
        private static final Message MESSAGE_COMMANDS_NPC_FLOCK_FAILED_LEAVE_FLOCK;
        
        public PlayerLeaveCommand() {
            super("playerleave", "server.commands.npc.flock.playerleave.desc");
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            if (store.removeComponentIfExists(ref, FlockMembership.getComponentType())) {
                context.sendMessage(PlayerLeaveCommand.MESSAGE_COMMANDS_NPC_FLOCK_LEFT_FLOCK);
            }
            else {
                context.sendMessage(PlayerLeaveCommand.MESSAGE_COMMANDS_NPC_FLOCK_FAILED_LEAVE_FLOCK);
            }
        }
        
        static {
            MESSAGE_COMMANDS_NPC_FLOCK_LEFT_FLOCK = Message.translation("server.commands.npc.flock.leftFlock");
            MESSAGE_COMMANDS_NPC_FLOCK_FAILED_LEAVE_FLOCK = Message.translation("server.commands.npc.flock.failedLeaveFlock");
        }
    }
}
