package org.kne.cloud.network;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.net.*;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.kne.cloud.network.ntp.NTPSocketType;

public class MultiProtocolSocketAddress implements Serializable{
	/**
	 * 
	 */
	private static final Map<String,SocketType> socketTypeRegister=new HashMap<>();

	static {
		socketTypeRegister.put("tcp", TCPSocketType.getInstance());
		socketTypeRegister.put("udp", UDPSocketType.getInstance());
		socketTypeRegister.put("ntp", NTPSocketType.getInstance());
	}
	

	
	public static Map<String, SocketType> getSocketTypeRegister() {
		return socketTypeRegister;
	}
	private static final long serialVersionUID = 1L;
	private String protocol;
	private String host;
	private ProtocolStack ps=new ProtocolStack();
	
	
	private int port;
	
	public MultiProtocolSocketAddress(String hostport)  {
		this(hostport,"tcp");
	}
	public MultiProtocolSocketAddress(String hostport, String defaultprotocol) {
		try {
			if (!hostport.contains("://")) {
				hostport = defaultprotocol.toLowerCase() + "://" + hostport;
			}
			URI uri = new URI(hostport);

			this.protocol = uri.getScheme().toLowerCase();
			this.host = uri.getHost();
			if (this.host == null) {
				throw new IllegalArgumentException("无效的主机地址: " + hostport);
			}
			this.port = resolvePort(uri, this.protocol);

			// 可选：处理查询参数
			String query = uri.getQuery();
			if (query != null && !query.isEmpty()) {
				// 解析查询参数作为扩展信息
			}
		} catch (URISyntaxException e) {
			throw new IllegalArgumentException("无效的地址格式: " + hostport, e);
		}
	}

	private int resolvePort(URI uri, String protocol) {
		int port = uri.getPort();
		if (port != -1) {
			return port;
		}
		SocketType socketType = socketTypeRegister.get(protocol);
		if (socketType != null) {
			int defaultPort = socketType.getDefaultPort();
			if (defaultPort != -1) {
				return defaultPort;
			}
		}
		throw new IllegalArgumentException(
				"未指定端口号，且协议 " + protocol + " 没有默认端口: " + uri
		);
	}
	@Override
	public int hashCode() {
		return Objects.hash(host, port, ps, protocol);
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MultiProtocolSocketAddress other = (MultiProtocolSocketAddress) obj;
		return Objects.equals(host, other.host) && port == other.port && Objects.equals(ps, other.ps)
				&& Objects.equals(protocol, other.protocol);
	}
	
	public boolean equals2(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MultiProtocolSocketAddress other = (MultiProtocolSocketAddress) obj;
		InetAddress addra=null;
		try {
			addra = InetAddress.getByName(host);
		} catch (UnknownHostException e) {
		}
		InetAddress addrb=null;
		try {
			addrb = InetAddress.getByName(other.host);
		} catch (UnknownHostException e) {
		}
		boolean hostequals=Objects.equals(addra, addrb);
		
		return hostequals && port == other.port && Objects.equals(ps, other.ps)
				&& Objects.equals(protocol, other.protocol);
	}
	
	public MultiProtocolSocketAddress(String host2, int port2) {
		this("tcp", host2, port2);
	}
	
	public MultiProtocolSocketAddress(String protocol, String host2, int port2) {
		host=host2;
		port=port2;
		this.protocol = protocol;
	}
	public MultiProtocolSocketAddress(InetSocketAddress remoteSocketAddress) {
		this("tcp",remoteSocketAddress);
	}
	public MultiProtocolSocketAddress(String protocol, InetSocketAddress remoteSocketAddress) {
		host=remoteSocketAddress.getAddress().getHostAddress();
		port=remoteSocketAddress.getPort();
		this.protocol = protocol;
	}
	
	
	public String getHost() {
		return host;
	}
	public int getPort() {
		return port;
	}
	public String getProtocol() {
		return protocol;
	}
	
	public InetSocketAddress getSocketAddress() {
		return new InetSocketAddress(host, port);
	}

	@Override
	public String toString() {
		try {
			// 获取协议的默认端口
			SocketType socketType = socketTypeRegister.get(protocol);
			int defaultPort = (socketType != null) ? socketType.getDefaultPort() : -1;

			// 如果当前端口等于默认端口，则隐去端口号
			int portToUse = (defaultPort != -1 && this.port == defaultPort) ? -1 : this.port;

			URI uri = new URI(protocol, null, host, portToUse, null, null, null);
			return uri.toString();
		} catch (URISyntaxException e) {
			return protocol + "://" + host + ":" + port;
		}
	}
	
	
	
