package com.zivilon.cindercore_modlist;

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.network.FMLNetworkEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent.ServerConnectionFromClientEvent;
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.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ChatComponentText;

import io.netty.channel.Channel;

@Mod(modid = CinderCoreModlist.MODID, version = CinderCoreModlist.VERSION, dependencies = "required-after:spongemixins@[1.1.0,)", acceptableRemoteVersions = "*", useMetadata = true)
public class CinderCoreModlist
{
    public static final String MODID = "cindercore_modlist";
    public static final String VERSION = "1.0";
//    public static final Map<UUID, Channel> channel_map = new HashMap<>();
    public static final Map<UUID, Map<String, String>> player_mods = new HashMap<>();
    public static final Map<Integer, FallbackEntry> item_fallback_map = new HashMap<>();
    public static final Map<Integer, FallbackEntry> entity_fallback_map = new HashMap<>();
    public static final Map<String, CustomFallbackEntry> custom_fallback_map = new HashMap<>();
    public static final Map<UUID, Map<Integer, Integer>> player_item_fallback_map = new HashMap<>();
    public static final Map<UUID, Map<Integer, Integer>> player_entity_fallback_map = new HashMap<>();
    public static final Map<UUID, Map<String, Map<Integer, Integer>>> player_custom_fallback_map = new HashMap<>();
    public static final boolean debug = false;

    @EventHandler
    public void init(FMLInitializationEvent event)
    {
        System.out.println("Cinder Core Modlist initialized");
    }

    // Used to save mod list into player_mods. Invoked in handshake process. USE WITH EXTREME CAUTION.
    public static void savePlayerModList(UUID player_id, Map<String, String> modList) {
        player_mods.put(player_id, modList);
    }

/*    public static Channel get_FML_channel(UUID uuid) {
        return channel_map.get(uuid);
    }*/

    // Get map of all mods player has installed. Includes versions.
    public static Map<String, String> getPlayerMods(String playerName) {
        return player_mods.get(playerName);
    }

    // Returns true if player has mod installed. False if not.
    public static boolean hasPlayerMod(String playerName, String modId) {
        Map<String, String> mods = player_mods.get(playerName);
        return mods != null && mods.containsKey(modId);
    }

    // Get base fallback maps. Used for generating player's fallback maps.
    public static Map<Integer, FallbackEntry> getItemFallbackMap() {
        return item_fallback_map;
    }
    public static Map<Integer, FallbackEntry> getEntityFallbackMap() {
        return entity_fallback_map;
    }
    public static Map<String, CustomFallbackEntry> getCustomFallbackMap() {
        return custom_fallback_map;
    }

    // Register fallbacks. Recommended to use the wrappers from CinderCore instead.
    public static void register_item_fallback(Integer original_id, FallbackEntry fallback) {
        item_fallback_map.put(original_id, fallback);
        if (debug) {
            if (item_fallback_map == null) {
                System.out.println("[CinderCoreModList] Item fallback map is null");
            } else if (item_fallback_map.isEmpty()) {
                System.out.println("[CinderCoreModList] Item fallback map is empty");
            } else {
                System.out.println("[CinderCoreModList] Contents of item fallback map:");
                printMapContents(item_fallback_map);
            }
        }
    }
    public static void register_entity_fallback(Integer entity_id, FallbackEntry fallback) {
        entity_fallback_map.put(entity_id, fallback);
        if (debug) {
            if (entity_fallback_map == null) {
                System.out.println("[CinderCoreModList] Entity fallback map is null");
            } else if (entity_fallback_map.isEmpty()) {
                System.out.println("[CinderCoreModList] Entity fallback map is empty");
            } else {
                System.out.println("[CinderCoreModList] Contents of entity fallback map:");
                printMapContents(entity_fallback_map);
            }
        }
    }

    public static void register_custom_fallback(String type, CustomFallbackEntry fallback) {
        custom_fallback_map.put(type, fallback);
        if (debug) {
            if (custom_fallback_map == null) {
                System.out.println("[CinderCoreModList] Custom fallback map is null");
            } else if (entity_fallback_map.isEmpty()) {
                System.out.println("[CinderCoreModList] Custom fallback map is empty");
            } else {
                System.out.println("[CinderCoreModList] Contents of custom fallback map:");
                printMapContentsCustom(custom_fallback_map);
            }
        }
    }


    // Get map of specific player's fallbacks
    public static Map<Integer, Integer> getPlayerItemFallbacks(UUID player_id) {
        return player_item_fallback_map.get(player_id);
    }
    public static Map<Integer, Integer> getPlayerEntityFallbacks(UUID player_id) {
        return player_entity_fallback_map.get(player_id);
    }
    public static Map<String, Map<Integer, Integer>> getPlayerCustomFallbacks(UUID player_id) {
        if (debug) {
            System.out.println("[CinderCoreModlist] Getting custom fallbacks for user " + player_id);
            System.out.println("[CinderCoreModlist] Full list of player fallbacks:");
            printAllPlayerMapContentsCustom(player_custom_fallback_map);
        }
        return player_custom_fallback_map.get(player_id);
    }

