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

package com.hypixel.hytale.event;

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import com.hypixel.hytale.logger.HytaleLogger;

public class EventBus implements IEventBus
{
    @Nonnull
    private static final HytaleLogger LOGGER;
    @Nonnull
    private final Map<Class<? extends IBaseEvent<?>>, EventBusRegistry<?, ?, ?>> registryMap;
    private final boolean timeEvents;
    
    public EventBus(final boolean timeEvents) {
        this.registryMap = new ConcurrentHashMap<Class<? extends IBaseEvent<?>>, EventBusRegistry<?, ?, ?>>();
        this.timeEvents = timeEvents;
    }
    
    public void shutdown() {
        this.registryMap.values().forEach(EventBusRegistry::shutdown);
    }
    
    @Nonnull
    public Set<Class<? extends IBaseEvent<?>>> getRegisteredEventClasses() {
        return new HashSet<Class<? extends IBaseEvent<?>>>(this.registryMap.keySet());
    }
    
    @Nonnull
    public Set<String> getRegisteredEventClassNames() {
        final Set<String> classNames = new HashSet<String>();
        for (final Class<?> aClass : this.registryMap.keySet()) {
            classNames.add(aClass.getSimpleName());
        }
        return classNames;
    }
    
    @Nullable
    public EventBusRegistry<?, ?, ?> getRegistry(@Nonnull final String eventName) {
        Class<? extends IBaseEvent> eventClass = null;
        for (final Class<? extends IBaseEvent<?>> aClass : this.registryMap.keySet()) {
            if (aClass.getSimpleName().equalsIgnoreCase(eventName) || aClass.getName().equalsIgnoreCase(eventName)) {
                eventClass = aClass;
            }
        }
        return (eventClass == null) ? null : this.getRegistry((Class<?>)eventClass);
    }
    
    @Nonnull
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventBusRegistry<KeyType, EventType, ?> getRegistry(@Nonnull final Class<? super EventType> eventClass) {
        if (IAsyncEvent.class.isAssignableFrom(eventClass)) {
            return (EventBusRegistry<KeyType, EventType, ?>)this.getAsyncRegistry((Class<? super IAsyncEvent>)eventClass);
        }
        return (EventBusRegistry<KeyType, EventType, ?>)this.getSyncRegistry((Class<? super IEvent>)eventClass);
    }
    
    @Nonnull
    public <KeyType, EventType extends IEvent<KeyType>> EventBusRegistry<KeyType, EventType, ?> getSyncRegistry(@Nonnull final Class<? super EventType> eventClass) {
        final EventBusRegistry<?, ? extends IBaseEvent<?>, ? extends EventBusRegistry.EventConsumerMap<? extends IBaseEvent<?>, ?, ?>> registry = this.registryMap.computeIfAbsent((Class<? extends IBaseEvent<?>>)eventClass, aClass -> new SyncEventBusRegistry(EventBus.LOGGER, aClass));
        if (this.timeEvents) {
            registry.setTimeEvents(true);
        }
        return (EventBusRegistry<KeyType, EventType, ?>)registry;
    }
    
    @Nonnull
    private <KeyType, EventType extends IAsyncEvent<KeyType>> AsyncEventBusRegistry<KeyType, EventType> getAsyncRegistry(@Nonnull final Class<? super EventType> eventClass) {
        final EventBusRegistry<?, ? extends IBaseEvent<?>, ? extends EventBusRegistry.EventConsumerMap<? extends IBaseEvent<?>, ?, ?>> registry = this.registryMap.computeIfAbsent((Class<? extends IBaseEvent<?>>)eventClass, aClass -> new AsyncEventBusRegistry(EventBus.LOGGER, aClass));
        if (this.timeEvents) {
            registry.setTimeEvents(true);
        }
        return (AsyncEventBusRegistry)registry;
    }
    
    @Override
    public <EventType extends IBaseEvent<Void>> EventRegistration<Void, EventType> register(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.register((short)0, eventClass, (Void)null, consumer);
    }
    
