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

package com.hypixel.hytale.server.core.modules.blockhealth;

import java.util.List;
import java.util.Collection;
import java.util.Iterator;
import java.time.Instant;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.world.UpdateBlockDamage;
import com.hypixel.hytale.protocol.BlockPosition;
import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Map;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.EcsEvent;
import javax.annotation.Nullable;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.asset.type.gameplay.WorldConfig;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.server.core.modules.time.TimeModule;
import com.hypixel.hytale.server.core.modules.LegacyModule;
import com.hypixel.hytale.component.ComponentRegistryProxy;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.ComponentType;
import javax.annotation.Nonnull;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class BlockHealthModule extends JavaPlugin
{
    @Nonnull
    public static final PluginManifest MANIFEST;
    private static final long SECONDS_UNTIL_REGENERATION = 5L;
    private static final float HEALING_PER_SECOND = 0.1f;
    private static BlockHealthModule instance;
    private ComponentType<ChunkStore, BlockHealthChunk> blockHealthChunkComponentType;
    
    public BlockHealthModule(@Nonnull final JavaPluginInit init) {
        super(init);
        BlockHealthModule.instance = this;
    }
    
    public static BlockHealthModule get() {
        return BlockHealthModule.instance;
    }
    
    @Override
    protected void setup() {
        final ComponentRegistryProxy<ChunkStore> chunkStoreRegistry = this.getChunkStoreRegistry();
        this.blockHealthChunkComponentType = chunkStoreRegistry.registerComponent(BlockHealthChunk.class, "BlockHealthChunk", BlockHealthChunk.CODEC);
        this.getEntityStoreRegistry().registerSystem(new PlaceBlockEventSystem());
        chunkStoreRegistry.registerSystem(new EnsureBlockHealthSystem(this.blockHealthChunkComponentType));
        chunkStoreRegistry.registerSystem(new BlockHealthSystem(this.blockHealthChunkComponentType));
        chunkStoreRegistry.registerSystem((ISystem<ChunkStore>)new BlockHealthPacketSystem(this.blockHealthChunkComponentType));
    }
    
    public ComponentType<ChunkStore, BlockHealthChunk> getBlockHealthChunkComponentType() {
        return this.blockHealthChunkComponentType;
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(BlockHealthModule.class).depends(LegacyModule.class).depends(TimeModule.class).build();
    }
    
    public static class PlaceBlockEventSystem extends EntityEventSystem<EntityStore, PlaceBlockEvent>
    {
        @Nonnull
        private static final ComponentType<ChunkStore, BlockHealthChunk> BLOCK_HEALTH_CHUNK_COMPONENT_TYPE;
        
        public PlaceBlockEventSystem() {
            super(PlaceBlockEvent.class);
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final PlaceBlockEvent event) {
            final World world = commandBuffer.getExternalData().getWorld();
            final Vector3i blockLocation = event.getTargetBlock();
            final ChunkStore chunkComponentStore = world.getChunkStore();
            final long chunkIndex = ChunkUtil.indexChunkFromBlock(blockLocation.x, blockLocation.z);
            final Ref<ChunkStore> chunkReference = chunkComponentStore.getChunkReference(chunkIndex);
            if (chunkReference == null) {
                return;
            }
            final BlockHealthChunk blockHealthComponent = chunkComponentStore.getStore().getComponent(chunkReference, PlaceBlockEventSystem.BLOCK_HEALTH_CHUNK_COMPONENT_TYPE);
            assert blockHealthComponent != null;
            final WorldConfig worldGameplayConfig = world.getGameplayConfig().getWorldConfig();
            final float blockPlacementFragilityTimer = worldGameplayConfig.getBlockPlacementFragilityTimer();
            blockHealthComponent.makeBlockFragile(blockLocation, blockPlacementFragilityTimer);
        }
        
        @Nullable
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Archetype.empty();
        }
        
        static {
            BLOCK_HEALTH_CHUNK_COMPONENT_TYPE = BlockHealthModule.get().getBlockHealthChunkComponentType();
        }
    }
    
    private static class EnsureBlockHealthSystem extends HolderSystem<ChunkStore>
    {
        @Nonnull
        private final ComponentType<ChunkStore, BlockHealthChunk> blockHealthChunkComponentType;
        
        public EnsureBlockHealthSystem(@Nonnull final ComponentType<ChunkStore, BlockHealthChunk> blockHealthChunkComponentType) {
            this.blockHealthChunkComponentType = blockHealthChunkComponentType;
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return WorldChunk.getComponentType();
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<ChunkStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<ChunkStore> store) {
            holder.ensureComponent(this.blockHealthChunkComponentType);
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<ChunkStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<ChunkStore> store) {
        }
    }
    
    private static class BlockHealthSystem extends EntityTickingSystem<ChunkStore>
    {
        @Nonnull
        private final ComponentType<ChunkStore, BlockHealthChunk> blockHealthComponentChunkType;
        @Nonnull
        private final ResourceType<EntityStore, TimeResource> timeResourceType;
        private final Archetype<ChunkStore> archetype;
        
        public BlockHealthSystem(@Nonnull final ComponentType<ChunkStore, BlockHealthChunk> blockHealthComponentChunkType) {
            this.blockHealthComponentChunkType = blockHealthComponentChunkType;
            this.timeResourceType = TimeResource.getResourceType();
            this.archetype = Archetype.of(blockHealthComponentChunkType, WorldChunk.getComponentType());
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return this.archetype;
        }
        
        @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 BlockHealthChunk blockHealthChunkComponent = archetypeChunk.getComponent(index, this.blockHealthComponentChunkType);
            assert blockHealthChunkComponent != null;
            final World world = store.getExternalData().getWorld();
            final Store<EntityStore> entityStore = world.getEntityStore().getStore();
            final TimeResource uptime = world.getEntityStore().getStore().getResource(this.timeResourceType);
            final Instant currentGameTime = uptime.getNow();
            final Instant lastRepairGameTime = blockHealthChunkComponent.getLastRepairGameTime();
            blockHealthChunkComponent.setLastRepairGameTime(currentGameTime);
            if (lastRepairGameTime == null) {
                return;
            }
            final Map<Vector3i, FragileBlock> blockFragilityMap = blockHealthChunkComponent.getBlockFragilityMap();
            if (!blockFragilityMap.isEmpty()) {
                final float deltaSeconds = (currentGameTime.toEpochMilli() - lastRepairGameTime.toEpochMilli()) / 1000.0f;
                final Iterator<Map.Entry<Vector3i, FragileBlock>> iterator = blockFragilityMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    final Map.Entry<Vector3i, FragileBlock> entry = iterator.next();
                    final FragileBlock fragileBlock = entry.getValue();
                    final float newDuration = fragileBlock.getDurationSeconds() - deltaSeconds;
                    if (newDuration <= 0.0f) {
                        iterator.remove();
                    }
                    else {
                        fragileBlock.setDurationSeconds(newDuration);
                    }
                }
            }
            final Map<Vector3i, BlockHealth> blockHealthMap = blockHealthChunkComponent.getBlockHealthMap();
            if (blockHealthMap.isEmpty()) {
                return;
            }
            final WorldChunk chunk = archetypeChunk.getComponent(index, WorldChunk.getComponentType());
            assert chunk != null;
            final Collection<PlayerRef> allPlayers = world.getPlayerRefs();
            final ObjectArrayList<PlayerRef> visibleTo = new ObjectArrayList<PlayerRef>(allPlayers.size());
            for (final PlayerRef playerRef : allPlayers) {
                final Ref<EntityStore> playerReference = playerRef.getReference();
                if (playerReference != null) {
                    if (!playerReference.isValid()) {
                        continue;
                    }
                    final ChunkTracker chunkTrackerComponent = entityStore.getComponent(playerReference, ChunkTracker.getComponentType());
                    assert chunkTrackerComponent != null;
                    if (!chunkTrackerComponent.isLoaded(chunk.getIndex())) {
                        continue;
                    }
                    visibleTo.add(playerRef);
                }
            }
            final float deltaSeconds2 = (currentGameTime.toEpochMilli() - lastRepairGameTime.toEpochMilli()) / 1000.0f;
            final Iterator<Map.Entry<Vector3i, BlockHealth>> iterator2 = blockHealthMap.entrySet().iterator();
            while (iterator2.hasNext()) {
                final Map.Entry<Vector3i, BlockHealth> entry2 = iterator2.next();
                final Vector3i position = entry2.getKey();
                final BlockHealth blockHealth = entry2.getValue();
                final Instant startRegenerating = blockHealth.getLastDamageGameTime().plusSeconds(5L);
                if (currentGameTime.isBefore(startRegenerating)) {
                    continue;
                }
                float healthDelta = 0.1f * deltaSeconds2;
                float health = blockHealth.getHealth() + healthDelta;
                if (health < 1.0f) {
                    blockHealth.setHealth(health);
                }
                else {
                    iterator2.remove();
                    health = BlockHealth.NO_DAMAGE_INSTANCE.getHealth();
                    healthDelta = health - blockHealth.getHealth();
                }
                final UpdateBlockDamage packet = new UpdateBlockDamage(new BlockPosition(position.getX(), position.getY(), position.getZ()), health, healthDelta);
                for (int i = 0; i < visibleTo.size(); ++i) {
                    visibleTo.get(i).getPacketHandler().writeNoCache(packet);
                }
            }
        }
    }
    
    private static class BlockHealthPacketSystem extends ChunkStore.LoadPacketDataQuerySystem
    {
        @Nonnull
        private final ComponentType<ChunkStore, BlockHealthChunk> blockHealthCunkComponentType;
        @Nonnull
        private final Archetype<ChunkStore> archetype;
        
        public BlockHealthPacketSystem(@Nonnull final ComponentType<ChunkStore, BlockHealthChunk> blockHealthChunkComponentType) {
            this.blockHealthCunkComponentType = blockHealthChunkComponentType;
            this.archetype = Archetype.of(blockHealthChunkComponentType, WorldChunk.getComponentType());
        }
        
        @Nonnull
        @Override
        public Query<ChunkStore> getQuery() {
            return this.archetype;
        }
        
        @Override
        public boolean isParallel() {
            return true;
        }
        
        @Override
        public void fetch(final int index, @Nonnull final ArchetypeChunk<ChunkStore> archetypeChunk, final Store<ChunkStore> store, final CommandBuffer<ChunkStore> commandBuffer, final PlayerRef player, @Nonnull final List<Packet> results) {
            final BlockHealthChunk blockHealthChunkComponent = archetypeChunk.getComponent(index, this.blockHealthCunkComponentType);
            assert blockHealthChunkComponent != null;
            blockHealthChunkComponent.createBlockDamagePackets(results);
        }
    }
}
