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

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import de.steamwar.Reflection;
import de.steamwar.core.*;
import de.steamwar.network.CoreNetworkHandler;
import de.steamwar.network.NetworkSender;
import de.steamwar.network.packets.common.PlayerSkinRequestPacket;
import lombok.Getter;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;

public class RPlayer extends REntity {

    private static int skinPartsIndex() {
        switch(Core.getVersion()) {
            case 8:
                return 10;
            case 9:
                return 12;
            case 10:
            case 12:
                return 13;
            case 14:
                return 15;
            case 15:
                return 16;
            case 18:
            case 19:
            default:
                return 17;
        }
    }

    private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(skinPartsIndex(), Byte.class);

    @Getter
    private final UUID actualUUID;
    @Getter
    private final String name;

    public RPlayer(REntityServer server, UUID uuid, String name, Location location) {
        super(server, EntityType.PLAYER, UUID.nameUUIDFromBytes(uuid.toString().getBytes(StandardCharsets.UTF_8)), location,0);
        this.actualUUID = uuid;
        this.name = name;
        server.addEntity(this);
    }

    private GameProfile getGameProfile() {
        Property skinData = CoreNetworkHandler.SKIN_DATA_PROMISES.computeIfAbsent(actualUUID, __ -> {
            NetworkSender.sendOrQueue(new PlayerSkinRequestPacket(actualUUID));
            return new Property("textures", null, null);
        });
        if (TrickyTrialsWrapper.impl.getValue(skinData) != null) {
            GameProfile gameProfile = new GameProfile(uuid, name);
            gameProfile.getProperties().put("textures", skinData);
            return gameProfile;
        } else {
            return new GameProfile(uuid, name);
        }
    }

    private GameProfile saved;

    @Override
    void list(Consumer<Object> packetSink) {
        saved = getGameProfile();
        packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, saved, GameMode.CREATIVE));
    }

    @Override
    void spawn(Consumer<Object> packetSink) {
        packetSink.accept(getNamedSpawnPacket());
        packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F));

        for (Map.Entry<Object, ItemStack> entry : itemSlots.entrySet()) {
            packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue()));
        }

        postSpawn(packetSink);
    }

    @Override
    void delist(Consumer<Object> packetSink) {
        if (saved == null) saved = getGameProfile();
        packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, saved, GameMode.CREATIVE));
    }

    private static Class<?> namedSpawnPacket = null;
    private static Function<REntity, Object> namedSpawnPacketGenerator = null;
    private static Reflection.Field<UUID> namedSpawnUUID = null;

    static {
        try {
            namedSpawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
            namedSpawnPacketGenerator = spawnPacketGenerator(namedSpawnPacket, Core.getVersion() == 8 ? 1 : 0);
            namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0);
        } catch (IllegalArgumentException e) { }
    }

    private Object getNamedSpawnPacket() {
        if (Core.getVersion() >= 21) return PacketConstructor.impl.createRPlayerSpawn(this);

        Object packet = namedSpawnPacketGenerator.apply(this);
        namedSpawnUUID.set(packet, uuid);
        FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet);
        return packet;
    }
}
