package protocolsupport.zplatform.impl.glowstone.network;

import java.net.InetSocketAddress;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import org.bukkit.entity.Player;

import com.flowpowered.network.Message;
import com.flowpowered.network.protocol.AbstractProtocol;

import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.glowstone.entity.meta.profile.PlayerProperty;
import net.glowstone.net.GlowSession;
import net.glowstone.net.ProxyData;
import net.glowstone.net.pipeline.MessageHandler;
import net.glowstone.net.protocol.ProtocolType;
import protocolsupport.api.events.PlayerPropertiesResolveEvent.ProfileProperty;
import protocolsupport.zplatform.impl.glowstone.GlowStoneMiscUtils;
import protocolsupport.zplatform.network.NetworkManagerWrapper;
import protocolsupport.zplatform.network.NetworkState;

public class GlowStoneNetworkManagerWrapper extends NetworkManagerWrapper {

	private static final AttributeKey<Object> packet_listener_key = AttributeKey.valueOf("ps_packet_listener");
	private static final UUID fakeUUID = UUID.randomUUID();

	public static Object getPacketListener(GlowSession session) {
		return session.getChannel().attr(packet_listener_key).get();
	}

	public static GlowStoneNetworkManagerWrapper getFromChannel(Channel channel) {
		return new GlowStoneNetworkManagerWrapper((MessageHandler) channel.pipeline().get(GlowStoneChannelHandlers.NETWORK_MANAGER));
	}

	private final MessageHandler handler;
	public GlowStoneNetworkManagerWrapper(MessageHandler handler) {
		this.handler = handler;
	}

	public GlowSession getSession() {
		return handler.getSession().get();
	}

	@Override
	public Object unwrap() {
		return handler;
	}

	@Override
	public InetSocketAddress getAddress() {
		return getSession().getAddress();
	}

	@Override
	public void setAddress(InetSocketAddress address) {
		ProxyData old = getSession().getProxyData();
		if (old != null) {
			getSession().setProxyData(new ProxyData(null, null, address, null, old.getProfile().getUniqueId(), old.getProfile().getProperties()));
		} else {
			getSession().setProxyData(new ProxyData(null, null, address, null, fakeUUID, Collections.emptyList()));
		}
	}

	@Override
	public boolean isConnected() {
		return getChannel().isOpen();
	}

	@Override
	public Channel getChannel() {
		return getSession().getChannel();
	}

	@Override
	public void close(String closeMessage) {
		getSession().disconnect(closeMessage);
	}

	@Override
	public void sendPacket(Object packet) {
		getSession().send((Message) packet);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void sendPacket(Object packet, GenericFutureListener<? extends Future<? super Void>> genericListener, GenericFutureListener<? extends Future<? super Void>>... futureListeners) {
		getSession().sendWithFuture((Message) packet).addListener(genericListener).addListeners(futureListeners);
	}

	public NetworkState getProtocol() {
		AbstractProtocol proto = getSession().getProtocol();
		for (ProtocolType type : ProtocolType.values()) {
			if (type.getProtocol() == proto) {
				return GlowStoneMiscUtils.protocolToNetState(type);
			}
		}
		throw new IllegalStateException(MessageFormat.format("Unkown protocol {0}", proto));
	}

	@Override
	public void setProtocol(NetworkState state) {
		getSession().setProtocol(GlowStoneMiscUtils.netStateToProtocol(state));
	}

	@Override
	public Object getPacketListener() {
		return getPacketListener(getSession());
	}

	@Override
	public void setPacketListener(Object listener) {
		getChannel().attr(packet_listener_key).set(listener);
	}

	@Override
	public UUID getSpoofedUUID() {
		return getSession().getProxyData().getProfile().getUniqueId();
	}

	@Override
	public ProfileProperty[] getSpoofedProperties() {
		return getSession().getProxyData().getProfile().getProperties()
		.stream()
		.map(property -> new ProfileProperty(property.getName(), property.getValue(), property.getSignature()))
		.collect(Collectors.toList())
		.toArray(new ProfileProperty[0]);
	}

	@Override
	public void setSpoofedProfile(UUID uuid, ProfileProperty[] properties) {
		ProxyData old = getSession().getProxyData();
		List<PlayerProperty> glowproperties = Collections.emptyList();
		if (properties != null) {
			glowproperties = Arrays.stream(properties)
			.map(prop -> new PlayerProperty(prop.getName(), prop.getValue(), prop.getSignature()))
			.collect(Collectors.toList());
		}
		if (old != null) {
			getSession().setProxyData(new ProxyData(null, null, old.getAddress(), null, uuid, glowproperties));
		} else {
			getSession().setProxyData(new ProxyData(null, null, getAddress(), null, uuid, glowproperties));
		}
	}

	@Override
	public Player getBukkitPlayer() {
		return getSession().getPlayer();
	}

}

@@ -549,24 +549,80 @@
 reListener;%0A
+import net.glowstone.entity.meta.profile.PlayerProfile;%0A
 import net.g
@@ -3882,55 +3882,87 @@
 %7B%0A%09%09
-return getSession().getProxyData().getP
+PlayerProfile profile = getSpoofedProfile();%0A%09%09return profile != null ? p
 rofile
-()
 .get
@@ -3971,16 +3971,23 @@
 iqueId()
+ : null
 ;%0A%09%7D%0A%0A%09@
@@ -4052,55 +4052,96 @@
 %7B%0A%09%09
-return getSession().getProxyData().getP
+PlayerProfile profile = getSpoofedProfile();%0A%09%09return profile == null ? null :%0A%09%09p
 rofile
-()
 .get
@@ -4152,19 +4152,16 @@
 erties()
-%0A%09%09
 .stream(
@@ -4336,24 +4336,214 @@
 ty%5B0%5D);%0A%09%7D%0A%0A
+%09private PlayerProfile getSpoofedProfile() %7B%0A%09%09ProxyData proxydata = getSession().getProxyData();%0A%09%09return proxydata != null ? proxydata.getProfile(%22?%5B%5D___PSFakeProfile!!!!!!!%22) : null;%0A%09%7D%0A%0A
 %09@Override%0A%09

Fix glowstone network manager get spoofed profile