package client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Vector;

import org.xbill.DNS.ResolverConfig;

import common.Common;
import common.CommonCmdLineParser;
import common.CommonOptions;

public class ClientCmdLineParser extends CommonCmdLineParser {
	private Option listenAddr;
	private Option listenPort;
	private Option dnsServer;
	private Option dnsRequestAddress;
	private Option interval;
	private Option truncationAllowance;
	
	public ClientCmdLineParser() {
		listenAddr = addStringOption("listen-address");
		listenPort = addIntegerOption("listen-port");
		dnsServer = addStringOption("dns-server");
		interval = addIntegerOption("interval");
		truncationAllowance = addIntegerOption("trunc-bytes");
		
		//hidden option that probably no one will want to use
		dnsRequestAddress = addStringOption("dns-request-address");
	}

	@SuppressWarnings("unchecked")
	public ClientOptions parseCmdLine(String args[]) {
		CommonOptions commonOpts = super.parseCmdLine(args);
		if (commonOpts == null) {
			return null;
		}
		
		try {
			ClientOptions result = new ClientOptions();
			result.domain = commonOpts.domain;
			result.mtu = commonOpts.mtu;
			result.logLevel = commonOpts.logLevel;
			
			String listenAddrString = (String)getOptionValue(listenAddr);
			if (listenAddrString != null) {
				result.listenAddr = InetAddress.getByName(listenAddrString);
			}
			Integer iListenPort = (Integer) getOptionValue(listenPort);
			
			if (iListenPort == null) {
				result.listenPort = -1;
			} else {
				result.listenPort = iListenPort;
			}
			
			result.dnsServers = new ArrayList<InetAddress>();
			Vector dnsServers = getOptionValues(dnsServer);
			for (int i=0; i<dnsServers.size(); i++) {
				String dnsServerString = (String)dnsServers.get(i);
				result.dnsServers.add(InetAddress.getByName(dnsServerString));
			}
			
			String dnsReqAddr = (String)getOptionValue(dnsRequestAddress);
			if (dnsReqAddr != null) {
				result.dnsRequestAddress = InetAddress.getByName(dnsReqAddr);
			}
			
			Integer iInterval = (Integer)getOptionValue(interval);
			if (iInterval != null) {
				result.interval = iInterval;
			}

			Integer iTruncationBytes = (Integer)getOptionValue(truncationAllowance);
			if (iTruncationBytes == null) {
				result.truncationAllowance = -1;
			} else {
				result.truncationAllowance = iTruncationBytes;
			}
			
			if (!setDefaults(result)) {
				return null;
			}
			
			if (result.domain == null) {
				return null;
			}

			if (result.listenPort <= 0 || result.listenPort >= 65536) {
				return null;
			}
			
			if (result.truncationAllowance < 0 || result.truncationAllowance > 100) {
				return null;
			}
			
			return result;
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
			return null;
		}
	}

	private static boolean setDefaults(ClientOptions result) throws UnknownHostException {
		if (result.listenPort < 0) {
			result.listenPort = Client.DEFAULT_PORT;
		}
		if (result.listenAddr == null) {
			result.listenAddr = InetAddress.getByName(Client.DEFAULT_ADDR);
		}
		if (result.mtu <= 0) {
			result.mtu = Common.DEFAULT_MTU;
		}
		if (result.dnsServers.size() == 0) {
			String [] servers = ResolverConfig.getCurrentConfig().servers(); 
			
			for (int i=0; i<servers.length; i++) {
				try {
					result.dnsServers.add(InetAddress.getByName(servers[i]));
					//System.out.println("Adding dns server:" + servers[i]);
					break;
				} catch (IOException e) {
					System.out.println("Failed to lookup server:" + servers[i]);
				}
			}
			
			//we couldnt autoconfig the dns server
			if (result.dnsServers.size() == 0) {
				System.out.println("Failed to automatically find dns server(s).");
				return false;
			}
		}
		
		if (result.dnsRequestAddress == null) {
			result.dnsRequestAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
		}
		
		if (result.interval <= 0) {
			result.interval = Client.DEFAULT_INTERVAL;
		}
		
		if (result.logLevel < 0) {
			result.logLevel = Common.DEFAULT_LOGLEVEL;
		}
		
		if (result.truncationAllowance < 0) {
			result.truncationAllowance = Client.DEFAULT_TRUNCATION_ALLOWANCE;
		}
		
		return true;
		
	}

	public void printUsage() {
		System.out.println("Usage: java -jar " + Client.CLIENT_NAME + ".jar  --domain tnl.example.com <options>");
		System.out.println("Available options:");
		System.out.println("--domain         a.b.com  The domain name that the dns server is sitting behind.");
		System.out.println("                          This must match the server's domain argument.");		
		System.out.println("--dns-server     a.b.c.d  Add a dns server to tunnel through. Overrides default");
		System.out.println("                          servers. This option may be specified multiple times.");
		System.out.println("--listen-port    portNum  The port the client should listen on. Defaults to");
		System.out.println("                          port " + Client.DEFAULT_PORT +".");
		System.out.println("--listen-address a.b.c.d  The address on which to listen for local connections.");
		System.out.println("                          Defaults to all addresses.");
		System.out.println("--interval       time     The delay between sending packets, in milliseconds.");
		System.out.println("                          Defaults to " + Client.DEFAULT_INTERVAL + ".");
		System.out.println("--mtu            bytes    Set the udp maximum MTU. Defaults to " + Common.DEFAULT_MTU + ".");
		System.out.println("--trunc-bytes    bytes    Tell the server to prevent truncation by sending");
		System.out.println("                          less data when answering queries. Defaults to " + Client.DEFAULT_TRUNCATION_ALLOWANCE + ".");
		System.out.println("--log-level      0-5      The amount of information to display. Defaults to 3.");
		System.out.println("                          0: Display nothing.");
		System.out.println("                          1: Display errors.");
		System.out.println("                          2: Display errors, warnings.");
		System.out.println("                          3: Display errors, warnings, general information.");
		System.out.println("                          4: Spam.");
		System.out.println("                          5: Megaspam.");
	}
}
