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

package com.hypixel.hytale.server.core.plugin;

import com.hypixel.hytale.codec.codecs.EnumCodec;
import com.hypixel.hytale.metrics.MetricProvider;
import java.util.function.Function;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.plugin.registry.MapKeyMapRegistry;
import com.hypixel.hytale.codec.lookup.MapKeyMapCodec;
import com.hypixel.hytale.assetstore.JsonAsset;
import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec;
import com.hypixel.hytale.server.core.plugin.registry.CodecMapRegistry;
import com.hypixel.hytale.codec.lookup.StringCodecMapCodec;
import javax.annotation.Nullable;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import java.util.concurrent.ConcurrentHashMap;
import com.hypixel.hytale.event.IEventRegistry;
import com.hypixel.hytale.server.core.HytaleServer;
import com.hypixel.hytale.server.core.plugin.registry.IRegistry;
import com.hypixel.hytale.codec.Codec;
import java.util.Map;
import com.hypixel.hytale.server.core.plugin.registry.AssetRegistry;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentRegistryProxy;
import com.hypixel.hytale.server.core.task.TaskRegistry;
import com.hypixel.hytale.server.core.modules.entity.EntityRegistry;
import com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.command.system.CommandRegistry;
import com.hypixel.hytale.server.core.registry.ClientFeatureRegistry;
import com.hypixel.hytale.function.consumer.BooleanConsumer;
import java.util.concurrent.CopyOnWriteArrayList;
import com.hypixel.hytale.server.core.util.Config;
import java.util.List;
import java.nio.file.Path;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.common.plugin.PluginIdentifier;
import com.hypixel.hytale.logger.HytaleLogger;
import javax.annotation.Nonnull;
import com.hypixel.hytale.metrics.MetricsRegistry;
import com.hypixel.hytale.server.core.command.system.CommandOwner;

public abstract class PluginBase implements CommandOwner
{
    @Nonnull
    public static final MetricsRegistry<PluginBase> METRICS_REGISTRY;
    @Nonnull
    private final HytaleLogger logger;
    @Nonnull
    private final PluginIdentifier identifier;
    @Nonnull
    private final PluginManifest manifest;
    @Nonnull
    private final Path dataDirectory;
    @Nonnull
    private final List<Config<?>> configs;
    @Nonnull
    private PluginState state;
    private final String notEnabledString;
    @Nonnull
    private final CopyOnWriteArrayList<BooleanConsumer> shutdownTasks;
    private final ClientFeatureRegistry clientFeatureRegistry;
    private final CommandRegistry commandRegistry;
    private final EventRegistry eventRegistry;
    private final BlockStateRegistry blockStateRegistry;
    private final EntityRegistry entityRegistry;
    private final TaskRegistry taskRegistry;
    private final ComponentRegistryProxy<EntityStore> entityStoreRegistry;
    private final ComponentRegistryProxy<ChunkStore> chunkStoreRegistry;
    private final AssetRegistry assetRegistry;
    private final Map<Codec<?>, IRegistry> codecMapRegistries;
    @Nonnull
    private final String basePermission;
    