    // Generate specific player's fallbacks. Invoked during handshake process.
    public static void generatePlayerItemFallbackMap(UUID player_id) {
        System.out.println("[CinderCoreModList] Generating fallback map for " + player_id);
        Map<String, String> player_modlist = player_mods.get(player_id);
        Map<Integer, Integer> fallbacks = new HashMap<>();

        if (debug) {
            if (item_fallback_map == null) {
                System.out.println("[CinderCoreModList] Item fallback map is null");
            } else {
                System.out.println("[CinderCoreModList] Contents of general fallback map during player map generation:");
                printMapContents(item_fallback_map);
            }
        }

        for (Map.Entry<Integer, FallbackEntry> entry : item_fallback_map.entrySet()) {
            FallbackEntry fallbackEntry = entry.getValue();
            String player_version = player_modlist.get(fallbackEntry.mod_name);

            if (player_version == null || compare_versions(player_version, fallbackEntry.version) < 0) {
                if (debug) System.out.println("[CinderCoreModList] Player missing mod: " + fallbackEntry.mod_name + " version " + fallbackEntry.version);
                fallbacks.put(entry.getKey(), fallbackEntry.fallback_id);
            }
        }
        if (debug) {
            System.out.println("[CinderCoreModList] Adding to fallback map:");
            printMapContentsInt(fallbacks);
        }
        player_item_fallback_map.put(player_id, fallbacks);
    }

    public static void generatePlayerEntityFallbackMap(UUID player_id) {
        Map<String, String> player_modlist = player_mods.get(player_id);
        Map<Integer, Integer> fallbacks = new HashMap<>();

        for (Map.Entry<Integer, FallbackEntry> entry : entity_fallback_map.entrySet()) {
            FallbackEntry fallbackEntry = entry.getValue();
            String player_version = player_modlist.get(fallbackEntry.mod_name);

            if (player_version == null || compare_versions(player_version, fallbackEntry.version) < 0) {
                fallbacks.put(entry.getKey(), fallbackEntry.fallback_id);
            }
        }

        player_entity_fallback_map.put(player_id, fallbacks);
    }

    public static void generatePlayerCustomFallbackMap(UUID player_id) {
        Map<String, String> player_modlist = player_mods.get(player_id); 
        Map<String, Map<Integer, Integer>> fallbacks = new HashMap<>(); // Nested structure

        for (Map.Entry<String, CustomFallbackEntry> entry : custom_fallback_map.entrySet()) {
            CustomFallbackEntry fallbackEntry = entry.getValue();
            String player_version = player_modlist.get(fallbackEntry.mod_name);

            if (player_version == null || compare_versions(player_version, fallbackEntry.version) < 0) {
                Map<Integer, Integer> type_fallbacks = fallbacks.get(fallbackEntry.type);
                if (type_fallbacks == null) {
                    type_fallbacks = new HashMap<>();
                    fallbacks.put(fallbackEntry.type, type_fallbacks);
                }
                type_fallbacks.put(fallbackEntry.original_id, fallbackEntry.fallback_id);
            }
        }
        player_custom_fallback_map.put(player_id, fallbacks); 
    }


    // Used for comparing versions of mods. Version must be numbers separated by . and nothing else.
    public static int compare_versions(String version1, String version2) {
        String[] parts1 = version1.split("\\.");
        String[] parts2 = version2.split("\\.");

        for (int i = 0; i < Math.max(parts1.length, parts2.length); i++) {
            int part1 = i < parts1.length ? Integer.parseInt(parts1[i]) : 0;
            int part2 = i < parts2.length ? Integer.parseInt(parts2[i]) : 0;
            if (part1 != part2) {
                return part1 < part2 ? -1 : 1;
            }
        }
        return 0;
    }

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

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

    // Debug method
    public static void printAllPlayerMapContents(Map<UUID, Map<Integer, Integer>> map) {
        if (map.isEmpty()) {
            System.out.println("[CinderCoreModList] There are no player fallback maps.");
        } else {
            for (Map.Entry<UUID, Map<Integer, Integer>> entry : map.entrySet()) {
                System.out.println("[CinderCoreModList] Reading player fallback map: " + entry.getKey());
                printMapContentsInt(entry.getValue());
            }
        }
    }
    public static void printAllPlayerMapContentsCustom(Map<UUID, Map<String, Map<Integer, Integer>>> map) {
        if (map.isEmpty()) {
            System.out.println("[CinderCoreModList] There are no player fallback maps.");
        } else {
            for (Map.Entry<UUID, Map<String, Map<Integer, Integer>>> playerEntry : map.entrySet()) {
                UUID playerUUID = playerEntry.getKey();
                Map<String, Map<Integer, Integer>> typeMap = playerEntry.getValue();

                System.out.println("[CinderCoreModList] Reading player fallback map for UUID: " + playerUUID);

                for (Map.Entry<String, Map<Integer, Integer>> typeEntry : typeMap.entrySet()) {
                    String dataType = typeEntry.getKey();
                    Map<Integer, Integer> fallbackMap = typeEntry.getValue();

                    System.out.println("[CinderCoreModList] Fallbacks for type: " + dataType);
                    printMapContentsInt(fallbackMap); 
                }
            }
        }
    }

}
