package org.kne.cloud.network.tun;

import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.ArrayList;
import java.util.List;

import org.kne.cloud.network.tun.WindowsDNSManager.DnsInterfaceSettings;
import org.kne.cloud.network.tun.WindowsIPAddressManager.AddressFamily;
import org.kne.cloud.network.tun.WindowsIPAddressManager.UnicastIPAddressRow;
import org.kne.cloud.network.tun.WindowsIPInterfaceManager.MibIpInterfaceRow;
import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID;
import org.kne.cloud.network.tun.WintunDriver.SendPacket;
import org.kne.cloud.network.tun.WintunDriver.WintunAdapterHandle;
import org.kne.cloud.network.tun.WintunDriver.WintunSessionHandle;

public class WindowsTUNNetworkDevice extends TUNNetworkDevice{

	private WintunAdapterHandle adapter;

	private long luid;
	private GUID guid;
	
	public WindowsTUNNetworkDevice(String name, String type) {
		 adapter = WintunDriver. createAdapter(name, type, null);
		 luid=WintunDriver.getAdapterLuid(adapter);
		 guid=WindowsInterfaceConverter .convertLuidToGuid(luid);
		// System.out.println("创建虚拟网卡： LUID="+luid+" GUID="+guid);
	}

	@Override
	public boolean isOpen() {
		return adapter!=null;
	}

	@Override
	public void close() throws IOException {
		if(channel!=null&&channel.isOpen()) {
			channel.close();
			channel=null;
		}
		WintunAdapterHandle adap=adapter;
		adapter=null;
		if(adap!=null) {
		WintunDriver.closeAdapter(adap);
		}
	}


	

	@Override
	public int getMTU() throws IOException {
        MibIpInterfaceRow ipv4Info =WindowsIPInterfaceManager. getInterfaceInfo(luid, WindowsIPInterfaceManager. AF_INET);
        MibIpInterfaceRow ipv6Info = WindowsIPInterfaceManager.getInterfaceInfo(luid,WindowsIPInterfaceManager. AF_INET6);
		
		return Math.min(ipv4Info.getNlMtu(),ipv6Info. getNlMtu());
	}

	@Override
	public void setMTU(int mtu) throws IOException {
		/*
		try {
			int rez = Runtime.getRuntime()
					.exec("netsh interface ipv6 set subinterface \"" + name + "\" mtu=" + mtu + " store=persistent")
					.waitFor();
			int rezx = Runtime.getRuntime()
					.exec("netsh interface ipv4 set subinterface \"" + name + "\" mtu=" + mtu + " store=persistent")
					.waitFor();
			if (rez != 0 || rezx != 0) {
				throw new IOException("set MTU failed!");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		WindowsIPInterfaceManager.setInterfaceMtu(luid, WindowsIPInterfaceManager. AF_INET, mtu);
		WindowsIPInterfaceManager.setInterfaceMtu(luid, WindowsIPInterfaceManager. AF_INET6, mtu);
	}

	@Override
	public void setDNSAddress(List<InetAddress> addresses) throws IOException {
		DnsInterfaceSettings dis=new DnsInterfaceSettings();
		dis.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
		dis.setFlags(WindowsDNSManager.DNS_SETTING_IPV6|WindowsDNSManager.DNS_SETTING_NAMESERVER);
		dis.setNameServers(new ArrayList<String>());

		DnsInterfaceSettings dis4=new DnsInterfaceSettings();
		dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
		dis4.setFlags(WindowsDNSManager.DNS_SETTING_NAMESERVER);
		dis4.setNameServers(new ArrayList<String>());
		
		for(InetAddress addr:addresses) {
			if(addr instanceof Inet6Address) {
				dis.getNameServers().add(addr.getHostAddress());
			}else {
				dis4.getNameServers().add(addr.getHostAddress());
			}
		}
		WindowsDNSManager.setInterfaceDnsSettings(guid, dis);
		WindowsDNSManager.setInterfaceDnsSettings(guid, dis4);
	}

	@Override
	public List<InetAddress> getDNSAddress() throws SocketException {
		DnsInterfaceSettings dis=new DnsInterfaceSettings();
		dis.setFlags(WindowsDNSManager.DNS_SETTING_IPV6);
		dis.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
		dis=WindowsDNSManager.getInterfaceDnsSettings(guid, dis);
		List<String> strs=dis.getNameServers();
		
		DnsInterfaceSettings dis4=new DnsInterfaceSettings();
		dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
		dis4=WindowsDNSManager.getInterfaceDnsSettings(guid, dis4);
		List<String> strs4=dis4.getNameServers();
		
		List<InetAddress>dnsaddrs=new ArrayList<>();
		for(String str:strs) {
			try {
				dnsaddrs.add(InetAddress.getByName(str));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}
		for(String str:strs4) {
			try {
				dnsaddrs.add(InetAddress.getByName(str));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}
		return dnsaddrs;
	}
	WindowsTUNChannel channel;
	
	@Override
	public TUNChannel openSession() {
		if(channel!=null&&channel.isOpen()) {
			throw new IllegalStateException("Session already created!");
		}
		WintunSessionHandle session=WintunDriver.startSession(adapter, WintunDriver.WINTUN_MAX_RING_CAPACITY);
		 channel=new WindowsTUNChannel(session);
		return channel;
	}

	@Override
	public List<InetAddressRow> getIPAddress() throws IOException {
		List<InetAddressRow> result=new ArrayList<>();
		List<UnicastIPAddressRow>info=  WindowsIPAddressManager.getUnicastIpAddressTable(AddressFamily.AF_UNSPEC);
		for(UnicastIPAddressRow cir:info) {
			InetAddressRow rowc=new InetAddressRow(cir.getAddress(), cir.getOnLinkPrefixLength());
			result.add(rowc);
		}
		return result;
	}

	@Override
	public void setIPAddress(List<InetAddressRow> rows) throws IOException {
		List<InetAddressRow> rowsinc=new ArrayList<>(rows);
		List<UnicastIPAddressRow>info=  WindowsIPAddressManager.getUnicastIpAddressTable(AddressFamily.AF_UNSPEC);
		for(UnicastIPAddressRow cir:info) {
			if(cir.getInterfaceLuid()!=luid)
				continue;
			InetAddressRow rowc=new InetAddressRow(cir.getAddress(), cir.getOnLinkPrefixLength());
			if(!rows.contains(rowc)) {
				WindowsIPAddressManager.deleteUnicastIpAddressEntry(cir);
			}else {
				rowsinc.remove(rowc);
			}
		}
		for(InetAddressRow row:rowsinc) {
			InetAddress addr=row.getAddress();
			WindowsIPAddressManager.addIpAddressToAdapter(luid, addr, row.getPerfix());
		}
	}
	
}

