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

package com.hypixel.hytale.server.spawning.blockstates;

import com.hypixel.hytale.server.core.universe.world.meta.BlockState;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab;
import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData;
import com.hypixel.hytale.server.core.entity.reference.PersistentRef;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.entity.nameplate.Nameplate;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import com.hypixel.hytale.component.RemoveReason;
import java.util.logging.Level;
import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;

public class SpawnMarkerBlockStateSystems
{
    private static final HytaleLogger LOGGER;
    
    private static void createMarker(@Nonnull final Ref<ChunkStore> ref, @Nonnull final SpawnMarkerBlockState state, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
        final StateData data = state.getBlockType().getState();
        if (!(data instanceof SpawnMarkerBlockState.Data)) {
            return;
        }
        final SpawnMarkerBlockState.Data stateData = (SpawnMarkerBlockState.Data)data;
        final SpawnMarker marker = SpawnMarker.getAssetMap().getAsset(stateData.getSpawnMarker());
        if (marker == null) {
            SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log(String.format("Marker %s does not exist!", stateData.getSpawnMarker()));
            commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
            return;
        }
        final Vector3i pos = state.getBlockPosition();
        final Vector3i offset = stateData.getMarkerOffset();
        if (offset != null) {
            pos.add(offset);
        }
        final SpawnMarkerEntity spawnMarker = new SpawnMarkerEntity();
        spawnMarker.setSpawnMarker(marker);
        final Holder<EntityStore> holder = EntityStore.REGISTRY.newHolder();
        holder.addComponent(SpawnMarkerEntity.getComponentType(), spawnMarker);
        holder.addComponent(SpawnMarkerBlockReference.getComponentType(), new SpawnMarkerBlockReference(state.getBlockPosition()));
        final Vector3d markerPos = pos.toVector3d();
        markerPos.add(0.5, 0.0, 0.5);
        holder.addComponent(Nameplate.getComponentType(), new Nameplate(marker.getId()));
        holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(markerPos, Vector3f.ZERO));
        final UUIDComponent uuidComponent = holder.ensureAndGetComponent(UUIDComponent.getComponentType());
        final Model model = SpawnMarkerEntity.getModel(marker);
        holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model));
        holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(model.toReference()));
        holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType());
        final Ref<EntityStore> markerRef = store.addEntity(holder, AddReason.SPAWN);
        final PersistentRef persistentRef = new PersistentRef();
        persistentRef.setEntity(markerRef, uuidComponent.getUuid());
        state.setSpawnMarkerReference(persistentRef);
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
    
    public static class AddOrRemove extends RefSystem<ChunkStore>
    {
        private final ComponentType<ChunkStore, SpawnMarkerBlockState> componentType;
        
        public AddOrRemove(final ComponentType<ChunkStore, SpawnMarkerBlockState> componentType) {
            this.componentType = componentType;
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return this.componentType;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<ChunkStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<ChunkStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
            if (reason == RemoveReason.REMOVE) {
                final SpawnMarkerBlockState state = store.getComponent(ref, this.componentType);
                final PersistentRef markerReference = state.getSpawnMarkerReference();
                if (markerReference == null) {
                    return;
                }
                final World world = store.getExternalData().getWorld();
                world.execute(() -> {
                    final Store<EntityStore> entityStore = world.getEntityStore().getStore();
                    final Ref<EntityStore> marker = markerReference.getEntity(entityStore);
                    if (marker != null) {
                        entityStore.removeEntity(marker, RemoveReason.REMOVE);
                    }
                });
            }
        }
    }
    
    public static class TickHeartbeat extends EntityTickingSystem<ChunkStore>
    {
        private final ComponentType<ChunkStore, SpawnMarkerBlockState> componentType;
        
        public TickHeartbeat(final ComponentType<ChunkStore, SpawnMarkerBlockState> componentType) {
            this.componentType = componentType;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return this.componentType;
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<ChunkStore> archetypeChunk, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
            final SpawnMarkerBlockState state = archetypeChunk.getComponent(index, this.componentType);
            if (state.getSpawnMarkerReference() == null) {
                final Ref<ChunkStore> ref = archetypeChunk.getReferenceTo(index);
                SpawnMarkerBlockStateSystems.createMarker(ref, state, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer);
            }
            if (state.getSpawnMarkerReference().getEntity(store.getExternalData().getWorld().getEntityStore().getStore()) != null) {
                state.refreshMarkerLostTimeout();
            }
            else if (state.tickMarkerLostTimeout(dt)) {
                final Ref<ChunkStore> ref = archetypeChunk.getReferenceTo(index);
                SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log("Creating new spawn marker due to desync with entity: %s", ref);
                SpawnMarkerBlockStateSystems.createMarker(ref, state, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer);
            }
        }
    }
    
    public static class SpawnMarkerAddedFromExternal extends RefSystem<EntityStore>
    {
        @Nonnull
        private final Query<EntityStore> query;
        
        public SpawnMarkerAddedFromExternal(final ComponentType<EntityStore, SpawnMarkerBlockReference> componentType) {
            this.query = (Query<EntityStore>)Query.and(componentType, Query.or(FromWorldGen.getComponentType(), FromPrefab.getComponentType()));
        }
        
        @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) {
            commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
    }
    
    public static class SpawnMarkerTickHeartbeat extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, SpawnMarkerBlockReference> componentType;
        
        public SpawnMarkerTickHeartbeat(final ComponentType<EntityStore, SpawnMarkerBlockReference> componentType) {
            this.componentType = componentType;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.componentType;
        }
        
        @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 SpawnMarkerBlockReference marker = archetypeChunk.getComponent(index, this.componentType);
            final Vector3i pos = marker.getBlockPosition();
            final WorldChunk chunk = store.getExternalData().getWorld().getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(pos.x, pos.z));
            if (chunk != null) {
                final BlockState state = chunk.getState(pos.x, pos.y, pos.z);
                if (!(state instanceof SpawnMarkerBlockState)) {
                    final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                    commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                    SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log("Removing block spawn marker due to blockstate mismatch: %s", ref);
                }
                else {
                    marker.refreshOriginLostTimeout();
                }
            }
            else if (marker.tickOriginLostTimeout(dt)) {
                final Ref<EntityStore> ref2 = archetypeChunk.getReferenceTo(index);
                commandBuffer.removeEntity(ref2, RemoveReason.REMOVE);
                SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log("Removing block spawn marker due to origin chunk being unloaded: %s", ref2);
            }
        }
    }
}
