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

package com.hypixel.hytale.server.flock;

import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.system.EcsEvent;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.component.ArchetypeChunk;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.component.system.RefChangeSystem;
import java.util.EnumSet;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.role.RoleDebugFlags;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Holder;
import java.util.UUID;
import java.util.logging.Level;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.core.entity.group.EntityGroup;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;

public class FlockMembershipSystems
{
    public static boolean canJoinFlock(@Nonnull final Ref<EntityStore> reference, @Nonnull final Ref<EntityStore> flockReference, @Nonnull final Store<EntityStore> store) {
        final Flock flockComponent = store.getComponent(flockReference, Flock.getComponentType());
        assert flockComponent != null;
        final PersistentFlockData flockData = flockComponent.getFlockData();
        if (flockData == null) {
            return false;
        }
        final EntityGroup entityGroupComponent = store.getComponent(flockReference, EntityGroup.getComponentType());
        assert entityGroupComponent != null;
        if (entityGroupComponent.size() >= flockData.getMaxGrowSize()) {
            return false;
        }
        final NPCEntity npcComponent = store.getComponent(reference, NPCEntity.getComponentType());
        if (npcComponent == null) {
            return false;
        }
        final String roleName = npcComponent.getRoleName();
        return roleName != null && flockData.isFlockAllowedRole(roleName);
    }
    
    public static void join(@Nonnull final Ref<EntityStore> ref, @Nonnull final Ref<EntityStore> flockRef, @Nonnull final Store<EntityStore> store) {
        final FlockMembership membership = new FlockMembership();
        final UUIDComponent uuidComponent = store.getComponent(flockRef, UUIDComponent.getComponentType());
        assert uuidComponent != null;
        membership.setFlockId(uuidComponent.getUuid());
        membership.setFlockRef(flockRef);
        membership.setMembershipType(FlockMembership.Type.JOINING);
        store.putComponent(ref, FlockMembership.getComponentType(), membership);
    }
    