    public PluginBase(@Nonnull final PluginInit init) {
        this.configs = new CopyOnWriteArrayList<Config<?>>();
        this.state = PluginState.NONE;
        this.notEnabledString = "The plugin " + String.valueOf(this.getIdentifier()) + " is not enabled!";
        this.shutdownTasks = new CopyOnWriteArrayList<BooleanConsumer>();
        this.clientFeatureRegistry = new ClientFeatureRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString, this);
        this.commandRegistry = new CommandRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString, this);
        this.eventRegistry = new EventRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString, HytaleServer.get().getEventBus());
        this.blockStateRegistry = new BlockStateRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString);
        this.entityRegistry = new EntityRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString);
        this.taskRegistry = new TaskRegistry(this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString);
        this.entityStoreRegistry = new ComponentRegistryProxy<EntityStore>(this.shutdownTasks, EntityStore.REGISTRY);
        this.chunkStoreRegistry = new ComponentRegistryProxy<ChunkStore>(this.shutdownTasks, ChunkStore.REGISTRY);
        this.assetRegistry = new AssetRegistry(this.shutdownTasks);
        this.codecMapRegistries = new ConcurrentHashMap<Codec<?>, IRegistry>();
        final PluginManifest pluginManifest = init.getPluginManifest();
        final String pluginName = pluginManifest.getName();
        final boolean isPlugin = this.getType() == PluginType.PLUGIN;
        this.logger = HytaleLogger.get(pluginName + (isPlugin ? "|P" : "|A"));
        this.dataDirectory = init.getDataDirectory();
        this.identifier = new PluginIdentifier(pluginManifest);
        this.manifest = pluginManifest;
        if (!init.isInServerClassPath()) {
            this.logger.setPropagatesSentryToParent(false);
        }
        this.basePermission = (pluginManifest.getGroup() + "." + pluginName).toLowerCase();
    }
    
    @Nonnull
    protected final <T> Config<T> withConfig(@Nonnull final BuilderCodec<T> configCodec) {
        return this.withConfig("config", configCodec);
    }
    
    @Nonnull
    protected final <T> Config<T> withConfig(@Nonnull final String name, @Nonnull final BuilderCodec<T> configCodec) {
        if (this.state != PluginState.NONE) {
            throw new IllegalStateException("Must be called before setup");
        }
        final Config<T> config = new Config<T>(this.dataDirectory, name, configCodec);
        this.configs.add(config);
        return config;
    }
    
    @Nullable
    public CompletableFuture<Void> preLoad() {
        if (this.configs.isEmpty()) {
            return null;
        }
        final CompletableFuture<?>[] futures = new CompletableFuture[this.configs.size()];
        for (int i = 0; i < this.configs.size(); ++i) {
            futures[i] = this.configs.get(i).load();
        }
        return CompletableFuture.allOf(futures);
    }
    
    @Nonnull
    @Override
    public String getName() {
        return this.identifier.toString();
    }
    
    @Nonnull
    public HytaleLogger getLogger() {
        return this.logger;
    }
    
    @Nonnull
    public PluginIdentifier getIdentifier() {
        return this.identifier;
    }
    
    @Nonnull
    public PluginManifest getManifest() {
        return this.manifest;
    }
    
    @Nonnull
    public Path getDataDirectory() {
        return this.dataDirectory;
    }
    
    @Nonnull
    public PluginState getState() {
        return this.state;
    }
    
    @Nonnull
    public ClientFeatureRegistry getClientFeatureRegistry() {
        return this.clientFeatureRegistry;
    }
    
    @Nonnull
    public CommandRegistry getCommandRegistry() {
        return this.commandRegistry;
    }
    
    @Nonnull
    public EventRegistry getEventRegistry() {
        return this.eventRegistry;
    }
    
    @Nonnull
    public BlockStateRegistry getBlockStateRegistry() {
        return this.blockStateRegistry;
    }
    
    @Nonnull
    public EntityRegistry getEntityRegistry() {
        return this.entityRegistry;
    }
    
    @Nonnull
    public TaskRegistry getTaskRegistry() {
        return this.taskRegistry;
    }
    
    @Nonnull
    public ComponentRegistryProxy<EntityStore> getEntityStoreRegistry() {
        return this.entityStoreRegistry;
    }
    
    @Nonnull
    public ComponentRegistryProxy<ChunkStore> getChunkStoreRegistry() {
        return this.chunkStoreRegistry;
    }
    
    @Nonnull
    public AssetRegistry getAssetRegistry() {
        return this.assetRegistry;
    }
    
    @Nonnull
    public <T, C extends Codec<? extends T>> CodecMapRegistry<T, C> getCodecRegistry(@Nonnull final StringCodecMapCodec<T, C> mapCodec) {
        final IRegistry registry = this.codecMapRegistries.computeIfAbsent(mapCodec, v -> new CodecMapRegistry(this.shutdownTasks, mapCodec));
        return (CodecMapRegistry)registry;
    }
    
    @Nonnull
    public <K, T extends JsonAsset<K>> CodecMapRegistry.Assets<T, ?> getCodecRegistry(@Nonnull final AssetCodecMapCodec<K, T> mapCodec) {
        final IRegistry registry = this.codecMapRegistries.computeIfAbsent(mapCodec, v -> new CodecMapRegistry.Assets(this.shutdownTasks, mapCodec));
        return (CodecMapRegistry.Assets)registry;
    }
    
    @Nonnull
    public <V> MapKeyMapRegistry<V> getCodecRegistry(@Nonnull final MapKeyMapCodec<V> mapCodec) {
        final IRegistry registry = this.codecMapRegistries.computeIfAbsent(mapCodec, v -> new MapKeyMapRegistry(this.shutdownTasks, mapCodec));
        return (MapKeyMapRegistry)registry;
    }
    
    @Nonnull
    public final String getBasePermission() {
        return this.basePermission;
    }
    
    public boolean isDisabled() {
        return this.state == PluginState.NONE || this.state == PluginState.DISABLED || this.state == PluginState.SHUTDOWN;
    }
    
    public boolean isEnabled() {
        return !this.isDisabled();
    }
    
    protected void setup0() {
        if (this.state != PluginState.NONE && this.state != PluginState.DISABLED) {
            throw new IllegalArgumentException(String.valueOf(this.state));
        }
        this.state = PluginState.SETUP;
        try {
            this.setup();
        }
        catch (final Throwable t) {
            this.logger.at(Level.SEVERE).withCause(t).log("Failed to setup plugin %s", this.identifier);
            this.state = PluginState.DISABLED;
        }
    }
    
    protected void setup() {
    }
    
    protected void start0() {
        if (this.state != PluginState.SETUP) {
            throw new IllegalArgumentException(String.valueOf(this.state));
        }
        this.state = PluginState.START;
        try {
            this.start();
            this.state = PluginState.ENABLED;
        }
        catch (final Throwable t) {
            this.logger.at(Level.SEVERE).withCause(t).log("Failed to start %s", this.identifier);
            this.state = PluginState.DISABLED;
        }
    }
    
    protected void start() {
    }
    
    protected void shutdown0(final boolean shutdown) {
        this.state = PluginState.SHUTDOWN;
        try {
            this.shutdown();
            this.state = PluginState.DISABLED;
        }
        catch (final Throwable t) {
            this.logger.at(Level.SEVERE).withCause(t).log("Failed to shutdown %s", this.identifier);
        }
        this.cleanup(shutdown);
    }
    
    protected void shutdown() {
    }
    
    void cleanup(final boolean shutdown) {
        this.commandRegistry.shutdown();
        this.eventRegistry.shutdown();
        this.clientFeatureRegistry.shutdown();
        this.blockStateRegistry.shutdown();
        this.taskRegistry.shutdown();
        this.entityStoreRegistry.shutdown();
        this.chunkStoreRegistry.shutdown();
        this.codecMapRegistries.forEach((k, v) -> v.shutdown());
        this.assetRegistry.shutdown();
        for (int i = this.shutdownTasks.size() - 1; i >= 0; --i) {
            this.shutdownTasks.get(i).accept(shutdown);
        }
    }
    
    @Nonnull
    public abstract PluginType getType();
    
    static {
        METRICS_REGISTRY = new MetricsRegistry<PluginBase>(MetricProvider.maybe((Function<PluginBase, Object>)Function.identity())).register("Identifier", plugin -> plugin.identifier.toString(), (Codec<String>)Codec.STRING).register("Type", (Function<PluginBase, Object>)PluginBase::getType, new EnumCodec<Object>((Class<Object>)PluginType.class)).register("Manifest", plugin -> plugin.manifest, PluginManifest.CODEC).register("State", plugin -> plugin.state, (Codec<Object>)new EnumCodec<Object>((Class<Object>)PluginState.class)).register("Builtin", plugin -> {
            final boolean b;
            if (plugin instanceof final JavaPlugin jp) {
                if (jp.getClassLoader().isInServerClassPath()) {
                    return Boolean.valueOf(b);
                }
            }
            return Boolean.valueOf(b);
        }, Codec.BOOLEAN);
    }
}
