package org.kne.cloud.network.klalb;

import java.io.IOException;
import java.net.BindException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;

import org.kne.cloud.clock.HighAccuracyClock;
import org.kne.cloud.network.IPMulticastDiscovery;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.PortPair;
import org.kne.cloud.network.SocketType;
import org.kne.cloud.network.ThreadTool;
import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.ipv6.IPv6Packet;
import org.kne.cloud.network.ipv6.IPv6TUNLoopbackNetworkLink;
import org.kne.cloud.network.ipv6.Neighbor;
import org.kne.cloud.network.kltp.KLTPPacket;
import org.kne.cloud.network.kltp.KLTPProtocolRegister;
import org.kne.cloud.network.monitor.HashMapTimestampMonitor;
import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorDataImpl;
import org.kne.cloud.network.ntp.NTPContext;
import org.kne.cloud.network.ntp.NTPv4Packet;
import org.kne.cloud.network.ntp.NTPv4Protocol;
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
import org.kne.cloud.network.srv6.SRv6Router;
import org.kne.cloud.network.srv6.SRv6RouterListener;
import org.kne.cloud.network.tcp.UDPPacket;
import org.kne.cloud.network.tcp.UDPProtocolRegister;
import org.kne.cloud.network.te.BandwidthDistributer;

public class KLALBController {
    private UUID nodeuuid=UUID.randomUUID();
	private HighAccuracyClock clock = new HighAccuracyClock();

