package com.elytradev.movingworld.common.experiments;

import com.elytradev.movingworld.client.experiments.InputReader;
import com.elytradev.movingworld.client.experiments.MovingWorldClientDatabase;
import com.elytradev.movingworld.common.experiments.network.messages.server.MessageDimensionPoolData;
import com.elytradev.movingworld.common.experiments.network.messages.server.MessageFullPoolData;
import com.elytradev.movingworld.common.experiments.region.RegionPool;
import com.elytradev.movingworld.common.experiments.world.MWPlayerChunkMap;
import com.elytradev.movingworld.common.experiments.world.MovingWorldProvider;
import com.google.common.collect.HashBiMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.DimensionType;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.ReflectionHelper;

import java.lang.reflect.Field;
import java.util.HashMap;

/**
 * Handles init and deinit for subworld registration, region pool io, etc.
 */
public class MovingWorldInitHandler {

    public static HashBiMap<Integer, Integer> registeredDimensions = HashBiMap.create();
    public static int startingDimID = 50;
    public static int activeDimID = startingDimID;

    @SubscribeEvent
    public void onWorldLoad(WorldEvent.Load e) {
        Integer loadedDimensionID = e.getWorld().provider.getDimension();

        if (registeredDimensions.containsKey(loadedDimensionID)
                || registeredDimensions.containsValue(loadedDimensionID)) {
            return;
        }

        try {
            registeredDimensions.put(loadedDimensionID, activeDimID);
            DimensionManager.registerDimension(activeDimID,
                    DimensionType.register("MovingWorld|P" + loadedDimensionID + "|C" + activeDimID,
                            "movingworld", activeDimID, MovingWorldProvider.class, true));
            DimensionManager.initDimension(activeDimID);
            WorldServer worldServer = DimensionManager.getWorld(activeDimID);
            Field playerChunkMap = ReflectionHelper.findField(WorldServer.class, "playerChunkMap", "field_73063_M");
            playerChunkMap.setAccessible(true);
            playerChunkMap.set(worldServer, new MWPlayerChunkMap(worldServer));
            RegionPool.getPool(activeDimID, true);
            worldServer.addEventListener(new BoundingBoxWorldListener());
            MovingWorldExperimentsMod.logger.info("DB check: " + MovingWorldExperimentsMod.modProxy.getCommonDB().getWorldFromDim(activeDimID));
            activeDimID++;
        } catch (Exception exception) {
            MovingWorldExperimentsMod.logger.error("Exception on subworld registration/load ", e);
        }
    }

    @SubscribeEvent
    public void onConnection(PlayerEvent.PlayerLoggedInEvent e) {
        if (!e.isCanceled() && e.player != null && !e.player.world.isRemote) {
            new MessageFullPoolData(RegionPool.writeAllToCompound()).sendTo(e.player);
        }
    }

    @SubscribeEvent
    public void onDimChange(PlayerEvent.PlayerChangedDimensionEvent e) {
        if (!e.isCanceled() && e.player != null && !e.player.world.isRemote) {
            new MessageDimensionPoolData(e.toDim, RegionPool.getPool(e.toDim, true).writePoolToCompound()).sendTo(e.player);
        }
    }

    @SubscribeEvent
    public void onClientTick(TickEvent.ClientTickEvent e) {
        if (e.phase == TickEvent.Phase.START) {
            Minecraft mc = Minecraft.getMinecraft();
            MovingWorldClientDatabase cDB = (MovingWorldClientDatabase) MovingWorldExperimentsMod.modProxy.getClientDB();

            if (mc.isGamePaused() || mc.world == null
                    || mc.player == null) {
                if ((mc.world == null
                        || mc.player == null
                        || mc.playerController == null) && InputReader.INSTANCE != null) {
                    MinecraftForge.EVENT_BUS.unregister(InputReader.INSTANCE);
                    InputReader.INSTANCE = null;
                }

                return;
            }

            if (InputReader.INSTANCE == null) {
                InputReader.INSTANCE = new InputReader(mc.playerController);
            }

            for (HashMap.Entry<Integer, WorldClient> mapEntry : cDB.worlds.entrySet()) {
                mapEntry.getValue().updateEntities();
                mapEntry.getValue().tick();
            }
        }
    }
}

@@ -48,16 +48,131 @@
 ments;%0A%0A
+import com.elytradev.concrete.reflect.accessor.Accessor;%0Aimport com.elytradev.concrete.reflect.accessor.Accessors;%0A
 import c
@@ -649,24 +649,109 @@
 erChunkMap;%0A
+import com.elytradev.movingworld.common.experiments.world.MWServerWorldEventHandler;%0A
 import com.e
@@ -978,27 +978,134 @@
 aft.
-world.DimensionType
+server.management.PlayerChunkMap;%0Aimport net.minecraft.world.DimensionType;%0Aimport net.minecraft.world.ServerWorldEventHandler
 ;%0Aim
@@ -1480,99 +1480,8 @@
 nt;%0A
-import net.minecraftforge.fml.relauncher.ReflectionHelper;%0A%0Aimport java.lang.reflect.Field;
 %0Aimp
@@ -1809,16 +1809,221 @@
 DimID;%0A%0A
+    private Accessor%3CPlayerChunkMap%3E playerChunkMap;%0A%0A%0A    public MovingWorldInitHandler() %7B%0A        this.playerChunkMap = Accessors.findField(WorldServer.class, %22playerChunkMap%22, %22field_73063_M%22);%0A    %7D%0A%0A
     @Sub
@@ -2343,16 +2343,54 @@
   try %7B%0A
+            // Register new dimension%0A
         
@@ -2830,32 +2830,33 @@
 d(activeDimID);%0A
+%0A
             Fiel
@@ -2855,240 +2855,575 @@
     
-Field playerChunkMap = ReflectionHelper.findField(WorldServer.class, %22playerChunkMap%22, %22field_73063_M%22);%0A            playerChunkMap.setAccessible(true);%0A            playerChunkMap.set(worldServer, new MWPlayerChunkMap(worldServer));
+// Inject set MW component alternatives.%0A            playerChunkMap.set(worldServer, new MWPlayerChunkMap(worldServer));%0A            ServerWorldEventHandler currentEventListener = (ServerWorldEventHandler) worldServer.eventListeners.stream()%0A                    .filter((ev) -%3E ev instanceof ServerWorldEventHandler).findFirst().get();%0A            worldServer.eventListeners.remove(currentEventListener);%0A            worldServer.addEventListener(new MWServerWorldEventHandler(worldServer.mcServer, worldServer));%0A%0A            // Init pool, increment dimension number.
 %0A   

Use accessor in MovingWorldInitHandler, some code commenting, set WorldEventHandler.