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

package com.hypixel.hytale.builtin.adventure.objectives.markers.objectivelocation;

import java.util.Arrays;
import java.util.Iterator;
import com.hypixel.hytale.server.core.universe.Universe;
import javax.annotation.Nullable;
import com.hypixel.hytale.builtin.adventure.objectives.task.ObjectiveTask;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.builtin.adventure.objectives.task.UseEntityObjectiveTask;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.assets.TrackOrUpdateObjective;
import com.hypixel.hytale.builtin.adventure.objectives.config.triggercondition.ObjectiveLocationTriggerCondition;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectList;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.ComponentAccessor;
import java.util.UUID;
import java.util.HashSet;
import java.util.List;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.system.tick.ArchetypeTickingSystem;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.OrderPriority;
import com.hypixel.hytale.server.core.modules.entity.system.PlayerSpatialSystem;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.builtin.weather.components.WeatherTracker;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.builtin.adventure.objectives.ObjectiveDataStore;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.builtin.adventure.objectives.Objective;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.protocol.packets.assets.UntrackObjective;
import com.hypixel.hytale.component.RemoveReason;
import java.util.logging.Level;
import com.hypixel.hytale.builtin.adventure.objectives.ObjectivePlugin;
import com.hypixel.hytale.builtin.adventure.objectives.config.ObjectiveLocationMarkerAsset;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.RefSystem;

public class ObjectiveLocationMarkerSystems
{
    public static class InitSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, ObjectiveLocationMarker> objectiveLocationMarkerComponent;
        @Nonnull
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        @Nonnull
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public InitSystem(@Nonnull final ComponentType<EntityStore, ObjectiveLocationMarker> objectiveLocationMarkerComponent) {
            this.objectiveLocationMarkerComponent = objectiveLocationMarkerComponent;
            this.modelComponentType = ModelComponent.getComponentType();
            this.transformComponentType = TransformComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(objectiveLocationMarkerComponent, this.modelComponentType, this.transformComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final ObjectiveLocationMarker objectiveLocationMarkerComponent = store.getComponent(ref, this.objectiveLocationMarkerComponent);
            assert objectiveLocationMarkerComponent != null;
            final ObjectiveLocationMarkerAsset markerAsset = ObjectiveLocationMarkerAsset.getAssetMap().getAsset(objectiveLocationMarkerComponent.objectiveLocationMarkerId);
            if (markerAsset == null) {
                ObjectivePlugin.get().getLogger().at(Level.WARNING).log("Failed to find ObjectiveLocationMarker '%s'. Entity removed!", objectiveLocationMarkerComponent.objectiveLocationMarkerId);
                commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                return;
            }
            if (objectiveLocationMarkerComponent.activeObjectiveUUID != null) {
                final Objective activeObjective = ObjectivePlugin.get().getObjectiveDataStore().loadObjective(objectiveLocationMarkerComponent.activeObjectiveUUID, store);
                if (activeObjective == null) {
                    ObjectivePlugin.get().getLogger().at(Level.WARNING).log("Failed to load Objective with UUID '%s'. Entity removed!", objectiveLocationMarkerComponent.activeObjectiveUUID);
                    commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                    return;
                }
                objectiveLocationMarkerComponent.setActiveObjective(activeObjective);
                objectiveLocationMarkerComponent.setUntrackPacket(new UntrackObjective(objectiveLocationMarkerComponent.activeObjectiveUUID));
            }
            final TransformComponent transformComponent = store.getComponent(ref, this.transformComponentType);
            assert transformComponent != null;
            final Vector3f rotation = transformComponent.getRotation();
            objectiveLocationMarkerComponent.updateLocationMarkerValues(markerAsset, rotation.getYaw(), store);
            final ModelComponent modelComponent = store.getComponent(ref, this.modelComponentType);
            assert modelComponent != null;
            final Model model = modelComponent.getModel();
            commandBuffer.putComponent(ref, this.modelComponentType, new ModelComponent(new Model(model.getModelAssetId(), model.getScale(), model.getRandomAttachmentIds(), model.getAttachments(), objectiveLocationMarkerComponent.getArea().getBoxForEntryArea(), model.getModel(), model.getTexture(), model.getGradientSet(), model.getGradientId(), model.getEyeHeight(), model.getCrouchOffset(), model.getAnimationSetMap(), model.getCamera(), model.getLight(), model.getParticles(), model.getTrails(), model.getPhysicsValues(), model.getDetailBoxes(), model.getPhobia(), model.getPhobiaModelAssetId())));
            commandBuffer.ensureComponent(ref, PrefabCopyableComponent.getComponentType());
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final ObjectiveLocationMarker objectLocationMarkerComponent = store.getComponent(ref, this.objectiveLocationMarkerComponent);
            assert objectLocationMarkerComponent != null;
            final Objective activeObjective = objectLocationMarkerComponent.getActiveObjective();
            if (activeObjective == null) {
                return;
            }
            final ObjectiveDataStore objectiveDataStore = ObjectivePlugin.get().getObjectiveDataStore();
            objectiveDataStore.saveToDisk(objectLocationMarkerComponent.activeObjectiveUUID.toString(), activeObjective);
            objectiveDataStore.unloadObjective(objectLocationMarkerComponent.activeObjectiveUUID);
            if (reason == RemoveReason.REMOVE) {
                commandBuffer.run(theStore -> ObjectivePlugin.get().cancelObjective(objectLocationMarkerComponent.activeObjectiveUUID, theStore));
            }
        }
    }
    
