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

package com.hypixel.hytale.event;

import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import com.hypixel.hytale.logger.HytaleLogger;

public class SyncEventBusRegistry<KeyType, EventType extends IEvent<KeyType>> extends EventBusRegistry<KeyType, EventType, SyncEventConsumerMap<EventType>>
{
    public static final IEventDispatcher NO_OP;
    private final IEventDispatcher<EventType, EventType> globalDispatcher;
    
    public SyncEventBusRegistry(final HytaleLogger logger, final Class<EventType> eventClass) {
        super(logger, eventClass, new SyncEventConsumerMap(null), new SyncEventConsumerMap(null));
        this.globalDispatcher = (IEventDispatcher<EventType, EventType>)(event -> {
            if (!this.dispatchGlobal((EventType)event)) {
                this.dispatchUnhandled((EventType)event);
            }
            return event;
        });
        final SyncEventConsumerMap syncEventConsumerMap = (SyncEventConsumerMap)this.global;
        ((SyncEventConsumerMap)this.unhandled).registry = this;
        syncEventConsumerMap.registry = this;
    }
    
    @Nonnull
    @Override
    public EventRegistration<KeyType, EventType> register(final short priority, @Nullable final KeyType key, @Nonnull final Consumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        final KeyType k = (KeyType)((key != null) ? key : SyncEventBusRegistry.NULL);
        final SyncEventConsumerMap<EventType> eventMap = (SyncEventConsumerMap<EventType>)this.map.computeIfAbsent((KeyType)k, o -> new SyncEventConsumerMap(this));
        final SyncEventConsumer<EventType> eventConsumer = new SyncEventConsumer<EventType>(priority, consumer);
        eventMap.add((SyncEventConsumer<EventType>)eventConsumer);
        return new EventRegistration<KeyType, EventType>((Class<EventType>)this.eventClass, this::isAlive, () -> this.unregister(key, eventConsumer));
    }
    
    private void unregister(@Nullable final KeyType key, @Nonnull final SyncEventConsumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        final KeyType k = (KeyType)((key != null) ? key : SyncEventBusRegistry.NULL);
        final SyncEventConsumerMap<EventType> eventMap = (SyncEventConsumerMap<EventType>)this.map.get(k);
        if (eventMap != null && !eventMap.remove((SyncEventConsumer<EventType>)consumer)) {
            throw new IllegalArgumentException(String.valueOf(consumer));
        }
    }
    
    @Nonnull
    @Override
    public EventRegistration<KeyType, EventType> registerGlobal(final short priority, @Nonnull final Consumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        final SyncEventConsumer<EventType> eventConsumer = new SyncEventConsumer<EventType>(priority, consumer);
        ((EventConsumerMap<EventType, SyncEventConsumer<EventType>, ReturnType>)this.global).add(eventConsumer);
        return new EventRegistration<KeyType, EventType>((Class<EventType>)this.eventClass, this::isAlive, () -> this.unregisterGlobal(eventConsumer));
    }
    
    private void unregisterGlobal(@Nonnull final SyncEventConsumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        if (!((EventConsumerMap<EventType, SyncEventConsumer<EventType>, ReturnType>)this.global).remove(consumer)) {
            throw new IllegalArgumentException(String.valueOf(consumer));
        }
    }
    
    @Nonnull
    @Override
    public EventRegistration<KeyType, EventType> registerUnhandled(final short priority, @Nonnull final Consumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        final SyncEventConsumer<EventType> eventConsumer = new SyncEventConsumer<EventType>(priority, consumer);
        ((EventConsumerMap<EventType, SyncEventConsumer<EventType>, ReturnType>)this.unhandled).add(eventConsumer);
        return new EventRegistration<KeyType, EventType>((Class<EventType>)this.eventClass, this::isAlive, () -> this.unregisterUnhandled(eventConsumer));
    }
    
