package com.zivilon.cindercore;

import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent.ServerConnectionFromClientEvent;
import cpw.mods.fml.common.registry.EntityRegistry;
import java.util.Collections;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;


import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;

import net.minecraft.command.CommandHandler;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.play.server.S30PacketWindowItems;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ChatComponentText;
import net.minecraft.world.World;

import io.netty.channel.Channel;

import com.zivilon.cindercore.network.PacketInterceptor;

import com.zivilon.cindercore.mixins.MixinEntityList;
import com.zivilon.cindercore_modlist.CinderCoreModlist;
import com.zivilon.cindercore_modlist.CustomFallbackEntry;
import com.zivilon.cindercore_modlist.FallbackEntry;

@Mod(modid = CinderCore.MODID, version = CinderCore.VERSION, dependencies = "required-after:spongemixins@[1.1.0,)", acceptableRemoteVersions = "*", useMetadata = true)
public class CinderCore
{
    public static final String MODID = "cindercore";
    public static final String VERSION = "1.0";
    public static final Map<Channel, UUID> playerChannelMap = new HashMap<>();
    public static final boolean debug = false;

    @EventHandler
    public void init(FMLInitializationEvent event)
    {
        System.out.println("Cinder Core initialized");
        if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
            FMLCommonHandler.instance().bus().register(new PlayerEventHandler());
        }
    }

    public static void registerItemFallback(int item_id, int fallback_id, String mod_name, String mod_version) {
        FallbackEntry fallback = new FallbackEntry(item_id, fallback_id, mod_name, mod_version);
        CinderCoreModlist.register_item_fallback(item_id, fallback);
    }

    public static void registerEntityFallback(Class<? extends Entity> entity_class, Class<? extends Entity> fallback_class, String mod_name, String mod_version) {
        EntityRegistry registry_instance = EntityRegistry.instance();
        int entity_id = registry_instance.lookupModSpawn(entity_class, false).getModEntityId();
        int fallback_id;

        EntityRegistry.EntityRegistration modRegistration = registry_instance.lookupModSpawn(fallback_class, false);
        if (modRegistration != null) {
            fallback_id = modRegistration.getModEntityId();
        } else {

            // If it's not a mod entity, check if it's a vanilla entity
            Integer vanillaId = getEntityIDFromClass(fallback_class);
            if (vanillaId != null) {
                fallback_id = vanillaId;
            } else {
                System.out.println("[CinderCore] Attempted to register fallback with unregistered entity class. This will not work!");
                return;
            }
        }

        FallbackEntry fallback = new FallbackEntry(entity_id, fallback_id, mod_name, mod_version);
        if (debug) System.out.println("[CinderCore] Registering entity " + entity_id + " with fallback " + fallback_id);
        CinderCoreModlist.register_entity_fallback(entity_id, fallback);
    }

    public static int getEntityIDFromClass(Class<? extends Entity> entityClass) {
        MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
        World world = (World) server.worldServers[0];
        try {
            Entity entity = entityClass.getConstructor(World.class).newInstance(world);
            return EntityList.getEntityID(entity);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    public static void registerCustomFallback(int original_id, int fallback_id, String type, String mod_name, String mod_version) {
        CustomFallbackEntry fallback = new CustomFallbackEntry(original_id, fallback_id, type, mod_name, mod_version);
        CinderCoreModlist.register_custom_fallback(type, fallback);
    }

    public static Map<Integer, Integer> getItemFallbackMap(UUID uuid) {
        return CinderCoreModlist.getPlayerItemFallbacks(uuid);
    }
    public static Map<Integer, Integer> getEntityFallbackMap(UUID uuid) {
        return CinderCoreModlist.getPlayerEntityFallbacks(uuid);
    }

    public static Map<String, Map<Integer, Integer>> getCustomFallbackMap(UUID uuid) {
        return CinderCoreModlist.getPlayerCustomFallbacks(uuid);
    }

    public static UUID getPlayerFromChannel(Channel channel) {
        return CinderCore.playerChannelMap.get(channel);
    }

    // Debug method
    public static void printMapContents(Map<Integer, Integer> map) {
        if (map.isEmpty()) {
            System.out.println("Map is empty.");
        } else {
            for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
            }
        }
    }

    public class PlayerEventHandler {
        @SubscribeEvent
        public void onConnectionFromClient(FMLNetworkEvent.ServerConnectionFromClientEvent event) {
            // Record channel to map
            Channel channel = event.manager.channel();
            UUID uuid = ((NetHandlerPlayServer) event.handler).playerEntity.getUniqueID();

            if (event.handler instanceof NetHandlerPlayServer) {
                playerChannelMap.put(channel, uuid);
            }
            Map<Integer, Integer> itemFallbackMap = getItemFallbackMap(uuid);
            Map<Integer, Integer> entityFallbackMap = getEntityFallbackMap(uuid);

            if (debug) {
                System.out.println("[CinderCore] Initiating Cinder for UUID: " + uuid);
                if (itemFallbackMap == null) {
                    System.out.println("[CinderCore] Item fallback map is null");
                } else if (itemFallbackMap.isEmpty()) {
                    System.out.println("[CinderCore] Item fallback map is empty");
                } else {
                    System.out.println("[CinderCore] Item fallback map:");                
                    printMapContents(itemFallbackMap);
                }
                if (entityFallbackMap == null) {
                    System.out.println("[CinderCore] Entity fallback map is null");
                } else if (entityFallbackMap.isEmpty()) {
                    System.out.println("[CinderCore] Entity fallback map is empty");                
                } else {
                    System.out.println("[CinderCore] Entity fallback map:");                
                    printMapContents(entityFallbackMap);
                }
                printMapContentsCustom(getCustomFallbackMap(uuid));
            }


            if (uuid != null) {
                // Only attach packet listener if fallback maps are not empty
                if (itemFallbackMap != null && !itemFallbackMap.isEmpty() && entityFallbackMap != null && !entityFallbackMap.isEmpty()) {
                    System.out.println("[CinderCore] Attaching packet interceptor to " + getPlayerFromChannel(channel));
                    channel.pipeline().addLast("modification_handler", new PacketInterceptor(uuid));
                } else if (debug) {
                    System.out.println("[CinderCore] No item fallbacks needed for UUID: " + uuid);
                }
            }
        }
    }

    // Debug method
    public static void printMapContentsCustom(Map<String, Map<Integer, Integer>> data) {
      if (data == null || data.isEmpty()) {
        System.out.println("Map is empty.");
        return;
      }

      for (String outerKey : data.keySet()) {
        System.out.println("Key: " + outerKey);
        Map<Integer, Integer> innerMap = data.get(outerKey);
        for (Integer innerKey : innerMap.keySet()) {
          System.out.println("  - " + innerKey + ": " + innerMap.get(innerKey));
        }
      }
    } 
}
