package pokefenn.totemic.advancements.criterion;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;

import net.minecraft.advancements.ICriterionTrigger;
import net.minecraft.advancements.PlayerAdvancements;
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;
import pokefenn.totemic.Totemic;
import pokefenn.totemic.api.TotemicRegistries;
import pokefenn.totemic.api.ceremony.Ceremony;

public class CeremonyTrigger implements ICriterionTrigger<CeremonyTrigger.Instance>
{
    private static final ResourceLocation ID = new ResourceLocation(Totemic.MOD_ID, "perform_ceremony");

    private final Map<PlayerAdvancements, Listeners> listeners = new HashMap<>();

    @Override
    public ResourceLocation getId()
    {
        return ID;
    }

    @Override
    public void addListener(PlayerAdvancements advancements, Listener<Instance> listener)
    {
        listeners.computeIfAbsent(advancements, Listeners::new).add(listener);
    }

    @Override
    public void removeListener(PlayerAdvancements advancements, Listener<Instance> listener)
    {
        Listeners ls = listeners.get(advancements);
        if(ls != null)
        {
            ls.remove(listener);
            if(ls.isEmpty())
                listeners.remove(advancements);
        }
    }

    @Override
    public void removeAllListeners(PlayerAdvancements advancements)
    {
        listeners.remove(advancements);
    }

    @Override
    public Instance deserializeInstance(JsonObject json, JsonDeserializationContext context)
    {
        String name = JsonUtils.getString(json, "ceremony");
        Ceremony ceremony = TotemicRegistries.ceremonies().getValue(new ResourceLocation(name));
        if(ceremony == null)
            throw new JsonSyntaxException("Unknown ceremony: '" + name + "'");
        else
            return new Instance(ceremony);
    }

    public void trigger(EntityPlayerMP player, Ceremony ceremony)
    {
        Listeners ls = listeners.get(player.getAdvancements());
        if(ls != null)
            ls.trigger(ceremony);
    }

    static class Instance extends AbstractCriterionInstance
    {
        private final Ceremony ceremony;

        Instance(Ceremony ceremony)
        {
            super(ID);
            this.ceremony = ceremony;
        }

        boolean test(Ceremony ceremony)
        {
            return this.ceremony == ceremony;
        }
    }

    static class Listeners
    {
        private final PlayerAdvancements playerAdvancements;
        private final Set<Listener<Instance>> listeners = new HashSet<>();

        public Listeners(PlayerAdvancements playerAdvancements)
        {
            this.playerAdvancements = playerAdvancements;
        }

        public boolean isEmpty()
        {
            return listeners.isEmpty();
        }

        public void add(Listener<Instance> listener)
        {
            listeners.add(listener);
        }

        public void remove(Listener<Instance> listener)
        {
            listeners.remove(listener);
        }

        public void trigger(Ceremony ceremony)
        {
            listeners.stream()
                .filter(listener -> listener.getCriterionInstance().test(ceremony))
                .collect(ImmutableList.toImmutableList()) //Need this intermediate list to avoid ConcurrentModificationException
                .forEach(listener -> listener.grantCriterion(playerAdvancements));
        }
    }
}

@@ -64,135 +64,11 @@
 til.
-HashMap;%0Aimport java.util.HashSet;%0Aimport java.util.Map;%0Aimport java.util.Set;%0A%0Aimport com.google.common.collect.ImmutableList;
+*;%0A
 %0Aimp
@@ -2284,32 +2284,39 @@
 ny);%0A    %7D%0A%0A    
+public 
 static class Ins
@@ -2355,16 +2355,16 @@
 nstance%0A
-
     %7B%0A  
@@ -2411,16 +2411,23 @@
         
+public 
 Instance
@@ -2528,24 +2528,24 @@
      %7D%0A%0A
-
         
 boolean 
@@ -2536,16 +2536,23 @@
         
+public 
 boolean 
@@ -3356,276 +3356,479 @@
     
-l
+List%3CL
 istener
-s.stream()%0A                .filter(listener -%3E listener.getCriterionInstance().test(ceremony))%0A                .collect(ImmutableList.toImmutableList()) //Need this intermediate list to avoid ConcurrentModificationException%0A                .forEach(listener -%3E
+%3CInstance%3E%3E list = null;%0A%0A            for(Listener%3CInstance%3E listener: listeners)%0A            %7B%0A                if(listener.getCriterionInstance().test(ceremony))%0A                %7B%0A                    if(list == null)%0A                        list = new ArrayList%3C%3E();%0A                    list.add(listener);%0A                %7D%0A            %7D%0A%0A            if(list != null)%0A            %7B%0A                for(Listener%3CInstance%3E listener: list)%0A                   
  lis
@@ -3867,18 +3867,31 @@
 cements)
-)
 ;
+%0A            %7D
 %0A       

Make CeremonyTrigger consistent with vanilla