/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.bausystem.utils.tps;

import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.ChatWrapper;
import de.steamwar.core.Core;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@UtilityClass
class PacketCache {

    private static List<Object> packets = new ArrayList<>();
    private static Set<Entity> entities = new HashSet<>();
    private static BukkitTask task = null;

    private static Class<?> vec3dClass = Reflection.getClass("net.minecraft.world.phys.Vec3");
    private static Reflection.Field<Object> zeroVec3d = (Reflection.Field<Object>) Reflection.getField(vec3dClass, vec3dClass, 0);
    private static Object ZERO_VEC3D = zeroVec3d.get(null);
    private static Class<?> velocityPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket");
    private static Reflection.Constructor velocityPacketConstructor = Reflection.getConstructor(velocityPacketClass, int.class, vec3dClass);

    private static Class<?> teleportPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
    private static Class<?> entityClass = Reflection.getClass("net.minecraft.world.entity.Entity");
    private static Reflection.Constructor teleportPacketConstructor = Reflection.getConstructor(teleportPacketClass, entityClass);

    private static Class<?> craftEntityClass = Reflection.getClass("org.bukkit.craftbukkit.entity.CraftEntity");
    private static Reflection.Method getHandle = Reflection.getMethod(craftEntityClass, "getHandle");

    private static Object noGravityDataWatcher = BountifulWrapper.impl.getDataWatcherObject(5, Boolean.class);
    private static Object fuseDataWatcher = BountifulWrapper.impl.getDataWatcherObject(8, Integer.class);

    public void continuousSendCache() {
        if (task != null) {
            return;
        }

        createPackets();
        task = new BukkitRunnable() {
            @Override
            public void run() {
                _sendCache();
            }
        }.runTaskTimer(Core.getInstance(), 1, 1);
    }

    public void sendCache() {
        if (task != null) {
            task.cancel();
            task = null;
        }
        _sendCache();
    }

    private void _sendCache() {
        createPackets();
        for (Player player : Bukkit.getOnlinePlayers()) {
            for (Object packet : packets) {
                TinyProtocol.instance.sendPacket(player, packet);
            }
        }
    }

    public void clearCache() {
        packets.clear();
        entities.clear();

        if (task != null) {
            task.cancel();
            task = null;
        }
    }

    private void createPackets() {
        if (entities.stream().anyMatch(Entity::isDead)) {
            entities.clear();
            packets.clear();
        }
        List<Entity> entities = Bukkit.getWorlds().get(0).getEntities().stream()
                .filter(e -> !(e instanceof Player))
                .filter(e -> PacketCache.entities.add(e))
                .collect(Collectors.toList());

        for (Entity entity : entities) {
            packets.add(teleportPacketConstructor.invoke(getHandle.invoke(entity)));
        }
        for (Entity entity : entities) {
            packets.add(velocityPacketConstructor.invoke(entity.getEntityId(), ZERO_VEC3D));
        }
        for (Entity entity : entities) {
            packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), noGravityDataWatcher, true));
        }
        for (Entity entity : entities) {
            if (!(entity instanceof TNTPrimed)) continue;
            TNTPrimed tnt = (TNTPrimed) entity;
            int fuse = tnt.getFuseTicks();
            packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), fuseDataWatcher, fuse - (fuse % 5) + 1));
        }
    }
}