	public Socket connectSocket(InetAddress bindip,int bindport,int timeout) throws UnknownHostException, IOException {
		SocketFactory sf=socketTypeRegister.get(protocol).getSocketFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		Socket s=sf.createSocket();
		try {
		s.bind(new InetSocketAddress(bindip, bindport));
		s.connect(new InetSocketAddress(host, port),timeout);
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	public Socket connectSocket(InetAddress bindip,int bindport) throws UnknownHostException, IOException {
		SocketFactory sf=socketTypeRegister.get(protocol).getSocketFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		Socket s=sf.createSocket();
		try {
		s.bind(new InetSocketAddress(bindip, bindport));
		s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	public Socket connectSocket() throws UnknownHostException, IOException {
		SocketFactory sf=socketTypeRegister.get(protocol).getSocketFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		Socket s=sf.createSocket();
		try {
		s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	public Socket connectSocket(int timeout) throws UnknownHostException, IOException {
		SocketFactory sf=socketTypeRegister.get(protocol).getSocketFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		Socket s=sf.createSocket();
		try {
		s.connect(new InetSocketAddress(host, port),timeout);
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	
	
	
	private static TimeoutTimer tmoTimer=new TimeoutTimer();
	
	
	
	public SocketChannel connectSocketChannel(InetAddress bindip,int bindport,int timeout) throws UnknownHostException, IOException {
		SocketChannelFactory sf=socketTypeRegister.get(protocol).getSocketChannelFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		SocketChannel s=sf.createSocketChannel();
		TimerTask tt=null;
		try {
		s.bind(new InetSocketAddress(bindip, bindport));
		 tt=tmoTimer.createTimeOutTask(s, timeout);
		
		s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}finally {
			if(tt!=null)
			tt.cancel();
		}
		return s;
	}
	public SocketChannel connectSocketChannel(InetAddress bindip,int bindport) throws UnknownHostException, IOException {
		SocketChannelFactory sf=socketTypeRegister.get(protocol).getSocketChannelFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		SocketChannel s=sf.createSocketChannel();
		try {
		s.bind(new InetSocketAddress(bindip, bindport));
		s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	public SocketChannel connectSocketChannel() throws UnknownHostException, IOException {
		SocketChannelFactory sf=socketTypeRegister.get(protocol).getSocketChannelFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		SocketChannel s=sf.createSocketChannel();
		try {
		s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}
		return s;
	}
	public SocketChannel connectSocketChannel(int timeout) throws UnknownHostException, IOException {
		SocketChannelFactory sf=socketTypeRegister.get(protocol).getSocketChannelFactory();
		if(sf==null) {
			throw new UnsupportedOperationException("Socket Unsupported");
		}
		SocketChannel s=sf.createSocketChannel();

		TimerTask tt=tmoTimer.createTimeOutTask(s, timeout);
		try {
			s.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			s.close();
			throw e;
		}finally {
			if(tt!=null)
			tt.cancel();
		}
		return s;
	}
	
	
	
	
	
	
	public ServerSocket listenServerSocket() throws UnknownHostException, IOException {
		ServerSocketFactory srf=socketTypeRegister.get(protocol).getServerSocketFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("ServerSocket Unsupported");
		}
		ServerSocket sk=srf.createServerSocket(port, 50, InetAddress.getByName(host));
		return sk;
	}
	public ServerSocket listenServerSocket(int backlog) throws UnknownHostException, IOException {
		ServerSocketFactory srf=socketTypeRegister.get(protocol).getServerSocketFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("ServerSocket Unsupported");
		}
		ServerSocket sk=srf.createServerSocket(port, backlog, InetAddress.getByName(host));
		return sk;
	}
	
	
	public ServerSocketChannel listenServerSocketChannel() throws UnknownHostException, IOException {
		ServerSocketChannelFactory srf=socketTypeRegister.get(protocol).getServerSocketChannelFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("ServerSocket Unsupported");
		}
		ServerSocketChannel sk=srf.createServerSocketChannel(port, 50, InetAddress.getByName(host));
		return sk;
	}
	public ServerSocketChannel listenServerSocketChannel(int backlog) throws UnknownHostException, IOException {
		ServerSocketChannelFactory srf=socketTypeRegister.get(protocol).getServerSocketChannelFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("ServerSocket Unsupported");
		}
		ServerSocketChannel sk=srf.createServerSocketChannel(port, backlog, InetAddress.getByName(host));
		return sk;
	}
	
	
	public boolean isStream() {
		return checkIsStream(protocol);
	}
	public static boolean checkIsStream(String type2) {
		return socketTypeRegister.get(type2).isStream();
	}
	public DatagramSocket connectDatagramSocket(InetAddress bindip,int bindport) throws IOException {
		DatagramSocketFactory dgs=socketTypeRegister.get(protocol).getDatagramSocketFactory();
		if(dgs==null) {
			throw new UnsupportedOperationException("DatagramSocket Unsupported");
		}
		DatagramSocket dgd=dgs.createSocket();
		try {
		dgd.bind(new InetSocketAddress(bindip, bindport));
		dgd.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			dgd.close();
			throw e;
		}
		return dgd;
	}
	public DatagramSocket connectDatagramSocket() throws IOException {
		DatagramSocketFactory dgs=socketTypeRegister.get(protocol).getDatagramSocketFactory();
		if(dgs==null) {
			throw new UnsupportedOperationException("DatagramSocket Unsupported");
		}
		DatagramSocket dgd=dgs.createSocket();
		try {
		dgd.connect(new InetSocketAddress(host, port));
		}catch(Exception e) {
			dgd.close();
			throw e;
		}
		return dgd;
	}
	public DatagramSocket listenDatagramSocket() throws UnknownHostException, IOException {
		DatagramSocketFactory dgs=socketTypeRegister.get(protocol).getDatagramSocketFactory();
		if(dgs==null) {
			throw new UnsupportedOperationException("DatagramSocket Unsupported");
		}
		DatagramSocket dgd=dgs.createSocket();
		try {
		dgd.bind(new InetSocketAddress(host, port));
		}catch(Exception e) {
			dgd.close();
			throw e;
		}
		return dgd;
	}
	
	public DatagramServerSocket listenDatagramServerSocket() throws UnknownHostException, IOException {
		DatagramServerSocketFactory srf=socketTypeRegister.get(protocol).getDatagramServerSocketFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("DatagramServerSocket Unsupported");
		}
		DatagramServerSocket sk=srf.createDatagramServerSocket(port, 50, InetAddress.getByName(host));
		return sk;
	}
	public DatagramServerSocket listenDatagramServerSocket(int backlog) throws UnknownHostException, IOException {
		DatagramServerSocketFactory srf=socketTypeRegister.get(protocol).getDatagramServerSocketFactory();
		if(srf==null) {
			throw new UnsupportedOperationException("DatagramServerSocket Unsupported");
		}
		DatagramServerSocket sk=srf.createDatagramServerSocket(port, backlog, InetAddress.getByName(host));
		return sk;
	}
	public InetAddress getInetAddress() throws UnknownHostException {
		return InetAddress.getByName(host);
	}
	public InetAddress[] getAllInetAddress() throws UnknownHostException {
		return InetAddress.getAllByName(host);
	}
	public boolean supportNIO() {
		SocketType stp= socketTypeRegister.get(getProtocol());
		if(stp==null)
		return false;
		if(stp.getSocketChannelFactory()==null) {
			return false;
		}else {
			return true;
		}
	}
	
	public static JsonDeserializer<MultiProtocolSocketAddress>getDefaultJsonDeserializer(){
		return new JsonDeserializer<MultiProtocolSocketAddress>() {
			
			@Override
			public MultiProtocolSocketAddress deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
					throws JsonParseException {
				if(arg0.isJsonPrimitive()) {
					JsonPrimitive jp=(JsonPrimitive) arg0;
					if(jp.isString()) {
						MultiProtocolSocketAddress msa=new MultiProtocolSocketAddress(jp.getAsString());
			//			System.out.println(msa);
						return msa;
					}
				}
				throw new JsonParseException("not a string:"+arg0);
			}
		};
	}
	public static JsonSerializer<MultiProtocolSocketAddress>getDefaultJsonSerializer(){
		return new JsonSerializer<MultiProtocolSocketAddress>() {
			
			@Override
			public JsonElement serialize(MultiProtocolSocketAddress arg0, Type arg1, JsonSerializationContext arg2) {
				return new JsonPrimitive(arg0.toString());
			}
		};
	}
	
	public static void registerToGsonBuilder(GsonBuilder gsonBuilder) {
		gsonBuilder.registerTypeAdapter(MultiProtocolSocketAddress.class, getDefaultJsonDeserializer());
		gsonBuilder.registerTypeAdapter(MultiProtocolSocketAddress.class, getDefaultJsonSerializer());
	}
	
}
