package org.callimachusproject.util;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Set;
import java.util.TreeSet;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class DomainNameSystemResolver {
	private static final DomainNameSystemResolver instance;
	static {
		try {
			instance = new DomainNameSystemResolver();
		} catch (NamingException e) {
			throw new AssertionError(e);
		}
	}

	public static DomainNameSystemResolver getInstance() {
		return instance;
	}

	private final DirContext ictx;

	private DomainNameSystemResolver() throws NamingException {
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put("java.naming.factory.initial",
				"com.sun.jndi.dns.DnsContextFactory");
		ictx = new InitialDirContext(env);
	}

	public String lookup(String domain, String... type) throws NamingException {
		Attributes attrs = ictx.getAttributes(domain, type);
		Enumeration e = attrs.getAll();
		if (e.hasMoreElements()) {
			Attribute a = (Attribute) e.nextElement();
			int size = a.size();
			if (size > 0) {
				return (String) a.get(0);
			}
		}
		return null;
	}

	public InetAddress getLocalHost() {
		try {
			return InetAddress.getByName(null);
		} catch (UnknownHostException e) {
			throw new AssertionError(e);
		}
	}

	public String getLocalHostName() {
		try {
			return InetAddress.getLocalHost().getHostName();
		} catch (UnknownHostException e) {
			throw new AssertionError(e);
		}
	}

	public String getCanonicalLocalHostName() {
		try {
			// attempt for the host canonical host name
			return InetAddress.getLocalHost().getCanonicalHostName();
		} catch (UnknownHostException uhe) {
			try {
				// attempt to get the loop back address
				return InetAddress.getByName(null).getCanonicalHostName();
			} catch (UnknownHostException uhe2) {
				// default to a standard loop back IP
				return "127.0.0.1";
			}
		}
	}

	public Collection<String> reverseAllLocalHosts() throws SocketException {
		Set<String> set = new TreeSet<String>();
		Enumeration<NetworkInterface> ifaces = NetworkInterface
				.getNetworkInterfaces();
		while (ifaces.hasMoreElements()) {
			NetworkInterface iface = ifaces.nextElement();
			Enumeration<InetAddress> raddrs = iface.getInetAddresses();
			while (raddrs.hasMoreElements()) {
				InetAddress raddr = raddrs.nextElement();
				addAllNames(raddr, set);
			}
			Enumeration<NetworkInterface> virtualIfaces = iface
					.getSubInterfaces();
			while (virtualIfaces.hasMoreElements()) {
				NetworkInterface viface = virtualIfaces.nextElement();
				Enumeration<InetAddress> vaddrs = viface.getInetAddresses();
				while (vaddrs.hasMoreElements()) {
					InetAddress vaddr = vaddrs.nextElement();
					addAllNames(vaddr, set);
				}
			}
		}
		try {
			addAllNames(InetAddress.getLocalHost(), set);
		} catch (UnknownHostException e) {
			throw new AssertionError(e);
		}
		addAllNames(getLocalHost(), set);
		return set;
	}

	public String reverse(String ip) {
		try {
			return reverse(InetAddress.getByName(ip));
		} catch (UnknownHostException e) {
			return ip;
		}
	}

	public String reverse(InetAddress netAddr) {
		String name = netAddr.getCanonicalHostName();
		try {
			if (!name.equals(netAddr.getHostAddress())
					&& netAddr.equals(InetAddress.getByName(name)))
				return name;
		} catch (UnknownHostException e) {
			// use reverse name
		}
		String address = getAddress(netAddr);
		if (address == null)
			return name;
		return address;
	}

	private void addAllNames(InetAddress addr, Set<String> set) {
		String name = addr.getHostName();
		if (!name.equals(addr.getHostAddress())) {
			set.add(name);
		}
		String canonical = addr.getCanonicalHostName();
		if (!canonical.equals(addr.getHostAddress())) {
			set.add(canonical);
		}
		String address = getAddress(addr);
		if (address != null) {
			set.add(address);
		}
	}

	private String getAddress(InetAddress netAddr) {
		byte[] addr = netAddr.getAddress();
		if (addr.length == 4) { // IPv4 Address
			StringBuilder sb = new StringBuilder();
			for (int i = addr.length - 1; i >= 0; i--) {
				sb.append((addr[i] & 0xff) + ".");
			}
			return sb.append("in-addr.arpa").toString();
		} else if (addr.length == 16) { // IPv6 Address
			StringBuilder sb = new StringBuilder();
			for (int i = addr.length - 1; i >= 0; i--) {
				sb.append(Integer.toHexString((addr[i] & 0x0f)));
				sb.append(".");
				sb.append(Integer.toHexString((addr[i] & 0xf0) >> 4));
				sb.append(".");
			}
			return sb.append("ip6.arpa").toString();
		}
		return null;
	}
}

@@ -3859,30 +3859,24 @@
 et) %7B%0A%09%09
-String name = 
+set.add(
 addr.get
@@ -3883,72 +3883,61 @@
 Host
-Name
+Address
 ()
+)
 ;%0A%09%09
-if (!name.equals(addr.getHostAddress())) %7B
+set.add(addr.getHostName());
 %0A%09%09
-%09
 set.add(
 name
@@ -3936,145 +3936,37 @@
 add(
-name);%0A%09%09%7D%0A%09%09String canonical = addr.getCanonicalHostName();%0A%09%09if (!canonical.equals(addr.getHostAddress())) %7B%0A%09%09%09set.add(canonical);%0A%09%09%7D
+addr.getCanonicalHostName());
 %0A%09%09S

Include local IP addresses for authorized system access