package com.minelittlepony.bigpony;

import java.util.function.Function;

import io.netty.buffer.Unpooled;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;

public interface Network {

    SPacketType<MsgPlayerSize> CLIENT_UPDATE_PLAYER_SIZE = clientToServer(new Identifier("minebp", "client_player_size"), MsgPlayerSize::new);

    MPacketType<MsgOtherPlayerSize> SERVER_OTHER_PLAYER_SIZE = serverToClients(new Identifier("minebp", "server_other_player_size"), MsgOtherPlayerSize::new);

    static void bootstrap() { }

    static <T extends Packet> SPacketType<T> clientToServer(Identifier id, Function<PacketByteBuf, T> factory) {
        ServerPlayNetworking.registerGlobalReceiver(id, (server, player, ignored, buffer, responder) -> {
            T packet = factory.apply(buffer);
            server.execute(() -> packet.handle(player));
        });
        return packet -> {
            if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) {
                throw new RuntimeException("Client packet send called by the server");
            }
            ClientPlayNetworking.send(id, toBuffer(packet));
        };
    }

    static <T extends Packet> CPacketType<T> serverToClient(Identifier id, Function<PacketByteBuf, T> factory) {
        if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
            ClientPlayNetworking.registerGlobalReceiver(id, (client, ignore1, buffer, ignore2) -> {
                T packet = factory.apply(buffer);
                client.execute(() -> packet.handle(client.player));
            });
        }
        return (recipient, packet) -> {
            ServerPlayNetworking.send((ServerPlayerEntity)recipient, id, toBuffer(packet));
        };
    }

    static <T extends Packet> MPacketType<T> serverToClients(Identifier id, Function<PacketByteBuf, T> factory) {
        if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
            ClientPlayNetworking.registerGlobalReceiver(id, (client, ignore1, buffer, ignore2) -> {
                T packet = factory.apply(buffer);
                client.execute(() -> packet.handle(client.player));
            });
        }
        return (world, packet) -> {
            world.getPlayers().forEach(player -> {
                if (player != null) {
                    ServerPlayNetworking.send((ServerPlayerEntity)player, id, toBuffer(packet));
                }
            });
        };
    }

    interface MPacketType<T extends Packet> {
        void send(World world, T packet);
    }

    interface CPacketType<T extends Packet> {
        void send(PlayerEntity recipient, T packet);
    }

    interface SPacketType<T extends Packet> {
        void send(T packet);
    }

    static PacketByteBuf toBuffer(Packet packet) {
        PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
        packet.toBuffer(buf);
        return buf;
    }

    interface Packet {
        void toBuffer(PacketByteBuf buffer);

        void handle(PlayerEntity sender);
    }
}

@@ -1113,17 +1113,16 @@
 er, 
-responder
+ignored2
 ) -%3E
@@ -1561,17 +1561,17 @@
 Packet%3E 
-C
+M
 PacketTy
@@ -1590,16 +1590,17 @@
 ToClient
+s
 (Identif
@@ -1749,809 +1749,33 @@
 entP
-layNetworking.registerGlobalReceiver(id, (client, ignore1, buffer, ignore2) -%3E %7B%0A                T packet = factory.apply(buffer);%0A                client.execute(() -%3E packet.handle(client.player));%0A            %7D);%0A        %7D%0A        return (recipient, packet) -%3E %7B%0A            ServerPlayNetworking.send((ServerPlayerEntity)recipient, id, toBuffer(packet));%0A        %7D;%0A    %7D%0A%0A    static %3CT extends Packet%3E MPacketType%3CT%3E serverToClients(Identifier id, Function%3CPacketByteBuf, T%3E factory) %7B%0A        if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) %7B%0A            ClientPlayNetworking.registerGlobalReceiver(id, (client, ignore1, buffer, ignore2) -%3E %7B%0A                T packet = factory.apply(buffer);%0A                client.execute(() -%3E packet.handle(client.player));%0A            %7D
+roxy.register(id, factory
 );%0A 
@@ -2156,114 +2156,8 @@
  %7D%0A%0A
-    interface CPacketType%3CT extends Packet%3E %7B%0A        void send(PlayerEntity recipient, T packet);%0A    %7D%0A%0A
     
@@ -2524,11 +2524,387 @@
 );%0A    %7D
+%0A%0A    class ClientProxy %7B%0A        static %3CT extends Packet%3E void register(Identifier id, Function%3CPacketByteBuf, T%3E factory) %7B%0A            ClientPlayNetworking.registerGlobalReceiver(id, (client, ignore1, buffer, ignore2) -%3E %7B%0A                T packet = factory.apply(buffer);%0A                client.execute(() -%3E packet.handle(client.player));%0A            %7D);%0A        %7D%0A    %7D
 %0A%7D%0A

Fix server crash