    private void unregisterUnhandled(@Nonnull final SyncEventConsumer<EventType> consumer) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        if (!((EventConsumerMap<EventType, SyncEventConsumer<EventType>, ReturnType>)this.unhandled).remove(consumer)) {
            throw new IllegalArgumentException(String.valueOf(consumer));
        }
    }
    
    @Nonnull
    @Override
    public IEventDispatcher<EventType, EventType> dispatchFor(@Nullable final KeyType key) {
        if (this.shutdown) {
            throw new IllegalArgumentException("EventRegistry is shutdown!");
        }
        final KeyType k = (KeyType)((key != null) ? key : SyncEventBusRegistry.NULL);
        final SyncEventConsumerMap<EventType> eventMap = (SyncEventConsumerMap<EventType>)this.map.get(k);
        if (eventMap != null && !eventMap.isEmpty()) {
            return (IEventDispatcher<EventType, EventType>)eventMap;
        }
        if (this.global.isEmpty() && this.unhandled.isEmpty()) {
            return SyncEventBusRegistry.NO_OP;
        }
        return this.globalDispatcher;
    }
    
    private boolean dispatchGlobal(final EventType event) {
        return this.dispatchEventMap(event, (SyncEventConsumerMap<EventType>)this.global, "Failed to dispatch event (global)");
    }
    
    private boolean dispatchUnhandled(final EventType event) {
        return this.dispatchEventMap(event, (SyncEventConsumerMap<EventType>)this.unhandled, "Failed to dispatch event (unhandled)");
    }
    
    private boolean dispatchEventMap(final EventType event, @Nonnull final SyncEventConsumerMap<EventType> eventMap, final String s) {
        boolean handled = false;
        for (final short priority : eventMap.getPriorities()) {
            final List<SyncEventConsumer<EventType>> consumers = (List<SyncEventConsumer<EventType>>)eventMap.get(priority);
            if (consumers != null) {
                for (final SyncEventConsumer<EventType> consumer : consumers) {
                    try {
                        final Consumer<EventType> theConsumer = this.timeEvents ? consumer.getTimedConsumer() : consumer.getConsumer();
                        theConsumer.accept(event);
                        if (event instanceof final IProcessedEvent processedEvent) {
                            processedEvent.processEvent(consumer.getConsumerString());
                        }
                        handled = true;
                    }
                    catch (final Throwable t) {
                        this.logger.at(Level.SEVERE).withCause(t).log("%s %s to %s", s, event, consumer);
                    }
                }
            }
        }
        return handled;
    }
    
    static {
        NO_OP = new IEventDispatcher<IBaseEvent, IBaseEvent>() {
            @Override
            public boolean hasListener() {
                return false;
            }
            
            @Override
            public IBaseEvent dispatch(final IBaseEvent event) {
                return event;
            }
        };
    }
    
    protected static class SyncEventConsumer<EventType extends IEvent> extends EventConsumer
    {
        @Nonnull
        private final Consumer<EventType> consumer;
        @Nonnull
        private final Consumer<EventType> timedConsumer;
        
        public SyncEventConsumer(final short priority, @Nonnull final Consumer<EventType> consumer) {
            super(priority, consumer.toString());
            this.consumer = consumer;
            this.timedConsumer = (t -> {
                final long before = System.nanoTime();
                consumer.accept(t);
                final long after = System.nanoTime();
                this.timer.add(after - before);
            });
        }
        
        @Nonnull
        protected Consumer<EventType> getConsumer() {
            return this.consumer;
        }
        
        @Nonnull
        public Consumer<EventType> getTimedConsumer() {
            return this.timedConsumer;
        }
        
        @Nonnull
        @Override
        public String toString() {
            return "SyncEventConsumer{consumer=" + String.valueOf(this.consumer) + ", timedConsumer=" + String.valueOf(this.timedConsumer) + "} " + super.toString();
        }
    }
    
    protected static class SyncEventConsumerMap<EventType extends IEvent> extends EventConsumerMap<EventType, SyncEventConsumer<EventType>, EventType>
    {
        protected SyncEventBusRegistry registry;
        
        public SyncEventConsumerMap(final SyncEventBusRegistry registry) {
            this.registry = registry;
        }
        
        @Override
        public EventType dispatch(final EventType event) {
            final boolean handled = this.registry.dispatchEventMap(event, this, "Failed to dispatch event");
            if (!this.registry.dispatchGlobal(event) && !handled) {
                this.registry.dispatchUnhandled(event);
            }
            return event;
        }
    }
}
