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

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

import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.debug.DebugUtils;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.math.matrix.Matrix4d;
import com.hypixel.hytale.math.vector.Vector3f;
import java.util.concurrent.ThreadLocalRandom;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.npc.role.RoleDebugFlags;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.role.support.WorldSupport;
import com.hypixel.hytale.server.npc.components.messaging.BeaconSupport;
import com.hypixel.hytale.server.npc.role.support.PositionCache;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider;
import com.hypixel.hytale.server.npc.role.Role;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderActionBase;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionBeacon;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import java.util.List;
import com.hypixel.hytale.server.npc.corecomponents.ActionBase;

public class ActionBeacon extends ActionBase
{
    protected final String message;
    protected final double range;
    protected final int[] targetGroups;
    protected final int targetToSendSlot;
    protected final double expirationTime;
    protected final int sendCount;
    @Nullable
    protected final List<Ref<EntityStore>> sendList;
    
    public ActionBeacon(@Nonnull final BuilderActionBeacon builderActionBeacon, @Nonnull final BuilderSupport support) {
        super(builderActionBeacon);
        this.message = builderActionBeacon.getMessage(support);
        this.range = builderActionBeacon.getRange();
        this.targetGroups = builderActionBeacon.getTargetGroups(support);
        this.targetToSendSlot = builderActionBeacon.getTargetToSendSlot(support);
        this.expirationTime = builderActionBeacon.getExpirationTime();
        this.sendCount = builderActionBeacon.getSendCount();
        this.sendList = ((this.sendCount > 0) ? new ObjectArrayList<Ref<EntityStore>>(this.sendCount) : null);
    }
    
    @Override
    public void registerWithSupport(@Nonnull final Role role) {
        role.getPositionCache().requireEntityDistanceUnsorted(this.range);
    }
    
    @Override
    public boolean canExecute(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, final InfoProvider sensorInfo, final double dt, @Nonnull final Store<EntityStore> store) {
        return super.canExecute(ref, role, sensorInfo, dt, store) && (this.targetToSendSlot == Integer.MIN_VALUE || role.getMarkedEntitySupport().hasMarkedEntityInSlot(this.targetToSendSlot));
    }
    
    @Override
    public boolean execute(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, final InfoProvider sensorInfo, final double dt, @Nonnull final Store<EntityStore> store) {
        super.execute(ref, role, sensorInfo, dt, store);
        final Ref<EntityStore> target = (this.targetToSendSlot >= 0) ? role.getMarkedEntitySupport().getMarkedEntityRef(this.targetToSendSlot) : ref;
        final PositionCache positionCache = role.getPositionCache();
        if (this.sendCount <= 0) {
            positionCache.forEachNPCUnordered(this.range, ActionBeacon::filterNPCs, (_ref, _this, _target, _self) -> _this.sendNPCMessage(_self, _ref, _target, _self.getStore()), this, role, target, ref, store);
            return true;
        }
        positionCache.forEachNPCUnordered(this.range, ActionBeacon::filterNPCs, (npcEntity, _this, _sendList, _self) -> RandomExtra.reservoirSample(npcEntity, _this.sendCount, _sendList), this, role, this.sendList, ref, store);
        for (int i = 0; i < this.sendList.size(); ++i) {
            this.sendNPCMessage(ref, this.sendList.get(i), target, store);
        }
        this.sendList.clear();
        return true;
    }
    
    protected static boolean filterNPCs(@Nonnull final Ref<EntityStore> ref, @Nonnull final ActionBeacon _this, @Nonnull final Role role, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        return ref.getStore().getComponent(ref, BeaconSupport.getComponentType()) != null && WorldSupport.isGroupMember(role.getRoleIndex(), ref, _this.targetGroups, componentAccessor);
    }
    
    protected void sendNPCMessage(@Nonnull final Ref<EntityStore> self, @Nonnull final Ref<EntityStore> targetRef, @Nonnull final Ref<EntityStore> target, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final NPCEntity npcComponent = componentAccessor.getComponent(self, NPCEntity.getComponentType());
        assert npcComponent != null;
        final Role role = npcComponent.getRole();
        if (role.getDebugSupport().isDebugFlagSet(RoleDebugFlags.BeaconMessages)) {
            NPCPlugin.get().getLogger().atInfo().log("ID %d sent message '%s' with target ID %d to ID %d", self.getIndex(), this.message, target.getIndex(), targetRef.getIndex());
            final ThreadLocalRandom random = ThreadLocalRandom.current();
            final Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat());
            final Matrix4d matrix = new Matrix4d();
            matrix.identity();
            final Matrix4d tmp = new Matrix4d();
            final TransformComponent transformComponent = componentAccessor.getComponent(self, TransformComponent.getComponentType());
            assert transformComponent != null;
            final Vector3d pos = transformComponent.getPosition();
            final ModelComponent modelComponent = componentAccessor.getComponent(self, ModelComponent.getComponentType());
            assert modelComponent != null;
            final Model model = modelComponent.getModel();
            double x = pos.x;
            double y = pos.y + ((model != null) ? model.getEyeHeight(self, componentAccessor) : 0.0f);
            double z = pos.z;
            matrix.translate(x, y + random.nextFloat() * 0.5 - 0.25, z);
            final TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TransformComponent.getComponentType());
            assert targetTransformComponent != null;
            final Vector3d targetPos = targetTransformComponent.getPosition();
            final ModelComponent targetModelComponent = componentAccessor.getComponent(targetRef, ModelComponent.getComponentType());
            final float targetEyeHeight = (targetModelComponent != null) ? targetModelComponent.getModel().getEyeHeight(targetRef, componentAccessor) : 0.0f;
            x -= targetPos.getX();
            y -= targetPos.getY() + targetEyeHeight;
            z -= targetPos.getZ();
            final double angleY = Math.atan2(-z, -x);
            matrix.rotateAxis(angleY + 1.5707963705062866, 0.0, 1.0, 0.0, tmp);
            final double angleX = Math.atan2(Math.sqrt(x * x + z * z), -y);
            matrix.rotateAxis(angleX, 1.0, 0.0, 0.0, tmp);
            DebugUtils.addArrow(componentAccessor.getExternalData().getWorld(), matrix, color, pos.distanceTo(targetPos), 5.0f, true);
        }
        final BeaconSupport beaconSupportComponent = componentAccessor.getComponent(targetRef, BeaconSupport.getComponentType());
        if (beaconSupportComponent != null) {
            beaconSupportComponent.postMessage(this.message, target, this.expirationTime);
        }
    }
}
