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

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

import com.hypixel.hytale.math.vector.Vector3i;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.component.Ref;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.universe.world.meta.BlockState;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.store.CodecKey;
import com.hypixel.hytale.codec.store.StoredCodec;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.server.core.universe.world.chunk.systems.ChunkSystems;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.chunk.section.blockpositions.BlockPositionProvider;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.environment.EnvironmentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.EntityChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class LegacyModule extends JavaPlugin
{
    public static final PluginManifest MANIFEST;
    private static LegacyModule instance;
    private ComponentType<ChunkStore, WorldChunk> worldChunkComponentType;
    private ComponentType<ChunkStore, BlockChunk> blockChunkComponentType;
    private ComponentType<ChunkStore, EntityChunk> entityChunkComponentType;
    private ComponentType<ChunkStore, BlockComponentChunk> blockComponentChunkComponentType;
    private ComponentType<ChunkStore, EnvironmentChunk> environmentChunkComponentType;
    private ComponentType<ChunkStore, ChunkColumn> chunkColumnComponentType;
    private ComponentType<ChunkStore, ChunkSection> chunkSectionComponentType;
    private ComponentType<ChunkStore, BlockSection> blockSectionComponentType;
    private ComponentType<ChunkStore, FluidSection> fluidSectionComponentType;
    private ComponentType<ChunkStore, BlockPositionProvider> blockPositionProviderComponentType;
    
    public static LegacyModule get() {
        return LegacyModule.instance;
    }
    
    public LegacyModule(@Nonnull final JavaPluginInit init) {
        super(init);
        LegacyModule.instance = this;
    }
    
    @Override
    protected void setup() {
        this.worldChunkComponentType = this.getChunkStoreRegistry().registerComponent(WorldChunk.class, "WorldChunk", WorldChunk.CODEC);
        this.blockChunkComponentType = this.getChunkStoreRegistry().registerComponent(BlockChunk.class, "BlockChunk", BlockChunk.CODEC);
        this.entityChunkComponentType = this.getChunkStoreRegistry().registerComponent(EntityChunk.class, "EntityChunk", EntityChunk.CODEC);
        this.blockComponentChunkComponentType = this.getChunkStoreRegistry().registerComponent(BlockComponentChunk.class, "BlockComponentChunk", BlockComponentChunk.CODEC);
        this.environmentChunkComponentType = this.getChunkStoreRegistry().registerComponent(EnvironmentChunk.class, "EnvironmentChunk", EnvironmentChunk.CODEC);
        this.chunkColumnComponentType = this.getChunkStoreRegistry().registerComponent(ChunkColumn.class, "ChunkColumn", ChunkColumn.CODEC);
        this.chunkSectionComponentType = this.getChunkStoreRegistry().registerComponent(ChunkSection.class, "ChunkSection", ChunkSection.CODEC);
        this.blockSectionComponentType = this.getChunkStoreRegistry().registerComponent(BlockSection.class, "Block", BlockSection.CODEC);
        this.fluidSectionComponentType = this.getChunkStoreRegistry().registerComponent(FluidSection.class, "Fluid", FluidSection.CODEC);
        this.blockPositionProviderComponentType = this.getChunkStoreRegistry().registerComponent(BlockPositionProvider.class, () -> {
            throw new UnsupportedOperationException("BlockPositionProvider cannot be constructed");
        });
        this.getChunkStoreRegistry().registerSystem(new ChunkSystems.OnNewChunk());
        this.getChunkStoreRegistry().registerSystem(new ChunkSystems.OnChunkLoad());
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new ChunkSystems.OnNonTicking());
        this.getChunkStoreRegistry().registerSystem(new ChunkSystems.EnsureBlockSection());
        this.getChunkStoreRegistry().registerSystem(new MigrateLegacySections());
        this.getChunkStoreRegistry().registerSystem(new ChunkSystems.LoadBlockSection());
        this.getChunkStoreRegistry().registerSystem(new ChunkSystems.ReplicateChanges());
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new BlockChunk.LoadBlockChunkPacketSystem(this.blockChunkComponentType));
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new EntityChunk.EntityChunkLoadingSystem());
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new BlockComponentChunk.BlockComponentChunkLoadingSystem());
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new BlockComponentChunk.LoadBlockComponentPacketSystem(this.blockComponentChunkComponentType));
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new BlockComponentChunk.UnloadBlockComponentPacketSystem(this.blockComponentChunkComponentType));
        final ComponentType<ChunkStore, LegacyBlockStateChunk> legacyBlockStateComponentType = this.getChunkStoreRegistry().registerComponent(LegacyBlockStateChunk.class, "BlockStateChunk", LegacyBlockStateChunk.CODEC, true);
        this.getChunkStoreRegistry().registerSystem(new MigrateLegacyBlockStateChunkSystem(legacyBlockStateComponentType, this.blockComponentChunkComponentType));
    }
    
    public ComponentType<ChunkStore, WorldChunk> getWorldChunkComponentType() {
        return this.worldChunkComponentType;
    }
    
    public ComponentType<ChunkStore, BlockChunk> getBlockChunkComponentType() {
        return this.blockChunkComponentType;
    }
    
    public ComponentType<ChunkStore, EntityChunk> getEntityChunkComponentType() {
        return this.entityChunkComponentType;
    }
    
    public ComponentType<ChunkStore, BlockComponentChunk> getBlockComponentChunkComponentType() {
        return this.blockComponentChunkComponentType;
    }
    
    public ComponentType<ChunkStore, EnvironmentChunk> getEnvironmentChunkComponentType() {
        return this.environmentChunkComponentType;
    }
    
    public ComponentType<ChunkStore, ChunkColumn> getChunkColumnComponentType() {
        return this.chunkColumnComponentType;
    }
    
    public ComponentType<ChunkStore, ChunkSection> getChunkSectionComponentType() {
        return this.chunkSectionComponentType;
    }
    
    public ComponentType<ChunkStore, BlockSection> getBlockSectionComponentType() {
        return this.blockSectionComponentType;
    }
    
    public ComponentType<ChunkStore, FluidSection> getFluidSectionComponentType() {
        return this.fluidSectionComponentType;
    }
    
    public ComponentType<ChunkStore, BlockPositionProvider> getBlockPositionProviderComponentType() {
        return this.blockPositionProviderComponentType;
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(LegacyModule.class).build();
    }
    
    private static class LegacyBlockStateChunk implements Component<ChunkStore>
    {
        public static final BuilderCodec<LegacyBlockStateChunk> CODEC;
        public Holder<ChunkStore>[] holders;
        
        public LegacyBlockStateChunk() {
        }
        
        public LegacyBlockStateChunk(final Holder<ChunkStore>[] holders) {
            this.holders = holders;
        }
        
        @Nonnull
        @Override
        public Component<ChunkStore> clone() {
            final Holder<ChunkStore>[] newHolders = new Holder[this.holders.length];
            for (int i = 0; i < this.holders.length; ++i) {
                newHolders[i] = this.holders[i].clone();
            }
            return new LegacyBlockStateChunk(newHolders);
        }
        
        static {
            CODEC = BuilderCodec.builder(LegacyBlockStateChunk.class, LegacyBlockStateChunk::new).addField(new KeyedCodec<Holder[]>("States", new ArrayCodec<Holder>((Codec<Holder>)new StoredCodec<Holder>((CodecKey<Holder>)ChunkStore.HOLDER_CODEC_KEY), Holder[]::new)), (entityChunk, array) -> entityChunk.holders = array, entityChunk -> {
                throw new UnsupportedOperationException("Serialise is not allowed for BlockStateChunk");
            }).build();
        }
    }
    
    @Deprecated(forRemoval = true)
    public static class MigrateLegacySections extends ChunkColumnMigrationSystem
    {
        private final Query<ChunkStore> QUERY;
        private final Set<Dependency<ChunkStore>> DEPENDENCIES;
        
        public MigrateLegacySections() {
            this.QUERY = (Query<ChunkStore>)Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType());
            this.DEPENDENCIES = Set.of((Dependency<ChunkStore>)new SystemDependency(Order.AFTER, (Class<ISystem>)ChunkSystems.OnNewChunk.class), (Dependency<ChunkStore>)RootDependency.first());
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<ChunkStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<ChunkStore> store) {
            final ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType());
            assert column != null;
            final BlockChunk blockChunk = holder.getComponent(BlockChunk.getComponentType());
            assert blockChunk != null;
            final Holder<ChunkStore>[] sections = column.getSectionHolders();
            final BlockSection[] migratedSections = blockChunk.takeMigratedSections();
            if (migratedSections != null) {
                for (int i = 0; i < sections.length; ++i) {
                    final Holder<ChunkStore> section = sections[i];
                    final BlockSection blockSection = migratedSections[i];
                    if (section != null) {
                        if (blockSection != null) {
                            section.putComponent(BlockSection.getComponentType(), blockSection);
                            blockChunk.markNeedsSaving();
                        }
                    }
                }
            }
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<ChunkStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<ChunkStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<ChunkStore> getQuery() {
            return this.QUERY;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<ChunkStore>> getDependencies() {
            return this.DEPENDENCIES;
        }
    }
    
    private static class MigrateLegacyBlockStateChunkSystem extends ChunkColumnMigrationSystem
    {
        private static final HytaleLogger LOGGER;
        private final ComponentType<ChunkStore, LegacyBlockStateChunk> legacyComponentType;
        private final ComponentType<ChunkStore, BlockComponentChunk> componentType;
        private final Archetype<ChunkStore> archetype;
        
        public MigrateLegacyBlockStateChunkSystem(final ComponentType<ChunkStore, LegacyBlockStateChunk> legacyComponentType, final ComponentType<ChunkStore, BlockComponentChunk> componentType) {
            this.legacyComponentType = legacyComponentType;
            this.componentType = componentType;
            this.archetype = Archetype.of(legacyComponentType, WorldChunk.getComponentType());
        }
        
        @Override
        public Query<ChunkStore> getQuery() {
            return this.archetype;
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<ChunkStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<ChunkStore> store) {
            final LegacyBlockStateChunk component = holder.getComponent(this.legacyComponentType);
            assert component != null;
            holder.removeComponent(this.legacyComponentType);
            final Int2ObjectOpenHashMap<Holder<ChunkStore>> holders = new Int2ObjectOpenHashMap<Holder<ChunkStore>>();
            for (final Holder<ChunkStore> blockComponentHolder : component.holders) {
                final BlockState blockState = BlockState.getBlockState(blockComponentHolder);
                final Vector3i position = blockState.__internal_getPosition();
                if (position == null) {
                    MigrateLegacyBlockStateChunkSystem.LOGGER.at(Level.SEVERE).log("Skipping migration for BlockState with null position!", blockComponentHolder);
                }
                else {
                    holders.put(blockState.getIndex(), blockComponentHolder);
                }
            }
            final BlockComponentChunk blockComponentChunk = new BlockComponentChunk(holders, new Int2ObjectOpenHashMap<Ref<ChunkStore>>());
            holder.addComponent(this.componentType, blockComponentChunk);
            holder.getComponent(WorldChunk.getComponentType()).setBlockComponentChunk(blockComponentChunk);
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<ChunkStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<ChunkStore> store) {
        }
        
        @Nonnull
        @Override
        public Set<Dependency<ChunkStore>> getDependencies() {
            return RootDependency.firstSet();
        }
        
        static {
            LOGGER = HytaleLogger.forEnclosingClass();
        }
    }
}
