/*
 *   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.core;

import de.steamwar.Reflection;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializer;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Sound;
import org.bukkit.entity.Player;

import java.util.UUID;

public class BountifulWrapper {
    public static final BountifulWrapper impl = new BountifulWrapper();

    public void playPling(Player player) {
        player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
    }

    public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
        if (type == ChatMessageType.CHAT) type = ChatMessageType.SYSTEM;
        player.spigot().sendMessage(type, msg);
    }

    private static final Class<?> dataWatcherRegistry = EntityDataSerializers.class;
    private static final Class<?> dataWatcherSerializer = EntityDataSerializer.class;

    public Object getDataWatcherObject(int index, Class<?> type) {
        return new EntityDataAccessor<>(index, (EntityDataSerializer<Object>) Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null));
    }

    public Object getDataWatcherItem(Object dwo, Object value) {
        return new SynchedEntityData.DataItem<>((EntityDataAccessor<Object>) dwo, value);
    }

    public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
        try {
            Reflection.Field<PositionMoveRotation> field = Reflection.getField(packetClass, PositionMoveRotation.class, 0);

            return (packet, x, y, z, pitch, yaw) -> {
                field.set(packet, new PositionMoveRotation(new Vec3(x, y, z), field.get(packet).deltaMovement(), yaw, pitch));
            };
        } catch (IllegalArgumentException e) {
            Reflection.Field<Double> posX = Reflection.getField(packetClass, double.class, fieldOffset);
            Reflection.Field<Double> posY = Reflection.getField(packetClass, double.class, fieldOffset + 1);
            Reflection.Field<Double> posZ = Reflection.getField(packetClass, double.class, fieldOffset + 2);
            boolean isByteClass = packetClass.getSimpleName().contains("PacketPlayOutEntityTeleport") || packetClass.getSimpleName().contains("PacketPlayOutNamedEntitySpawn");
            Class<?> pitchYawType = isByteClass ? byte.class : int.class;
            Reflection.Field<?> lookYaw = Reflection.getField(packetClass, pitchYawType, isByteClass ? 0 : 1);
            Reflection.Field<?> lookPitch = Reflection.getField(packetClass, pitchYawType, isByteClass ? 1 : 2);

            return (packet, x, y, z, pitch, yaw) -> {
                posX.set(packet, x);
                posY.set(packet, y);
                posZ.set(packet, z);
                if (isByteClass) {
                    lookYaw.set(packet, (byte) (yaw * 256 / 360));
                    lookPitch.set(packet, (byte) (pitch * 256 / 360));
                } else {
                    lookYaw.set(packet, (int) (yaw * 256 / 360));
                    lookPitch.set(packet, (int) (pitch * 256 / 360));
                }
            };
        }
    }

    public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
        Reflection.Field<UUID> uuidField = Reflection.getField(packetClass, UUID.class, 0);

        return uuidField::set;
    }

    public interface PositionSetter {
        void set(Object packet, double x, double y, double z, float pitch, float yaw);
    }

    public interface UUIDSetter {
        void set(Object packet, UUID uuid);
    }
}