	public HighAccuracyClock getClock() {
		return clock;
	}
	private final long TIME_WINDOW=2000000000L;
	private SpeedAndTrafficMonitorDataImpl linkMonitor = new SpeedAndTrafficMonitorDataImpl(
			new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100,TIME_WINDOW ),
			new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW));

	private SpeedAndTrafficMonitorDataImpl datatMonitor = new SpeedAndTrafficMonitorDataImpl(
			new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100, TIME_WINDOW),
			new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW));

	private List<MultiProtocolSocketAddress> externalEndpoints = new ArrayList<>();

	private List<MultiProtocolSocketAddress> listensSocketAddress = new CopyOnWriteArrayList<>();

	public List<MultiProtocolSocketAddress> getListenSocketAddress() {
		return listensSocketAddress;
	}
	private static final boolean debug=false;
	private static final boolean showpacket = false;

	private static final int PREFIX = 128;// 112
	private static final int DISCOVERY_PORT = 4569;

	private static List<IPMulticastDiscovery> ipmd = new ArrayList<>();

	private NetworkInterfaceManager networkInterfaceManager=new NetworkInterfaceManager();
	public NetworkInterfaceManager getNetworkInterfaceManager() {
		return networkInterfaceManager;
	}
	public List<NetworkInterface> getNetworkInterfaceExcept() {
		return networkInterfaceManager.getNetworkInterfaceExcept();
	}

	private List<InetAddress> dnsAddresses = new ArrayList<>();

	public List<InetAddress> getDnsAddresses() {
		return dnsAddresses;
	}
	private ScheduledExecutorService scheduleTimer=Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(),new ThreadFactory() {
		
		@Override
		public Thread newThread(Runnable r) {
			return ThreadTool.makeVDaemonThreadIfSupport("计时器线程", r);
		}
	});

	public ScheduledExecutorService getScheduleTimer() {
		return scheduleTimer;
	}

	private Timer twk = new Timer("网卡检测扫描计时器", true);



	private TimerTask tsk1 = new TimerTask() {

		@Override
		public void run() {
			try {
				List<InetAddress> localaddress = networkInterfaceManager.getAllNetworkInterfaceAddress();
			
				for (InetAddress inetAddress : localaddress) {
						for (Iterator<MultiProtocolSocketAddress> iterator = listensSocketAddress.iterator(); iterator.hasNext();) {
							MultiProtocolSocketAddress tcpl = (MultiProtocolSocketAddress) iterator.next();

							try {
								if (tcpl.getInetAddress().isAnyLocalAddress()
										|| tcpl.getInetAddress().equals(inetAddress)) {
									MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(tcpl.getProtocol(),
											inetAddress.getHostAddress(), tcpl.getPort());
									// System.out.println(bind);
									synchronized (externalEndpoints) {
										if (!externalEndpoints.contains(bind)) {
											externalEndpoints.add(bind);
										}
									}
								}
							} catch (UnknownHostException e) {
								// TODO 自动生成的 catch 块
								e.printStackTrace();
							}

						}
				}
			
			lineslock.writeLock().lock();
			try {

				
				Set<MultiProtocolSocketAddress> st = new HashSet<>();
                for (IPv6NetworkLink link : srv6Router.getLinkTabel()) {
                    if (link instanceof KLALBRemoteLink) {
                        KLALBRemoteLink multipurposeSocketAddress = (KLALBRemoteLink) link;
                        if (multipurposeSocketAddress.getSocketAddress() != null)
                            st.add(multipurposeSocketAddress.getSocketAddress());
                    }
                }

                for (MultiProtocolSocketAddress target : st) {
                    addRemoteLines(target);
                }

				/*
                for (IPv6NetworkLink link : srv6Router.getLinkTabel()) {
                    if (link instanceof KLALBRemoteLink) {
                        KLALBRemoteLink reml = (KLALBRemoteLink) link;
                        if (reml.getSocketAddress() != null) {
                            if (reml.getBindAddress() != null || reml.getRemoteVaddr() != null)
                                try {
                                    MultipurposeSocketAddress iadr = reml.getBindAddress();
                                    if (iadr != null) {
                                        if (!localaddress.contains(iadr.getInetAddress())) {
                                            reml.close();
                                            srv6Router.getLinkTabel().remove(reml);
                                        }
                                    } else {

                                        reml.close();
                                    }
                                } catch (UnknownHostException e) {
                                    reml.close();
                                    srv6Router.getLinkTabel().remove(reml);
                                    e.printStackTrace();
                                }
                        }
                    }
                }
*/
			} finally {
				lineslock.writeLock().unlock();
			}
			

			updateBroadcastNetworkInterface();
			} catch (SocketException e) {
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		private void updateBroadcastNetworkInterface() throws SocketException {
			for (Iterator<IPMulticastDiscovery> iterator = ipmd.iterator(); iterator.hasNext(); ) {
				IPMulticastDiscovery ipMulticastDiscovery = (IPMulticastDiscovery) iterator.next();
				if (ipMulticastDiscovery.isClosed() || (!ipMulticastDiscovery.getInterface().isUp())
						|| (configItem != null && configItem.isDenyExternalEndpointBroadcast())) {
					iterator.remove();
					try {
						ipMulticastDiscovery.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					if (debug)
						System.out.println("关闭网卡地址广播：" + ipMulticastDiscovery.getInterface());
				}
			}

			if (configItem != null && configItem.isDenyExternalEndpointBroadcast()) {

			} else {
				List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
				for(NetworkInterface networkInterface:interfaceList){
					List<InetAddress> addressList=	networkInterfaceManager.getNetworkInterfaceAddress(networkInterface);
				loop:for (InetAddress bidr:addressList) {

					for (IPMulticastDiscovery ipMulticastDiscovery : ipmd) {
						if (networkInterface.equals(ipMulticastDiscovery.getInterface())
								&& bidr.equals(ipMulticastDiscovery.getBind().getAddress())) {
							continue loop;
						}
					}

					try {
						// InetAddress bidr=InetAddress.getByName("::0");

						if (bidr instanceof Inet6Address) {
							IPMulticastDiscovery ipd = new IPMulticastDiscovery(new InetSocketAddress(bidr,DISCOVERY_PORT),
									new InetSocketAddress(InetAddress.getByName("ff02::2486"),
											DISCOVERY_PORT),
									networkInterface, listensSocketAddress, nodeuuid, 10000L);
							ipd.setCon((mpa) -> {
								if (debug)
									System.out.println("添加本地IPv6链路：" + mpa);
								try {
									if (!checkIsSelf(mpa))
										addRemoteLines(mpa);
								} catch (UnknownHostException e) {
								}
							});
							ipd.start();
							ipmd.add(ipd);
						} else if (bidr instanceof Inet4Address) {
							// bidr=InetAddress.getByName("0.0.0.0");
							IPMulticastDiscovery ipd2 = new IPMulticastDiscovery(new InetSocketAddress(bidr,DISCOVERY_PORT),
									new InetSocketAddress(InetAddress.getByName("224.0.0.86"),
											DISCOVERY_PORT),
									networkInterface, listensSocketAddress, nodeuuid, 10000L);
							ipd2.setCon((mpa) -> {
								if (debug)
									System.out.println("添加本地IPv4链路：" + mpa);
								try {
									if (!checkIsSelf(mpa))
										addRemoteLines(mpa);
								} catch (UnknownHostException e) {
								}
							});
							ipd2.start();
							ipmd.add(ipd2);
						}
						if (debug)
							System.out.println("开启网卡地址广播：" + networkInterface);
					} catch (BindException e) {
						// e.printStackTrace();
					} catch (UnknownHostException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}


				// }

			}
		}


			}
		}

	};

	private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException {

		return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress()
				|| externalEndpoints.contains(inetAddress);
	}

	private boolean checkIsSelfLocator(InetAddress inetAddress) {
		return srv6Router.getLocator().getAddress().toInet6Address().equals(inetAddress);
	}

	private TimerTask tsk2 = new TimerTask() {

		@Override
		public void run() {

			List<MultiProtocolSocketAddress> nt = ntptable;
			// System.out.println("srv6 ip"+nb);
			if (nvc2 != null) {
				Set<NTPPeer> sp = nvc2.getPeers();
				for (MultiProtocolSocketAddress server : nt) {
					sp.add(new NTPPeer(server, NTPv4Packet.NTP_CLIENT));

				}
				sp.removeIf((p) -> {
					for (MultiProtocolSocketAddress server : nt) {
						if (server.equals(p.getAddress())) {
							return false;
						}
					}
					return true;
				});
			}
		}
	};

	public void tryConnectMore(){
		if (srv6Router != null && routingProtocol != null) {
			Set<IPv6AddressGroup> nb = srv6Router.getLocators();
			for (Iterator<IPv6AddressGroup> iterator = nb.iterator(); iterator.hasNext();) {
				IPv6AddressGroup neighbor = (IPv6AddressGroup) iterator.next();
				try {
					InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
							KLALBRoutingProtocol.DEFAULT_PORT);
					apiClient.requestNodeInfoFull(iaddr, (v) -> {
						ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
							for (MultiProtocolSocketAddress msa : v.getOpenLines()) {
								// System.out.print(msa);
								addRemoteLines(msa);
							}
						}).start();
					});
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}
	}


	public SpeedAndTrafficMonitorDataImpl getLinkMonitor() {
		return linkMonitor;
	}

	public SpeedAndTrafficMonitorDataImpl getDatatMonitor() {
		return datatMonitor;
	}

	private SocketType streamSocketType = new KLALBStreamSocketType();



	public class KLALBStreamSocketType extends SocketType {

		public KLALBStreamSocketType() {
			super(new KLALBVirtualSocketFactory(KLALBController.this),
					new KLALBVirtualServerSocketFactory(KLALBController.this),
					new KLALBVirtualSocketChannelFactory(KLALBController.this),
					new KLALBVirtualServerSocketChannelFactory(KLALBController.this));
		}

	}

	public SocketType getStreamSocketType() {
		return streamSocketType;
	}

	private SocketType datagramSocketType = new KLALBDatagramSocketType();

	public class KLALBDatagramSocketType extends SocketType {

		public KLALBDatagramSocketType() {
			super(new KLALBVirtualDatagramSocketFactory(KLALBController.this),
					new KLALBVirtualDatagramServerSocketFactory(KLALBController.this));
			// new KLALBVirtualDatagramSocketChannelFactory(KLALBController.this),
			// new KLALBVirtualDatagramServerSocketChannelFactory(KLALBController.this));
		}

	}

	public SocketType getDatagramSocketType() {
		return datagramSocketType;
	}


	public List<MultiProtocolSocketAddress> getExternalEndpoints() {
		return externalEndpoints;
	}


	public IPv6AddressGroup getSelf() {
		return srv6Router.getLocator();
	}

	private ReadWriteLock lineslock = new ReentrantReadWriteLock();

	public List<IPv6NetworkLink> getLines() {
		return srv6Router.getLinkTabel();
	}



	public void reconnectImmediately() {
		for (Iterator<IPv6NetworkLink> iterator = srv6Router.getLinkTabel().iterator(); iterator.hasNext();) {
			IPv6NetworkLink val = iterator.next();
			if (val instanceof KLALBRemoteLink) {

				KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) val;
				klalbRemoteLine.reconnectImmediately();
			}
		}
	}

	private NTPv4Protocol nvc2;

	private KLALBRoutingProtocolAPIServer apiServer;

	private KLALBRoutingProtocolAPIClient apiClient;

	private KLALBControllerConfigItem configItem;
	
	public void addRemoteLines(List<MultiProtocolSocketAddress> select) {
		for (MultiProtocolSocketAddress target : select) {
			addRemoteLines(target);
		}
	}

	public List<KLALBRemoteLink> addRemoteLines(MultiProtocolSocketAddress target) {
		lineslock.writeLock().lock();
		try {
			List<KLALBRemoteLink> added = new ArrayList<>();
			try {
				Enumeration<NetworkInterface> eu = NetworkInterface.getNetworkInterfaces();
				while (eu.hasMoreElements()) {
					NetworkInterface networkInterface = (NetworkInterface) eu.nextElement();
					if (networkInterface.isUp()) {
						// System.out.println(networkInterface+" "+networkInterface.isUp());
						Enumeration<InetAddress> ei = networkInterface.getInetAddresses();
						while (ei.hasMoreElements()) {
							InetAddress inetAddress = (InetAddress) ei.nextElement();
							try {
								MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(
										inetAddress.getHostAddress(), 0);
								try {
									if (target.getInetAddress().isLoopbackAddress()
											&& (!bind.getInetAddress().isLoopbackAddress())) {
										continue;
									}
									if ((!target.getInetAddress().isLoopbackAddress())
											&& bind.getInetAddress().isLoopbackAddress()) {
										continue;
									}
									if (target.getInetAddress() instanceof Inet4Address
											&& bind.getInetAddress() instanceof Inet6Address) {
										continue;
									}
									if (target.getInetAddress() instanceof Inet6Address
											&& bind.getInetAddress() instanceof Inet4Address) {
										continue;
									}
									// System.out.println(target+"->"+bind);
								} catch (UnknownHostException e) {
									if (e.getMessage().trim().toLowerCase().contains("no scope_id found")) {
										throw e;
									}
									// e.printStackTrace();
								}
								if (!checkContainsTargetAndBind(target, bind)) {
									int count=1;
									if(configItem!=null) {
										count=configItem.getLinkConnectionsCount();
									}
									if(count<=0) {
										count=1;
									}
									for(int i=0;i<count;i++) {
									KLALBRemoteLink line = new KLALBRemoteLink(this, target, bind);
									addRemoteLine(line);
									added.add(line);
									}
								}
							} catch (UnknownHostException e) {

							}
						}
					}
				}
			} catch (SocketException e) {
				if (!checkContainsTarget(target)) {
					KLALBRemoteLink line = new KLALBRemoteLink(this, target);
					addRemoteLine(line);
					added.add(line);
				}
				// throw e;
			}

			return added;
		} finally {
			lineslock.writeLock().unlock();
		}
	}

	public List<KLALBRemoteLink> removeRemoteLines(MultiProtocolSocketAddress mpsa) {

		List<KLALBRemoteLink> rmved = new ArrayList<>();
		for (Iterator<IPv6NetworkLink> iterator = srv6Router.getLinkTabel().iterator(); iterator.hasNext();) {
			IPv6NetworkLink link = iterator.next();
			if (link instanceof KLALBRemoteLink) {
				KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
				if (mpsa.equals(klalbRemoteLine.getSocketAddress())) {
					klalbRemoteLine.close();
					rmved.add(klalbRemoteLine);
				}
			}
		}
		return rmved;
	}

	public Inet6Address getRemoteVaddrBySocketAddress(MultiProtocolSocketAddress target) throws SocketTimeoutException {
		KLALBRemoteLink kr = null;
		for (Iterator<IPv6NetworkLink> iterator = srv6Router.getLinkTabel().iterator(); iterator.hasNext();) {
			IPv6NetworkLink link = iterator.next();
			if (link instanceof KLALBRemoteLink) {
				KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
				if (target.equals(klalbRemoteLine.getSocketAddress()) && klalbRemoteLine.getBindAddress() == null) {
					kr = klalbRemoteLine;
					break;
				}
			}
		}

		if (kr == null) {
			kr = new KLALBRemoteLink(this, target);
			addRemoteLine(kr);
			kr.waitForRemoteVaddrAvaliable(20000);
		} else {
			kr.reconnectImmediately();
			kr.waitForRemoteVaddrAvaliable(20000);
		}
		return kr.getRemoteVaddr().getAddress().toInet6Address();
	}

	private boolean checkContainsTargetAndBind(MultiProtocolSocketAddress target, MultiProtocolSocketAddress bind) {
		boolean b = false;
		for (Iterator<IPv6NetworkLink> iterator = srv6Router.getLinkTabel().iterator(); iterator.hasNext();) {
			IPv6NetworkLink link = iterator.next();
			if (link instanceof KLALBRemoteLink) {
				KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
				if (bind.equals2(klalbRemoteLine.getBindAddress())
						&& target.equals2(klalbRemoteLine.getSocketAddress())) {
					b = true;
					break;
				}
			}
		}
		return b;
	}

	private boolean checkContainsTarget(MultiProtocolSocketAddress target) {
		boolean b = false;
		for (Iterator<IPv6NetworkLink> iterator = srv6Router.getLinkTabel().iterator(); iterator.hasNext();) {
			IPv6NetworkLink link = iterator.next();
			if (link instanceof KLALBRemoteLink) {
				KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
				if (target.equals(klalbRemoteLine.getSocketAddress())) {
					b = true;
					break;
				}
			}
		}
		return b;
	}

	public void addRemoteLine(KLALBRemoteLink krs) {
		lineslock.writeLock().lock();
		try {
			krs.startIO();
			String externalEndpointsText = generateExternalEndpointsString();
			if (externalEndpointsText != null && !externalEndpointsText.equals(""))
				krs.sendPacket(new ADDLINESPacket(externalEndpointsText));
			srv6Router.getLinkTabel().add(krs);
		} finally {
			lineslock.writeLock().unlock();
		}
	}

	private String generateExternalEndpointsString() {
		StringBuilder sbd = new StringBuilder();
		for (Iterator<MultiProtocolSocketAddress> iterator = externalEndpoints.iterator(); iterator.hasNext();) {
			MultiProtocolSocketAddress klalbRemoteLine = (MultiProtocolSocketAddress) iterator.next();
			sbd.append(klalbRemoteLine.toString());
			sbd.append('\n');
		}
		return sbd.toString();
	}

	

	
	private UDPProtocolRegister udpr;
	public UDPProtocolRegister getUDPregister() {
		return udpr;
	}
	
	private KLTPProtocolRegister kltpr;
	public KLTPProtocolRegister getKLTPregister() {
		return kltpr;
	}


	private PortBinder rawPortBinder ;

	protected PortBinder getRawPortBinder() {
		return rawPortBinder;
	}
	
	private void loadSRv6ProtocolStack(IPv6AddressGroup selfx, boolean enableVirtualAdapter) {
		networkInterfaceManager.getInetAddressesExcept().add(selfx.getAddress().toInet6Address());
		srv6Router = new SRv6Router(selfx, clock);
		if(configItem!=null) {
			srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy()));
			srv6Router.setDeviceName(configItem.getDeviceName());
		}
		srv6Router.runKLALBRouteProtocol();
		routingProtocol = srv6Router.getKlalbRouteProtol();
		routingProtocol.addReceiver((addr, packet) -> {

		});
		rawPortBinder= new PortBinder(this.getSelf().getAddress());
		System.out.println("  Loaded: SRv6 Stack");

		String name = (configItem != null && configItem.getTUNName() != null) ? configItem.getTUNName() : CONST.KLALB_S_RV6;
		boolean isEnabled = configItem == null || configItem.isEnableTUN();
		boolean enableTUN = enableVirtualAdapter && isEnabled && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
		if (enableTUN) {
			Thread t=new Thread(()->{
			try {
				IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),
						new IPv6AddressGroup(srv6Router.getLocator().getAddress(), 32), SRv6Router.MTU, dnsAddresses);
				tunlink.setMonitor(datatMonitor);
				// srv6Router.getLinkTabel().add(tunlink);
				srv6Router.getinLoopback().setFallbackLink(tunlink);
				System.out.println("  Mounted: Tun Adapter");
				
			} catch (Exception e) {
				System.err.println("挂载虚拟网卡失败，可能是无管理员权限？请尝试使用管理员权限运行软件");
				e.printStackTrace();
			}
			});
			t.start();
		}else {
			System.out.println("    Tun Adapter Disabled");
			
		}
		udpr=new UDPProtocolRegister(this);
		srv6Router.getinLoopback().getProtocolNumberRegister()[UDPPacket.UDP_PROTOCOL_NUMBER]=
				udpr;
		System.out.println("  Loaded: UDP Protocol");
		kltpr=new KLTPProtocolRegister(this);
		srv6Router.getinLoopback().getProtocolNumberRegister()[KLTPPacket.KLTP_PROTOCOL_NUMBER]=
				kltpr;
		System.out.println("  Loaded: KLTP Protocol");
		String iproxyname = "KLALB_" + UUID.randomUUID();
		registerToProxyTypeAs(iproxyname);
		System.out.println("  Loaded: KLALB Protocol");

		NTPContext context = new NTPContext(clock);
		context.syncToSystem();
		// System.out.println(context);
		try {
			NTPv4Protocol nvc = new NTPv4Protocol(context,
					new MultiProtocolSocketAddress(iproxyname + "_Datagram","0.0.0.0",123));
			srv6Router.addSRv6RouterListener(new SRv6RouterListener() {

				@Override
				public void onLinkChanged(SRv6Router router, IPv6NetworkLink link) {

					List<Neighbor> nb = router.getNeighbors();
					Set<NTPPeer> sp = nvc.getPeers();
					for (Neighbor neighbor : nb) {
						if (neighbor.getLocator() != null)
							sp.add(new NTPPeer(
									new MultiProtocolSocketAddress(iproxyname + "_Datagram",
											neighbor.getLocator().getAddress().toString(), 123),
									NTPv4Packet.NTP_SYMMETRIC_ACTIVE));

					}
					sp.removeIf((p) -> {
						for (Neighbor neighbor : nb) {
							try {
								if (neighbor.getLocator() != null)
									if (neighbor.getLocator().getAddress().toInet6Address()
											.equals(p.getAddress().getInetAddress())) {
										return false;
									}
							} catch (UnknownHostException e) {
								e.printStackTrace();
							}
						}
						return true;
					});
				}
			});
			nvc2 = new NTPv4Protocol(context, new MultiProtocolSocketAddress("udp","0.0.0.0",0));

			System.out.println("  Loaded: NTP Module");
		} catch (UnknownHostException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} // 106.55.184.199
	}

	private void loadController() {
		this.apiServer = new KLALBRoutingProtocolAPIServer(routingProtocol, this);
		this.apiClient = new KLALBRoutingProtocolAPIClient(routingProtocol);
		twk.schedule(tsk1, 5000, 5000);
		twk.schedule(tsk2, 5000, 5000);
	}

	public KLALBController() {
		this(true, null);
	}

	public KLALBController(Inet6Address self) {
		this(self, true, null);
	}

	public KLALBController(Inet6Address self, boolean enableVirtualAdapter, List<InetAddress> dnsaddr) {
		this.dnsAddresses = dnsaddr;
		IPv6AddressGroup selfg = new IPv6AddressGroup(IPv6Address.valueOf(self), PREFIX);
		initKLALB(enableVirtualAdapter, selfg);
	}

	protected void initKLALB(boolean enableVirtualAdapter, IPv6AddressGroup selfg) {
		ThreadTool.makeVDaemonThread("线路性能采样线程", ()->{
			while(true) {
			linkMonitor.update();
			datatMonitor.update();
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			}
		}).start();
		loadSRv6ProtocolStack(selfg, enableVirtualAdapter);
		loadController();
	}

	public KLALBController(boolean enableVirtualAdapter, List<InetAddress> dnsaddr) {
		this.dnsAddresses = dnsaddr;

		IPv6AddressGroup selfx = new IPv6AddressGroup(KLALBUtils.randomKLALBIPv6Address(), PREFIX);
		initKLALB(enableVirtualAdapter, selfx);
	}

	public KLALBController(KLALBControllerConfigItem configItem) {
		this.configItem = configItem;
		String vase = configItem.getVirtualAddress();
		this.dnsAddresses = new ArrayList<InetAddress>();
		List<InetAddress> vdns = configItem.getDNS();
		if (vdns != null) {
			dnsAddresses.addAll(vdns);
		}
		IPv6AddressGroup selfx = null;
		if (vase != null) {
			try {
				selfx = new IPv6AddressGroup(IPv6Address.valueOf(vase), PREFIX);
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		} else {
			selfx = new IPv6AddressGroup(KLALBUtils.randomKLALBIPv6Address(), PREFIX);

		}

		initKLALB(true, selfx);
		Long vasn = configItem.getVirtualASN();
		if (vasn != null) {
			getIpv6Router().setASN(vasn);
		}

		List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints();
		if (linele != null) {
			getExternalEndpoints().addAll(linele);
		}
		List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
		if (linetoc != null) {
			linetoc.forEach((aline) -> {
				addRemoteLines(aline);
			});
		}

		List<MultiProtocolSocketAddress> ntps = configItem.getNtpServers();
		if (ntps != null) {
			getNTPTable().addAll(ntps);
		}

		List<String> strexc = configItem.getNetworkInterfaceExcepts();
		getNetworkInterfaceExcept().clear();
		if (strexc != null) {
			for (String strexci : strexc) {
				try {
					getNetworkInterfaceExcept().add(NetworkInterface.getByName(strexci));
				} catch (SocketException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public KLALBController(List<InetAddress> daddr) {
		this(true, daddr);
	}

	public KLALBController(Inet6Address self, List<InetAddress> daddr) {
		this(self, true, daddr);
	}

	public KLALBController(boolean enableVirtualAdapter) {
		this(enableVirtualAdapter, null);
	}

	protected KLALBVirtualSocketImpl createVirtualSocketImpl() {
		return new KLALBVirtualSocketImpl(this);
	}

	protected KLALBVirtualDatagramSocketImpl createVirtualDatagramSocketImpl() {
		return new KLALBVirtualDatagramSocketImpl(this);
	}

	private SRv6Router srv6Router;

	public SRv6Router getIpv6Router() {
		return srv6Router;
	}

	private Timer trtr = new Timer("路由表刷新计时器", true);
	{
		trtr.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				if (srv6Router != null) {
					srv6Router.getLinkTabel().removeIf((v) -> {
						return (v instanceof KLALBRemoteLink) && ((KLALBRemoteLink) v).isClosed();
					});
					srv6Router.updateRouteTabel();
				}
			}
		}, 100, 100);

		trtr.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				if (srv6Router != null) {
					srv6Router.sortRouteTabel();
				}

			}
		}, 10, 10);
	}

	public IPv6Packet createPacketToAddress(IPv6Address addr, int flowlabel, IPv6Packet.IPv6Payload packet) {
		return createPacketToAddress(addr, flowlabel, packet, 1);
	}

	public IPv6Packet createPacketToAddress(IPv6Address addr, int flowlabel, IPv6Packet.IPv6Payload packet,
			int count) {

		IPv6Packet ipv = new IPv6Packet();

		if (showpacket)
			System.out.println("KLALB_TX:" + packet);

		ipv.setVersion(6);
		ipv.setTrafficClass(0b10);
		ipv.setFlowLabel(flowlabel);
		ipv.setHopLimit(255);
		ipv.setSourceAddress(getSelf().getAddress());
		ipv.setDestinationAddress(addr);

		ipv.setPayload(packet);
		packet.setParent(ipv);

		

		return ipv;
	}

	private Map<IPv6Address, BandwidthDistributer<PortPair>> bandwidthDistrmap = new ConcurrentHashMap<>();

	private Lock bdmLock = new ReentrantLock();

	protected Map<IPv6Address, BandwidthDistributer<PortPair>> getBandwidthDistrmap() {
		return bandwidthDistrmap;
	}

	protected void registerDistUpdateConsumer(IPv6Address targetaAddress, PortPair portp,
			Consumer<Long> updateConsumer) {
		if (updateConsumer == null) {
			System.out.println("连接" + targetaAddress + " " + portp + " 释放带宽!");
			bdmLock.lock();
			try {
				BandwidthDistributer<PortPair> bdr = bandwidthDistrmap.get(targetaAddress);
				if (bdr != null) {
					bdr.setDistrUpdateConsumer(portp, updateConsumer);
					bdr.setBandwidthRequest(portp, 0L);
					if (bdr.getDistrUpdateConsumerMap().isEmpty()) {
						bandwidthDistrmap.remove(targetaAddress);
					}
				}
			} finally {
				bdmLock.unlock();
			}
		} else {
			System.out.println("连接" + targetaAddress + " " + portp + " 申请带宽!");
			bdmLock.lock();
			try {
				BandwidthDistributer<PortPair> bdr = bandwidthDistrmap.get(targetaAddress);
				if (bdr == null) {
					bdr = new BandwidthDistributer<>(1024 * 20000L * 1024);
					bdr.setTotalRequestUpdateConsumer((treq) -> {
						KLALBRoutingProtocol krp = srv6Router.getKlalbRouteProtol();
						krp.updateTotalRequestBandwidth(targetaAddress, treq);
					});
					bandwidthDistrmap.put(targetaAddress, bdr);
				}

				bdr.setDistrUpdateConsumer(portp, updateConsumer);
			} finally {
				bdmLock.unlock();
			}
		}
	}

	protected void updateBandwidthRequest(IPv6Address targetaAddress, PortPair portp, long bandwidth) {
		if (bandwidth < 0) {
			throw new IllegalArgumentException(bandwidth + "<0");
		}
		// System.out.println("连接"+targetaAddress.getHostAddress()+" "+portp+"
		// 调整带宽到"+bandwidth/1024 +"KB/s!");
		BandwidthDistributer<PortPair> bdr = bandwidthDistrmap.get(targetaAddress);

		if (bdr != null) {
			bdr.setBandwidthRequest(portp, bandwidth);
		} else {
			throw new NullPointerException("连接" + targetaAddress + " " + portp + " 未申请带宽!");
		}

	}

	




	public void registerToProxyTypeAs(String proxyname) {
		MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Stream", streamSocketType);
		MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Datagram", datagramSocketType);
		// ProxyProfileEntry.getRegister().put(proxyname, this);
	}

	private List<MultiProtocolSocketAddress> ntptable = new CopyOnWriteArrayList();

	private KLALBRoutingProtocol routingProtocol;

	public List<MultiProtocolSocketAddress> getNTPTable() {
		return ntptable;
	}

	public KLALBControllerConfigItem getConfigItem() {
		return configItem;
	}



	

}
