package com.zivilon.cindercore.util;

import cpw.mods.fml.common.network.NetworkRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.common.network.internal.FMLNetworkHandler;
import cpw.mods.fml.common.network.FMLEmbeddedChannel;
import cpw.mods.fml.common.registry.GameData;
import cpw.mods.fml.common.registry.GameData.GameDataSnapshot;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
import cpw.mods.fml.common.network.internal.FMLMessage;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.WorldServer;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.UUID;
import java.util.EnumMap;

import org.apache.logging.log4j.Logger;

import com.google.common.collect.BiMap;

import com.zivilon.cindercore.interfaces.IFMLNetworkHandlerMixin;
import com.zivilon.cindercore.CinderCore;
import com.zivilon.cindercore.network.PacketInterceptor;
import com.zivilon.cindercore.mixins.MixinEntityRegistry;
import com.zivilon.cindercore.mixins.EntityRegistryAccessor;
import com.zivilon.cindercore.mixins.FMLNetworkHandlerAccessor;

public class NetworkUtil {

    private static EnumMap<Side, FMLEmbeddedChannel> channelPair;

    private static EnumMap<Side, FMLEmbeddedChannel> getChannelPair() {
        if (channelPair == null) {
            channelPair = FMLNetworkHandlerAccessor.getChannelPair();
        }
        return channelPair;
    }

    public static Packet getEntitySpawningPacket(Entity entity, EntityPlayerMP player) {
        Entity new_entity = null;
        UUID uuid = player.getUniqueID();
        boolean modified = false;
        Map<String, Map<Integer, Integer>> custom_fallback_map = CinderCore.getCustomFallbackMap(uuid);
        if (custom_fallback_map != null && !(custom_fallback_map.isEmpty())) {
            modified = true;
            new_entity = deepCopy(entity);
            PacketInterceptor.process_custom_entity(new_entity, uuid);
        }

        EntityRegistration er = EntityRegistry.instance().lookupModSpawn(entity.getClass(), false);

        if (er == null || er.usesVanillaSpawning()) {
            return null;
        }
        Map<Integer, Integer> fallback_map = CinderCore.getEntityFallbackMap(uuid);
        Integer entity_ID = er.getModEntityId();

        if (fallback_map != null && fallback_map.containsKey(entity_ID)) {
            modified = true;
            int fallback_ID = fallback_map.get(entity_ID);
            er = get_entity_registration_by_entity_id(fallback_ID) ;
        }

        if (!modified) {
            FMLMessage.EntitySpawnMessage msg = new FMLMessage.EntitySpawnMessage(er, entity, er.getContainer());
            return getChannelPair().get(Side.SERVER).generatePacketFrom(msg);
        }

        if (new_entity == null) {
            new_entity = deepCopy(entity);
        }
        FMLMessage.EntitySpawnMessage msg = new FMLMessage.EntitySpawnMessage(er, new_entity, er.getContainer());
        return getChannelPair().get(Side.SERVER).generatePacketFrom(msg);
    }

    public static EntityRegistration get_entity_registration_by_entity_id(int entityId) {
        EntityRegistryAccessor accessor = (EntityRegistryAccessor) EntityRegistry.instance();
        BiMap<Class<? extends Entity>, EntityRegistration> entityClassRegistrations = accessor.getEntityClassRegistrations();

        for (EntityRegistration registration : entityClassRegistrations.values()) {
            if (registration.getModEntityId() == entityId) {
                return registration;
            }
        }
        return null;
    }

    public static Entity deepCopy(Entity entity) {
        MinecraftServer minecraftserver = MinecraftServer.getServer();
        WorldServer worldserver = minecraftserver.worldServerForDimension(entity.dimension);
        if (worldserver == null) return entity;
        Entity new_entity = EntityList.createEntityByName(EntityList.getEntityString(entity), worldserver);
        if (new_entity == null) return entity;

        new_entity.copyDataFrom(entity, true);
        int old_entity_id = entity.getEntityId();
        int new_entity_id = new_entity.getEntityId();
        new_entity.setEntityId(old_entity_id);
        return new_entity;
    }

    public static void writeLog(String message) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("custom_log.txt", true))) {
            LocalDateTime now = LocalDateTime.now();
            String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            writer.write(timestamp + " - " + message + "\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