    @Override
    public <EventType extends IBaseEvent<Void>> EventRegistration<Void, EventType> register(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.register(priority.getValue(), eventClass, (Void)null, consumer);
    }
    
    @Override
    public <EventType extends IBaseEvent<Void>> EventRegistration<Void, EventType> register(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.register(priority, eventClass, (Void)null, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> register(@Nonnull final Class<? super EventType> eventClass, @Nonnull final KeyType key, @Nonnull final Consumer<EventType> consumer) {
        return this.register((short)0, eventClass, key, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> register(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final KeyType key, @Nonnull final Consumer<EventType> consumer) {
        return this.register(priority.getValue(), eventClass, key, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> register(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nullable final KeyType key, @Nonnull final Consumer<EventType> consumer) {
        return this.getRegistry(eventClass).register(priority, key, consumer);
    }
    
    @Override
    public <EventType extends IAsyncEvent<Void>> EventRegistration<Void, EventType> registerAsync(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsync((short)0, eventClass, (Void)null, function);
    }
    
    @Override
    public <EventType extends IAsyncEvent<Void>> EventRegistration<Void, EventType> registerAsync(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsync(priority.getValue(), eventClass, (Void)null, function);
    }
    
    @Override
    public <EventType extends IAsyncEvent<Void>> EventRegistration<Void, EventType> registerAsync(final short priority, final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsync(priority, eventClass, (Void)null, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsync(@Nonnull final Class<? super EventType> eventClass, @Nonnull final KeyType key, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsync((short)0, eventClass, key, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsync(@Nonnull final EventPriority priority, final Class<? super EventType> eventClass, @Nonnull final KeyType key, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsync(priority.getValue(), eventClass, key, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsync(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nullable final KeyType key, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.getAsyncRegistry(eventClass).registerAsync(priority, key, function);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerGlobal(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.registerGlobal((short)0, eventClass, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerGlobal(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.registerGlobal(priority.getValue(), eventClass, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerGlobal(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.getRegistry(eventClass).registerGlobal(priority, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncGlobal(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsyncGlobal((short)0, eventClass, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncGlobal(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsyncGlobal(priority.getValue(), eventClass, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncGlobal(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.getAsyncRegistry(eventClass).registerAsyncGlobal(priority, function);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerUnhandled(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.registerUnhandled((short)0, eventClass, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerUnhandled(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.registerUnhandled(priority.getValue(), eventClass, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IBaseEvent<KeyType>> EventRegistration<KeyType, EventType> registerUnhandled(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Consumer<EventType> consumer) {
        return this.getRegistry(eventClass).registerUnhandled(priority, consumer);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncUnhandled(@Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsyncUnhandled((short)0, eventClass, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncUnhandled(@Nonnull final EventPriority priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.registerAsyncUnhandled(priority.getValue(), eventClass, function);
    }
    
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> EventRegistration<KeyType, EventType> registerAsyncUnhandled(final short priority, @Nonnull final Class<? super EventType> eventClass, @Nonnull final Function<CompletableFuture<EventType>, CompletableFuture<EventType>> function) {
        return this.getAsyncRegistry(eventClass).registerAsyncUnhandled(priority, function);
    }
    
    @Nonnull
    @Override
    public <KeyType, EventType extends IEvent<KeyType>> IEventDispatcher<EventType, EventType> dispatchFor(@Nonnull final Class<? super EventType> eventClass, final KeyType key) {
        final SyncEventBusRegistry<KeyType, EventType> registry = this.registryMap.get(eventClass);
        if (registry == null) {
            return SyncEventBusRegistry.NO_OP;
        }
        return registry.dispatchFor(key);
    }
    
    @Nonnull
    @Override
    public <KeyType, EventType extends IAsyncEvent<KeyType>> IEventDispatcher<EventType, CompletableFuture<EventType>> dispatchForAsync(@Nonnull final Class<? super EventType> eventClass, final KeyType key) {
        final AsyncEventBusRegistry<KeyType, EventType> registry = this.registryMap.get(eventClass);
        if (registry == null) {
            return AsyncEventBusRegistry.NO_OP;
        }
        return registry.dispatchFor(key);
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
}
