package org.kne.cloud.network.minecraft;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Base64;

import javax.imageio.ImageIO;

import org.kne.cloud.network.MultiProtocolSocketAddress;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class MinecraftServerPinger implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private transient Socket socket;
	private transient MinecraftProtocolContext mpc=new MinecraftProtocolContext();
	private MultiProtocolSocketAddress targetaddr;
	
	

	private long latency=-1;
	private String json;
	//private BufferedImage icon;
	private int max;
	private int online;
	private String description;
	private boolean isOldDescription;
	private String version;
	private int protocol;
	private String klalbversion;
	private Inet6Address klalbvaddr;
	private int klalbvport;
	private String icon;
	private transient BufferedImage image;
	
	public MinecraftServerPinger(MultiProtocolSocketAddress target) throws UnknownHostException, IOException {
		this(target,target.connectSocket());
	}
	
	public MinecraftServerPinger(MultiProtocolSocketAddress target, Socket Asocket)  throws UnknownHostException, IOException{
		targetaddr=target;
		socket=Asocket;
		socket.setTcpNoDelay(true);
		ping();
		analyseJson();
	}

	private void ping() throws IOException {
		MinecraftInputStream in=null;
		MinecraftOutputStream out=null;
		try {
			in=new MinecraftInputStream(socket.getInputStream(), mpc);
			out=new MinecraftOutputStream(socket.getOutputStream(), mpc);
			if(targetaddr instanceof MinecraftServerAddress) {
				out.writePacket(new MinecraftHandShakePacket(763, ((MinecraftServerAddress) targetaddr).getOriginAddr(), ((MinecraftServerAddress) targetaddr).getOriginPort(), 1));
			}else {
				out.writePacket(new MinecraftHandShakePacket(763, targetaddr.getHost(), targetaddr.getPort(), 1));
			}
			out.writePacket(new MinecraftMotdRequest());
			out.flush();
			MinecraftMotdResponse resp=(MinecraftMotdResponse) in.readPacket();
			if(resp==null)
				throw new EOFException();
			json=resp.getJson();
			long pst=System.nanoTime();
			out.writePacket(new MinecraftPingPacket(pst));
			out.flush();
			try {
			MinecraftPongPacket repo=(MinecraftPongPacket) in.readPacket();
			latency=System.nanoTime()-repo.getTime();
			}catch(EOFException e) {
				
			}
			
		}finally {
			try {
			if(in!=null)
				in.close();
			}catch(IOException e) {}

			try {
			if(out!=null)
				out.close();

			}catch(IOException e) {}
			try {
			if(socket!=null)
				socket.close();
			}catch(IOException e) {}
		}
	}
	private void analyseJson() {
	//	System.out.println(json);
	JsonObject jobj= (JsonObject) new JsonParser().parse(json);
	
	JsonObject versiont=(JsonObject) jobj.get("version");
	if(versiont!=null) {
	version=versiont.get("name").getAsString();
	protocol=versiont.get("protocol").getAsInt();
	}
	
	JsonObject players=(JsonObject) jobj.get("players");
	if(players!=null) {
	max=players.get("max").getAsInt();
	online=players.get("online").getAsInt();
	}
	
	JsonElement descriptionj =jobj.get("description");
	if(descriptionj!=null) {
		isOldDescription=!(descriptionj instanceof JsonObject);
		if(isOldDescription) {
			description=descriptionj.getAsString();
		}else {
	description=descriptionj.toString();
		}
	}
	
	JsonElement klalb= jobj.get("KLALB");
	if(klalb!=null&&(klalb instanceof JsonObject)) {
		JsonObject jobk=(JsonObject) klalb;
		klalbversion=jobk.get("version").getAsString();
		try {
			klalbvaddr=(Inet6Address) InetAddress.getByName( jobk.get("vaddr").getAsString());
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		klalbvport=jobk.get("vport").getAsInt();
	}
	
	JsonElement favicon= jobj.get("favicon");
	if(favicon!=null) {
		String s=favicon.getAsString();
		if(s.startsWith("data:image/png;base64,")){
			icon=s.replace("\n", "");
			
		}
	}
		/*try {
			JsonReader jr = new JsonReader(new StringReader(json));
			jr.setLenient(true);
			jr.beginObject();
			while (jr.hasNext()) {
				String n0 = jr.nextName();
				switch (n0) {
				case "version":
					jr.beginObject();
					while (jr.hasNext()) {
						String n1 = jr.nextName();
						switch (n1) {
						case "name":
							version=jr.nextString();
							break;

						default:
							jr.skipValue();
							break;
						}
					}
					jr.endObject();
					break;
				case "players":
					jr.beginObject();
					while (jr.hasNext()) {
						String n1 = jr.nextName();
						switch (n1) {
						case "max":
							max=jr.nextInt();
							break;
						case "online":
							online=jr.nextInt();
							break;
						default:
							jr.skipValue();
							break;
						}
					}
					jr.endObject();
					break;
				case "description":
					JsonToken j=jr.peek();
				
					if(j.equals(JsonToken.BEGIN_OBJECT)){
					jr.beginObject();
					while (jr.hasNext()) {
						String n1 = jr.nextName();
						switch (n1) {
						
						case "text":
							description=jr.nextString();
							break;
						default:
							jr.skipValue();
							break;
						}
					}
					jr.endObject();
					}else{
					description=jr.nextString();
					}
					break;
				case "favicon":
					String s=jr.nextString();
					if(s.startsWith("data:image/png;base64,")){
						s=s.replace("\n", "");
						icon=ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(s.substring("data:image/png;base64,".length()))));
					}
					break;
				case "KLALB":
					klalbversion=jr.nextString();
					break;
				default:
					jr.skipValue();
					break;
				}
			}
			jr.endObject();
			jr.close();
			//System.out.println(time);
		} catch (IOException e) {
			e.printStackTrace();
		}*/
	}
	
	@Override
	public String toString() {
		return "MinecraftServerPinger [targetaddr=" + targetaddr + ", latency=" + latency + ", json=" + json + ", icon="
				+ icon + ", max=" + max + ", online=" + online + ", description=" + description + ", isOldDescription="
				+ isOldDescription + ", version=" + version + ", protocol=" + protocol + ", klalbversion="
				+ klalbversion + ", klalbvaddr=" + klalbvaddr+ ", klalbvport=" + klalbvport+ "]";
	}

	public String getKlalbversion() {
		return klalbversion;
	}
	
	public Inet6Address getKlalbvaddr() {
		return klalbvaddr;
	}

	public int getKlalbvport() {
		return klalbvport;
	}

	public BufferedImage getIcon() {
		if(icon==null)
			return null;
		if(image==null)
		try {
			image= ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(icon.substring("data:image/png;base64,".length()))));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return image;
	}

	public int getMax() {
		return max;
	}

	public int getOnline() {
		return online;
	}

	public String getDescription() {
		return description;
	}

	public String getVersion() {
		return version;
	}

	public int getProtocol() {
		return protocol;
	}


	public boolean isOldDescription() {
		return isOldDescription;
	}
	
	public String getJson() {
		return json;
	}

	public MultiProtocolSocketAddress getTargetaddr() {
		return targetaddr;
	}

	public long getLatency() {
		return latency;
	}

	public static void main(String[] args) throws UnknownHostException, IOException {//hypixel.net
		MinecraftServerPinger sp=new MinecraftServerPinger(MinecraftServerAddress.findAddress("mc.hypixel.cn"));
		System.out.println(sp);
	}


}
