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

package com.hypixel.hytale.builtin.portals.systems.voidevent;

import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.component.query.Query;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.Collection;
import java.util.List;
import com.hypixel.hytale.common.util.RandomUtil;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.FitsAPortal;
import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery;
import com.hypixel.hytale.builtin.portals.utils.posqueries.generators.SearchBelow;
import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.NotNearPointXZ;
import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate;
import com.hypixel.hytale.builtin.portals.utils.posqueries.generators.SearchCone;
import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.NotNearAnyInHashGrid;
import com.hypixel.hytale.builtin.portals.resources.PortalWorld;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.builtin.portals.components.voidevent.config.InvasionPortalConfig;
import com.hypixel.hytale.builtin.portals.components.voidevent.config.VoidEventConfig;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.builtin.portals.utils.spatial.SpatialHashGrid;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.concurrent.Executor;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.builtin.portals.components.voidevent.VoidSpawner;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.builtin.portals.components.voidevent.VoidEvent;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.math.vector.Vector3d;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.tick.DelayedEntitySystem;

public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem<EntityStore>
{
    private static final int MAX_PORTALS = 24;
    private CompletableFuture<Vector3d> findPortalSpawnPos;
    
    public VoidInvasionPortalsSpawnSystem() {
        super(2.0f);
    }
    
    @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 VoidEvent voidEvent = archetypeChunk.getComponent(index, VoidEvent.getComponentType());
        final World world = store.getExternalData().getWorld();
        if (this.findPortalSpawnPos == null) {
            final SpatialHashGrid<Ref<EntityStore>> spawners = this.cleanupAndGetSpawners(voidEvent);
            if (spawners.size() >= 24) {
                return;
            }
            this.findPortalSpawnPos = this.findPortalSpawnPosition(world, voidEvent, commandBuffer);
        }
        else {
            if (!this.findPortalSpawnPos.isDone()) {
                return;
            }
            Vector3d portalPos;
            try {
                portalPos = this.findPortalSpawnPos.join();
                this.findPortalSpawnPos = null;
            }
            catch (final Throwable t) {
                HytaleLogger.getLogger().at(Level.SEVERE).withCause(t).log("Error trying to find a void event spawn position");
                return;
            }
            if (portalPos == null) {
                return;
            }
            final Holder<EntityStore> voidSpawnerHolder = EntityStore.REGISTRY.newHolder();
            voidSpawnerHolder.addComponent(VoidSpawner.getComponentType(), new VoidSpawner());
            voidSpawnerHolder.addComponent(TransformComponent.getComponentType(), new TransformComponent(portalPos, new Vector3f()));
            final Ref<EntityStore> voidSpawner = commandBuffer.addEntity(voidSpawnerHolder, AddReason.SPAWN);
            voidEvent.getVoidSpawners().add(portalPos, voidSpawner);
            final VoidEventConfig eventConfig = voidEvent.getConfig(world);
            if (eventConfig == null) {
                HytaleLogger.getLogger().at(Level.WARNING).log("There's a Void Event entity but no void event config in the gameplay config");
                return;
            }
            final InvasionPortalConfig invasionPortalConfig = eventConfig.getInvasionPortalConfig();
            final Vector3i portalBlockPos = portalPos.toVector3i();
            world.getChunkAsync(ChunkUtil.indexChunkFromBlock(portalBlockPos.x, portalBlockPos.z)).thenAcceptAsync(chunk -> {
                final BlockType blockType = invasionPortalConfig.getBlockType();
                chunk.setBlock(portalBlockPos.x, portalBlockPos.y, portalBlockPos.z, blockType, 4);
            }, (Executor)world);
        }
    }
    
    private CompletableFuture<Vector3d> findPortalSpawnPosition(final World world, final VoidEvent voidEvent, final CommandBuffer<EntityStore> commandBuffer) {
        final PortalWorld portalWorld = commandBuffer.getResource(PortalWorld.getResourceType());
        if (!portalWorld.exists()) {
            return null;
        }
        final Vector3d spawnPos = portalWorld.getSpawnPoint().getPosition();
        final Transform playerTransform = this.findRandomPlayerTransform(world, commandBuffer);
        if (playerTransform == null) {
            return null;
        }
        final Vector3d origin = playerTransform.getPosition().clone().add(0.0, 5.0, 0.0);
        final Vector3d direction = playerTransform.getDirection();
        final SpatialHashGrid<Ref<EntityStore>> existingSpawners = voidEvent.getVoidSpawners();
        final NotNearAnyInHashGrid noNearbySpawners = new NotNearAnyInHashGrid(existingSpawners, 62.0);
        return CompletableFuture.supplyAsync(() -> new SearchCone(direction, 48.0, 64.0, 90.0, 8).filter(noNearbySpawners).filter(new NotNearPointXZ(spawnPos, 18.0)).then(new SearchBelow(12)).filter(new FitsAPortal()).execute(world, origin).orElse(null), world);
    }
    
    @Nullable
    private Transform findRandomPlayerTransform(final World world, final CommandBuffer<EntityStore> commandBuffer) {
        final Collection<PlayerRef> playerRefs = world.getPlayerRefs();
        if (playerRefs.isEmpty()) {
            return null;
        }
        final List<Ref<EntityStore>> players = new ObjectArrayList<Ref<EntityStore>>(playerRefs.size());
        for (final PlayerRef playerRef : playerRefs) {
            players.add(playerRef.getReference());
        }
        final Ref<EntityStore> randomPlayer = RandomUtil.selectRandom((List<? extends Ref<EntityStore>>)players);
        final TransformComponent transformComponent = commandBuffer.getComponent(randomPlayer, TransformComponent.getComponentType());
        assert transformComponent != null;
        return transformComponent.getTransform();
    }
    
    private SpatialHashGrid<Ref<EntityStore>> cleanupAndGetSpawners(final VoidEvent voidEvent) {
        final SpatialHashGrid<Ref<EntityStore>> spawners = voidEvent.getVoidSpawners();
        spawners.removeIf(ref -> !ref.isValid());
        return spawners;
    }
    
    @Nullable
    @Override
    public Query<EntityStore> getQuery() {
        return VoidEvent.getComponentType();
    }
}