    private static boolean canBecomeLeader(@Nonnull final Ref<EntityStore> ref) {
        final Store<EntityStore> store = ref.getStore();
        if (store.getComponent(ref, Player.getComponentType()) != null) {
            return true;
        }
        final NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType());
        return npcComponent != null && npcComponent.getRole() != null && npcComponent.getRole().isCanLeadFlock();
    }
    
    private static void markChunkNeedsSaving(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store) {
        final TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
        if (transformComponent == null) {
            return;
        }
        transformComponent.markChunkDirty(store);
    }
    
    public static class EntityRef extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
        
        public EntityRef(@Nonnull final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType) {
            this.flockMembershipComponentType = flockMembershipComponentType;
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.flockMembershipComponentType;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            commandBuffer.run(_store -> this.joinOrCreateFlock(ref, _store));
        }
        
        private void joinOrCreateFlock(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store) {
            final FlockMembership flockMembershipComponent = store.getComponent(ref, this.flockMembershipComponentType);
            assert flockMembershipComponent != null;
            final UUID flockId = flockMembershipComponent.getFlockId();
            Ref<EntityStore> flockReference = store.getExternalData().getRefFromUUID(flockId);
            EntityGroup entityGroup;
            Flock flock;
            if (flockReference != null) {
                entityGroup = store.getComponent(flockReference, EntityGroup.getComponentType());
                assert entityGroup != null;
                flock = store.getComponent(flockReference, Flock.getComponentType());
                assert flock != null;
            }
            else {
                entityGroup = new EntityGroup();
                flock = new Flock();
                final Holder<EntityStore> holder = EntityStore.REGISTRY.newHolder();
                holder.addComponent(UUIDComponent.getComponentType(), new UUIDComponent(flockId));
                holder.addComponent(EntityGroup.getComponentType(), entityGroup);
                holder.addComponent(Flock.getComponentType(), flock);
                flockReference = store.addEntity(holder, AddReason.LOAD);
            }
            flockMembershipComponent.setFlockRef(flockReference);
            if (entityGroup.isMember(ref)) {
                throw new IllegalStateException(String.format("Entity %s attempting to reload into group with ID %s despite already being a member", ref, flockId));
            }
            entityGroup.add(ref);
            if (flockMembershipComponent.getMembershipType() == FlockMembership.Type.LEADER) {
                final PersistentFlockData persistentFlockData = store.getComponent(ref, PersistentFlockData.getComponentType());
                if (persistentFlockData != null) {
                    flock.setFlockData(persistentFlockData);
                }
                else {
                    final PersistentFlockData flockData = flock.getFlockData();
                    if (flockData != null) {
                        store.putComponent(ref, PersistentFlockData.getComponentType(), flockData);
                    }
                }
                final Ref<EntityStore> oldLeaderRef = entityGroup.getLeaderRef();
                entityGroup.setLeaderRef(ref);
                if (oldLeaderRef != null && !oldLeaderRef.equals(ref)) {
                    final FlockMembership oldLeaderComponent = store.getComponent(oldLeaderRef, this.flockMembershipComponentType);
                    if (oldLeaderComponent != null) {
                        oldLeaderComponent.setMembershipType(FlockMembership.Type.MEMBER);
                    }
                    store.tryRemoveComponent(oldLeaderRef, PersistentFlockData.getComponentType());
                    FlockMembershipSystems.markChunkNeedsSaving(oldLeaderRef, store);
                }
                markNeedsSave(ref, store, flock);
                if (flock.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, oldLeaderRef, ref, entityGroup.size());
                }
            }
            else if (entityGroup.getLeaderRef() == null) {
                setInterimLeader(store, flockMembershipComponent, entityGroup, ref, flock, flockId);
            }
            if (flock.isTrace()) {
                FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: reference=%s, size=%s", flockId, ref, entityGroup.size());
            }
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final FlockMembership membership = store.getComponent(ref, this.flockMembershipComponentType);
            assert membership != null;
            final Ref<EntityStore> flockRef = membership.getFlockRef();
            if (flockRef == null || !flockRef.isValid()) {
                return;
            }
            final Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
            assert flockComponent != null;
            final EntityGroup entityGroupComponent = commandBuffer.getComponent(flockRef, EntityGroup.getComponentType());
            assert entityGroupComponent != null;
            final UUIDComponent uuidComponent = commandBuffer.getComponent(flockRef, UUIDComponent.getComponentType());
            assert uuidComponent != null;
            final UUID flockId = uuidComponent.getUuid();
            if (reason == RemoveReason.REMOVE || store.getArchetype(ref).contains(Player.getComponentType())) {
                entityGroupComponent.remove(ref);
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Left flock, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
                }
                if (!entityGroupComponent.isDissolved() && entityGroupComponent.size() < 2) {
                    commandBuffer.removeEntity(flockRef, RemoveReason.REMOVE);
                    return;
                }
                if (entityGroupComponent.isDissolved()) {
                    return;
                }
                final PersistentFlockData flockData = flockComponent.getFlockData();
                if (flockData != null) {
                    flockData.decreaseSize();
                }
                final Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
                if (leader != null && !leader.equals(ref)) {
                    FlockMembershipSystems.markChunkNeedsSaving(leader, store);
                    return;
                }
                final Ref<EntityStore> newLeader = entityGroupComponent.testMembers(FlockMembershipSystems::canBecomeLeader, true);
                if (newLeader == null) {
                    if (flockComponent.isTrace()) {
                        FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Leave failed to get new leader, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
                    }
                    commandBuffer.removeEntity(flockRef, RemoveReason.REMOVE);
                    return;
                }
                entityGroupComponent.setLeaderRef(newLeader);
                final FlockMembership flockMembershipComponent = store.getComponent(newLeader, this.flockMembershipComponentType);
                assert flockMembershipComponent != null;
                flockMembershipComponent.setMembershipType(FlockMembership.Type.LEADER);
                if (flockData != null) {
                    commandBuffer.putComponent(newLeader, PersistentFlockData.getComponentType(), flockData);
                }
                markNeedsSave(newLeader, store, flockComponent);
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, ref, newLeader, entityGroupComponent.size());
                }
            }
            else if (reason == RemoveReason.UNLOAD) {
                entityGroupComponent.remove(ref);
                if (!entityGroupComponent.isDissolved() && membership.getMembershipType().isActingAsLeader()) {
                    final Ref<EntityStore> interimLeader = entityGroupComponent.testMembers(member -> true, true);
                    if (interimLeader != null) {
                        final FlockMembership interimLeaderMembership = store.getComponent(interimLeader, this.flockMembershipComponentType);
                        if (interimLeaderMembership == null) {
                            throw new IllegalStateException("Member is missing FlockMembership component!");
                        }
                        setInterimLeader(store, interimLeaderMembership, entityGroupComponent, interimLeader, flockComponent, flockId);
                    }
                }
                membership.unload();
                if (entityGroupComponent.size() <= 0) {
                    commandBuffer.tryRemoveEntity(flockRef, RemoveReason.UNLOAD);
                }
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Unloaded from flock, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
                }
            }
        }
        
        private static void markNeedsSave(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, @Nonnull final Flock flockComponent) {
            final NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType());
            if (npcComponent != null) {
                final Role role = npcComponent.getRole();
                if (role != null) {
                    final EnumSet<RoleDebugFlags> flags = role.getDebugSupport().getDebugFlags();
                    flockComponent.setTrace(flags.contains(RoleDebugFlags.Flock));
                }
            }
            FlockMembershipSystems.markChunkNeedsSaving(ref, store);
        }
        
        private static void setInterimLeader(@Nonnull final Store<EntityStore> store, @Nonnull final FlockMembership interimLeaderMembership, @Nonnull final EntityGroup entityGroup, final Ref<EntityStore> interimLeader, @Nonnull final Flock flockComponent, @Nonnull final UUID flockId) {
            interimLeaderMembership.setMembershipType(FlockMembership.Type.INTERIM_LEADER);
            entityGroup.setLeaderRef(interimLeader);
            markNeedsSave(interimLeader, store, flockComponent);
            if (flockComponent.isTrace()) {
                FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Set new interim leader, old=%s, new=%s, size=%s", flockId, entityGroup.getLeaderRef(), interimLeader, entityGroup.size());
            }
        }
    }
    
    public static class RefChange extends RefChangeSystem<EntityStore, FlockMembership>
    {
        @Nonnull
        private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
        
        public RefChange(@Nonnull final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType) {
            this.flockMembershipComponentType = flockMembershipComponentType;
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.flockMembershipComponentType;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, FlockMembership> componentType() {
            return this.flockMembershipComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            this.doJoin(ref, component, store, commandBuffer);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final FlockMembership oldComponent, @Nonnull final FlockMembership newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            assert oldComponent != null;
            if (oldComponent.getMembershipType() == FlockMembership.Type.JOINING) {
                this.doJoin(ref, newComponent, store, commandBuffer);
                return;
            }
            doLeave(ref, oldComponent, store, commandBuffer);
            commandBuffer.run(_store -> this.doJoin(ref, newComponent, store, commandBuffer));
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            if (component.getMembershipType() == FlockMembership.Type.JOINING) {
                return;
            }
            doLeave(ref, component, store, commandBuffer);
        }
        
        private void doJoin(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership membershipComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> flockRef = membershipComponent.getFlockRef();
            if (flockRef == null) {
                return;
            }
            if (!flockRef.isValid()) {
                FlockPlugin.get().getLogger().atWarning().log("Entity %s attempting to join invalid flock with ref %s", ref, flockRef);
                commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
                return;
            }
            final Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
            assert flockComponent != null;
            final EntityGroup entityGroupComponent = commandBuffer.getComponent(flockRef, EntityGroup.getComponentType());
            assert entityGroupComponent != null;
            final UUIDComponent uuidComponent = commandBuffer.getComponent(flockRef, UUIDComponent.getComponentType());
            assert uuidComponent != null;
            final UUID flockId = uuidComponent.getUuid();
            if (entityGroupComponent.isMember(ref)) {
                return;
            }
            if (membershipComponent.getMembershipType() != FlockMembership.Type.JOINING) {
                throw new IllegalStateException(String.format("Entity %s attempting to join group with ID %s but has wrong membership status %s", ref, flockId, membershipComponent.getMembershipType()));
            }
            final boolean isDead = store.getArchetype(ref).contains(DeathComponent.getComponentType());
            if (!ref.isValid() || isDead) {
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Failed to join entity ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
                }
                commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
                return;
            }
            final Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType());
            if (playerComponent != null && playerComponent.getGameMode() != GameMode.Adventure) {
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Failed to join, ref=%s. Player in creative mode.", flockId, ref);
                }
                commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
                return;
            }
            final PersistentFlockData flockData = flockComponent.getFlockData();
            if (flockData == null) {
                if (flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Rejected join entity due to leader not being loaded, ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
                }
                commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
                return;
            }
            final Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
            final boolean wasFirstJoiner;
            boolean mustBecomeLeader = wasFirstJoiner = (leader == null);
            if (playerComponent != null) {
                if (leader != null && store.getComponent(leader, Player.getComponentType()) != null) {
                    if (flockComponent.isTrace()) {
                        FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Failed join 2 players, ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
                    }
                    commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
                    return;
                }
                mustBecomeLeader = true;
            }
            entityGroupComponent.add(ref);
            if (mustBecomeLeader) {
                setNewLeader(flockId, entityGroupComponent, flockComponent, ref, store, commandBuffer);
                if (wasFirstJoiner && flockComponent.isTrace()) {
                    FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Joined no leader, ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
                }
            }
            else {
                membershipComponent.setMembershipType(FlockMembership.Type.MEMBER);
            }
            flockData.increaseSize();
            FlockMembershipSystems.markChunkNeedsSaving(entityGroupComponent.getLeaderRef(), store);
            if (flockComponent.isTrace()) {
                FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Joined join ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
            }
            FlockMembershipSystems.markChunkNeedsSaving(ref, store);
        }
        
        private static void doLeave(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership membershipComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> flockReference = membershipComponent.getFlockRef();
            if (flockReference == null || !flockReference.isValid()) {
                return;
            }
            final Flock flockComponent = commandBuffer.getComponent(flockReference, Flock.getComponentType());
            assert flockComponent != null;
            final EntityGroup entityGroupComponent = commandBuffer.getComponent(flockReference, EntityGroup.getComponentType());
            assert entityGroupComponent != null;
            final UUIDComponent uuidComponent = commandBuffer.getComponent(flockReference, UUIDComponent.getComponentType());
            assert uuidComponent != null;
            final UUID flockId = uuidComponent.getUuid();
            entityGroupComponent.remove(ref);
            if (flockComponent.isTrace()) {
                FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Left flock, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
            }
            if (!entityGroupComponent.isDissolved() && entityGroupComponent.size() < 2) {
                commandBuffer.removeEntity(flockReference, RemoveReason.REMOVE);
                return;
            }
            if (entityGroupComponent.isDissolved()) {
                return;
            }
            final PersistentFlockData flockData = flockComponent.getFlockData();
            if (flockData != null) {
                flockData.decreaseSize();
            }
            final Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
            if (leader == null || ref.equals(leader)) {
                final Ref<EntityStore> newLeader = entityGroupComponent.testMembers(FlockMembershipSystems::canBecomeLeader, true);
                if (newLeader == null) {
                    if (flockComponent.isTrace()) {
                        FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Leave failed to get new leader, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
                    }
                    commandBuffer.removeEntity(flockReference, RemoveReason.REMOVE);
                    return;
                }
                setNewLeader(flockId, entityGroupComponent, flockComponent, newLeader, store, commandBuffer);
            }
            else {
                FlockMembershipSystems.markChunkNeedsSaving(leader, store);
            }
        }
        
        private static void setNewLeader(@Nonnull final UUID flockId, @Nonnull final EntityGroup entityGroup, @Nonnull final Flock flock, @Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> oldLeader = entityGroup.getLeaderRef();
            if (oldLeader != null && oldLeader.equals(ref)) {
                return;
            }
            entityGroup.setLeaderRef(ref);
            if (oldLeader != null) {
                final FlockMembership oldLeaderComponent = store.getComponent(oldLeader, FlockMembership.getComponentType());
                if (oldLeaderComponent != null) {
                    oldLeaderComponent.setMembershipType(FlockMembership.Type.MEMBER);
                }
                commandBuffer.tryRemoveComponent(oldLeader, PersistentFlockData.getComponentType());
                FlockMembershipSystems.markChunkNeedsSaving(oldLeader, store);
            }
            final FlockMembership flockMembershipComponent = store.getComponent(ref, FlockMembership.getComponentType());
            assert flockMembershipComponent != null;
            flockMembershipComponent.setMembershipType(FlockMembership.Type.LEADER);
            final NPCEntity newLeaderNpcComponent = store.getComponent(ref, NPCEntity.getComponentType());
            if (newLeaderNpcComponent != null) {
                final Role role = newLeaderNpcComponent.getRole();
                if (role != null) {
                    final EnumSet<RoleDebugFlags> flags = role.getDebugSupport().getDebugFlags();
                    flock.setTrace(flags.contains(RoleDebugFlags.Flock));
                }
            }
            final PersistentFlockData flockData = flock.getFlockData();
            if (flockData != null) {
                commandBuffer.putComponent(ref, PersistentFlockData.getComponentType(), flockData);
            }
            FlockMembershipSystems.markChunkNeedsSaving(ref, store);
            if (flock.isTrace()) {
                FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, oldLeader, ref, entityGroup.size());
            }
        }
    }
    
    public static class OnDamageReceived extends DamageEventSystem
    {
        @Nonnull
        private final Query<EntityStore> query;
        
        public OnDamageReceived() {
            this.query = FlockMembership.getComponentType();
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return DamageModule.get().getInspectDamageGroup();
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Damage danage) {
            final FlockMembership flockMembershipComponent = archetypeChunk.getComponent(index, FlockMembership.getComponentType());
            if (flockMembershipComponent == null) {
                return;
            }
            final Ref<EntityStore> flockRef = flockMembershipComponent.getFlockRef();
            if (flockRef == null || !flockRef.isValid()) {
                return;
            }
            final Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
            assert flockComponent != null;
            flockComponent.getNextDamageData().onSufferedDamage(commandBuffer, danage);
            if (flockMembershipComponent.getMembershipType().isActingAsLeader()) {
                flockComponent.getNextLeaderDamageData().onSufferedDamage(commandBuffer, danage);
            }
        }
    }
    
    public static class OnDamageDealt extends DamageEventSystem
    {
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return DamageModule.get().getInspectDamageGroup();
        }
        
        @Nullable
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Archetype.empty();
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Damage damage) {
            final Damage.Source source = damage.getSource();
            if (!(source instanceof Damage.EntitySource)) {
                return;
            }
            final Damage.EntitySource entitySource = (Damage.EntitySource)source;
            final Ref<EntityStore> damageSourceRef = entitySource.getRef();
            if (!damageSourceRef.isValid()) {
                return;
            }
            final FlockMembership flockMembershipComponent = commandBuffer.getComponent(damageSourceRef, FlockMembership.getComponentType());
            if (flockMembershipComponent == null) {
                return;
            }
            final Ref<EntityStore> flockReference = flockMembershipComponent.getFlockRef();
            if (flockReference == null || !flockReference.isValid()) {
                return;
            }
            final Flock flockComponent = commandBuffer.getComponent(flockReference, Flock.getComponentType());
            assert flockComponent != null;
            final Ref<EntityStore> entityRef = archetypeChunk.getReferenceTo(index);
            flockComponent.getNextDamageData().onInflictedDamage(entityRef, damage.getAmount());
            if (flockMembershipComponent.getMembershipType().isActingAsLeader()) {
                flockComponent.getNextLeaderDamageData().onInflictedDamage(entityRef, damage.getAmount());
            }
        }
    }
    
    public static class NPCAddedFromWorldGen extends HolderSystem<EntityStore>
    {
        @Nullable
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, FromWorldGen> fromWorldGenComponentType;
        @Nonnull
        private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public NPCAddedFromWorldGen() {
            this.npcComponentType = NPCEntity.getComponentType();
            this.fromWorldGenComponentType = FromWorldGen.getComponentType();
            this.flockMembershipComponentType = FlockMembership.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.npcComponentType, this.fromWorldGenComponentType, this.flockMembershipComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityModule.get().getPreClearMarkersGroup();
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.removeComponent(this.flockMembershipComponentType);
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
    }
}
