package org.henrya.pingapi.v1_9_R1;

import com.mojang.authlib.GameProfile;

import io.netty.channel.ChannelHandlerContext;
import net.minecraft.server.v1_9_R1.ChatComponentText;
import net.minecraft.server.v1_9_R1.PacketStatusOutServerInfo;
import net.minecraft.server.v1_9_R1.ServerPing;
import net.minecraft.server.v1_9_R1.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_9_R1.ServerPing.ServerData;
import net.minecraft.server.v1_9_R1.ServerPing.ServerPingPlayerSample;
import org.bukkit.craftbukkit.v1_9_R1.util.CraftIconCache;
import org.henrya.pingapi.PingReply;
import org.henrya.pingapi.ServerInfoPacket;
import org.henrya.pingapi.reflect.ReflectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * A class that handles the sending of PacketStatusOutServerInfo packets
 * @author Henry Anderson
 */
public class ServerInfoPacketHandler extends ServerInfoPacket {
	private static final Field SERVER_PING_FIELD = ReflectUtils.getFirstFieldByType(PacketStatusOutServerInfo.class, ServerPing.class);

	/**
	 * Constructs a new ServerInfoPacketHandler from a PingReply instance
	 * @param reply The PingReply instance
	 */
	public ServerInfoPacketHandler(PingReply reply) {
		super(reply);
	}
	
	/**
	 * Sends the PacketStatusOutServerInfo to the client
	 */
	@Override
	public void send() {
		try {
			Field field = this.getReply().getClass().getDeclaredField("ctx");
			field.setAccessible(true);
			Object ctx = field.get(this.getReply());
			Method writeAndFlush = ctx.getClass().getMethod("writeAndFlush", Object.class);
			writeAndFlush.setAccessible(true);
			writeAndFlush.invoke(ctx, ServerInfoPacketHandler.constructPacket(this.getReply()));
		} catch(NoSuchFieldException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Creates a new PacketStatusOutServerInfo packet from the data found in a PingReply instance
	 * @param reply The PingReply instance
	 * @return A PacketStatusOutServerInfo packet
	 */
	public static PacketStatusOutServerInfo constructPacket(PingReply reply) {
		GameProfile[] sample = new GameProfile[reply.getPlayerSample().size()];
		List<String> list = reply.getPlayerSample();
		for(int i = 0; i < list.size(); i++) {
			sample[i] = new GameProfile(UUID.randomUUID(), list.get(i));
		}
		ServerPingPlayerSample playerSample = new ServerPingPlayerSample(reply.getMaxPlayers(), reply.getOnlinePlayers());
		playerSample.a(sample);
		ServerPing ping = new ServerPing();
		ping.setMOTD(new ChatComponentText(reply.getMOTD()));
		ping.setPlayerSample(playerSample);
		ping.setServerInfo(new ServerData(reply.getProtocolName(), reply.getProtocolVersion()));
		ping.setFavicon(((CraftIconCache) reply.getIcon()).value);
		return new PacketStatusOutServerInfo(ping);
	}
	
	/**
	 * Creates a new PingReply instance from the data found in a PacketStatusOutServerInfo packet
	 * @param packet The PacketStatusOutServerInfo instance
	 * @param ctx The ChannelHandlerContext instance
	 * @return A PingReply instance
	 */
	public static PingReply constructReply(PacketStatusOutServerInfo packet, ChannelHandlerContext ctx) {
		try {
			ServerPing ping = (ServerPing) SERVER_PING_FIELD.get(packet);
			String motd = ChatSerializer.a(ping.a());
			int max = ping.b().a();
			int online = ping.b().b();
			int protocolVersion = ping.getServerData().getProtocolVersion();
			String protocolName = ping.getServerData().a();
			GameProfile[] profiles = ping.b().c();
			List<String> list = new ArrayList<String>();
			for(int i = 0; i < profiles.length; i++) {
				list.add(profiles[i].getName());
			}
			PingReply reply = new PingReply(ctx, motd, online, max, protocolVersion, protocolName, list);
			return reply;
		} catch(IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}
}