    public static class EnsureNetworkSendableSystem extends HolderSystem<EntityStore>
    {
        @Nonnull
        private final Query<EntityStore> query;
        
        public EnsureNetworkSendableSystem() {
            this.query = (Query<EntityStore>)Query.and(ObjectiveLocationMarker.getComponentType(), Query.not((Query<Object>)NetworkId.getComponentType()));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
    }
    
    public static class TickingSystem extends EntityTickingSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, ObjectiveLocationMarker> objectiveLocationMarkerComponentType;
        @Nonnull
        private final ComponentType<EntityStore, PlayerRef> playerRefComponentType;
        @Nonnull
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        @Nonnull
        private final ComponentType<EntityStore, WeatherTracker> weatherTrackerComponentType;
        @Nonnull
        private final ComponentType<EntityStore, UUIDComponent> uuidComponentType;
        @Nonnull
        private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> playerSpatialComponent;
        @Nonnull
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public TickingSystem(@Nonnull final ComponentType<EntityStore, ObjectiveLocationMarker> objectiveLocationMarkerComponentType, @Nonnull final ComponentType<EntityStore, PlayerRef> playerRefComponentType, @Nonnull final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> playerSpatialComponent) {
            this.transformComponentType = TransformComponent.getComponentType();
            this.weatherTrackerComponentType = WeatherTracker.getComponentType();
            this.uuidComponentType = UUIDComponent.getComponentType();
            this.objectiveLocationMarkerComponentType = objectiveLocationMarkerComponentType;
            this.playerRefComponentType = playerRefComponentType;
            this.playerSpatialComponent = playerSpatialComponent;
            this.query = (Query<EntityStore>)Archetype.of(objectiveLocationMarkerComponentType, this.transformComponentType, this.uuidComponentType);
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, PlayerSpatialSystem.class, OrderPriority.CLOSEST));
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @Override
        public void tick(final float dt, final int systemIndex, @Nonnull final Store<EntityStore> store) {
            final World world = store.getExternalData().getWorld();
            if (!world.getWorldConfig().isObjectiveMarkersEnabled()) {
                return;
            }
            store.tick(this, dt, systemIndex);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final ObjectiveLocationMarker objectiveLocationMarkerComponent = archetypeChunk.getComponent(index, this.objectiveLocationMarkerComponentType);
            assert objectiveLocationMarkerComponent != null;
            final TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType);
            assert transformComponent != null;
            final Ref<EntityStore> entityReference = archetypeChunk.getReferenceTo(index);
            final Vector3d position = transformComponent.getPosition();
            final Objective activeObjective = objectiveLocationMarkerComponent.getActiveObjective();
            if (activeObjective == null) {
                final UUIDComponent uuidComponent = archetypeChunk.getComponent(index, this.uuidComponentType);
                assert uuidComponent != null;
                final UUID uuid = uuidComponent.getUuid();
                this.setupMarker(store, objectiveLocationMarkerComponent, entityReference, position, uuid, commandBuffer);
            }
            else if (!activeObjective.isCompleted()) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent);
                final ObjectList<Ref<EntityStore>> playerReferences = SpatialResource.getThreadLocalReferenceList();
                objectiveLocationMarkerComponent.area.getPlayersInExitArea(spatialResource, playerReferences, position);
                final HashSet<UUID> playersInExitArea = new HashSet<UUID>(playerReferences.size());
                final PlayerRef[] playersInEntryArea = new PlayerRef[playerReferences.size()];
                int playersInEntryAreaSize = 0;
                for (final Ref<EntityStore> playerReference : playerReferences) {
                    final PlayerRef playerRefComponent = commandBuffer.getComponent(playerReference, this.playerRefComponentType);
                    assert playerRefComponent != null;
                    final UUIDComponent playerUuidComponent = commandBuffer.getComponent(playerReference, this.uuidComponentType);
                    assert playerUuidComponent != null;
                    final TransformComponent playerTransformComponent = commandBuffer.getComponent(playerReference, this.transformComponentType);
                    assert playerTransformComponent != null;
                    final WeatherTracker playerWeatherTrackerComponent = commandBuffer.getComponent(playerReference, this.weatherTrackerComponentType);
                    assert playerWeatherTrackerComponent != null;
                    if (!isPlayerInSpecificEnvironment(objectiveLocationMarkerComponent, playerWeatherTrackerComponent, playerTransformComponent, commandBuffer)) {
                        continue;
                    }
                    playersInExitArea.add(playerUuidComponent.getUuid());
                    if (!objectiveLocationMarkerComponent.area.isPlayerInEntryArea(playerTransformComponent.getPosition(), position)) {
                        continue;
                    }
                    playersInEntryArea[playersInEntryAreaSize++] = playerRefComponent;
                }
                final Set<UUID> playerUUIDs = activeObjective.getPlayerUUIDs();
                final Set<UUID> activePlayerUUIDs = activeObjective.getActivePlayerUUIDs();
                final String objectiveId = activeObjective.getObjectiveId();
                updateIncomingPlayers(playersInEntryArea, playersInEntryAreaSize, objectiveLocationMarkerComponent, playerUUIDs, activePlayerUUIDs, objectiveId);
                updateOutgoingPlayers(playersInExitArea, objectiveLocationMarkerComponent, activePlayerUUIDs, objectiveId);
            }
            else {
                commandBuffer.removeEntity(entityReference, RemoveReason.REMOVE);
            }
        }
        
        private void setupMarker(@Nonnull final Store<EntityStore> store, @Nonnull final ObjectiveLocationMarker entity, @Nonnull final Ref<EntityStore> entityReference, @Nonnull final Vector3d position, @Nonnull final UUID uuid, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            if (entity.triggerConditions != null && entity.triggerConditions.length > 0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent);
                if (!entity.area.hasPlayerInExitArea(spatialResource, this.playerRefComponentType, position, commandBuffer)) {
                    return;
                }
                for (final ObjectiveLocationTriggerCondition triggerCondition : entity.triggerConditions) {
                    if (!triggerCondition.isConditionMet(commandBuffer, entityReference, entity)) {
                        return;
                    }
                }
            }
            final ObjectiveLocationMarkerAsset objectiveLocationMarkerAsset = ObjectiveLocationMarkerAsset.getAssetMap().getAsset(entity.objectiveLocationMarkerId);
            if (objectiveLocationMarkerAsset == null) {
                ObjectivePlugin.get().getLogger().at(Level.WARNING).log("Could not find ObjectiveLocationMarker '%s'. Entity removed!", entity.objectiveLocationMarkerId);
                commandBuffer.removeEntity(entityReference, RemoveReason.REMOVE);
                return;
            }
            final World world = store.getExternalData().getWorld();
            final Objective objective = objectiveLocationMarkerAsset.getObjectiveTypeSetup().setup(new HashSet<UUID>(), world.getWorldConfig().getUuid(), uuid, store);
            if (objective == null) {
                ObjectivePlugin.get().getLogger().at(Level.WARNING).log("Objective failed to setup for ObjectiveLocationMarker '%s'. Entity removed!", entity.objectiveLocationMarkerId);
                commandBuffer.removeEntity(entityReference, RemoveReason.REMOVE);
                return;
            }
            entity.setActiveObjective(objective);
            entity.activeObjectiveUUID = objective.getObjectiveUUID();
            entity.setUntrackPacket(new UntrackObjective(entity.activeObjectiveUUID));
        }
        
        private static void updateIncomingPlayers(@Nonnull final PlayerRef[] playersInArea, final int playersInAreaSize, @Nonnull final ObjectiveLocationMarker entity, @Nonnull final Set<UUID> playerUUIDs, @Nonnull final Set<UUID> activePlayerUUIDs, @Nonnull final String objectiveId) {
            if (playersInArea.length == 0) {
                return;
            }
            TrackOrUpdateObjective trackPacket = null;
            final ObjectivePlugin objectiveModule = ObjectivePlugin.get();
            final HytaleLogger logger = objectiveModule.getLogger();
            final ObjectiveDataStore objectiveDataStore = objectiveModule.getObjectiveDataStore();
            for (final PlayerRef playerRef : playersInArea) {
                final UUID playerUUID = playerRef.getUuid();
                if (activePlayerUUIDs.add(playerUUID)) {
                    playerUUIDs.add(playerUUID);
                    logger.at(Level.FINE).log("Player '%s' joined the objective area for marker '%s', current objective '%s' with UUID '%s'", playerRef.getUsername(), entity.objectiveLocationMarkerId, objectiveId, entity.activeObjectiveUUID);
                    if (trackPacket == null) {
                        trackPacket = new TrackOrUpdateObjective(entity.getActiveObjective().toPacket());
                    }
                    playerRef.getPacketHandler().writeNoCache(trackPacket);
                    for (final ObjectiveTask task : entity.getActiveObjective().getCurrentTasks()) {
                        if (task instanceof final UseEntityObjectiveTask useEntityObjectiveTask) {
                            objectiveDataStore.addEntityTaskForPlayer(playerUUID, useEntityObjectiveTask.getAsset().getTaskId(), entity.activeObjectiveUUID);
                        }
                    }
                }
            }
        }
        
        private static void updateOutgoingPlayers(@Nonnull final Set<UUID> playersInArea, @Nonnull final ObjectiveLocationMarker entity, @Nullable final Set<UUID> activePlayerUUIDs, @Nonnull final String objectiveId) {
            if (activePlayerUUIDs == null || activePlayerUUIDs.isEmpty()) {
                return;
            }
            final HytaleLogger logger = ObjectivePlugin.get().getLogger();
            final Iterator<UUID> iterator = activePlayerUUIDs.iterator();
            final Universe universe = Universe.get();
            while (iterator.hasNext()) {
                final UUID uuid = iterator.next();
                if (playersInArea.contains(uuid)) {
                    continue;
                }
                iterator.remove();
                untrackEntityObjectiveForPlayer(entity, uuid);
                final PlayerRef playerRef = universe.getPlayer(uuid);
                logger.at(Level.FINE).log("Player '%s' left the objective area for marker '%s', current objective '%s' with UUID '%s'", (playerRef == null) ? uuid : playerRef.getUsername(), entity.objectiveLocationMarkerId, objectiveId, entity.activeObjectiveUUID);
                if (playerRef == null) {
                    continue;
                }
                playerRef.getPacketHandler().write(entity.getUntrackPacket());
            }
        }
        
        private static boolean isPlayerInSpecificEnvironment(@Nonnull final ObjectiveLocationMarker entity, @Nonnull final WeatherTracker weatherTracker, @Nonnull final TransformComponent transform, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
            if (entity.environmentIndexes == null) {
                return true;
            }
            weatherTracker.updateEnvironment(transform, componentAccessor);
            final int environmentIndex = weatherTracker.getEnvironmentId();
            return Arrays.binarySearch(entity.environmentIndexes, environmentIndex) >= 0;
        }
        
        private static void untrackEntityObjectiveForPlayer(@Nonnull final ObjectiveLocationMarker entity, @Nonnull final UUID playerUUID) {
            final ObjectiveDataStore objectiveDataStore = ObjectivePlugin.get().getObjectiveDataStore();
            for (final ObjectiveTask task : entity.getActiveObjective().getCurrentTasks()) {
                if (task instanceof final UseEntityObjectiveTask useEntityObjectiveTask) {
                    final String taskId = useEntityObjectiveTask.getAsset().getTaskId();
                    objectiveDataStore.removeEntityTaskForPlayer(entity.activeObjectiveUUID, taskId, playerUUID);
                }
            }
        }
    }
}
