#include "config"
#include "../build/usage.h"

using namespace std;

void Config::parsecmdline (int ac, char **av) {
    // Remember original argv.
    org_argv = av;
    
    // Prepare invoking command line.
    string cmdline;
    for (int i = 0; i < ac; i++) {
	if (i)
	    cmdline += ' ';
	cmdline += av[i];
    }
    // Not a single argument? Usage.
    if (ac == 1)
	throw Error("Bad command line '" + cmdline + "'\n" + USAGE);

#   define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:Gg:hH:Ij:l:" \
	             "m:M:np:PQ:r:R:Ss:t:T:u:U:vVW:w:xXy:Y:z:Z:"
#   ifdef HAVE_GETOPT_LONG
    static struct option longopts[] = {
	{ "allow-from",		   required_argument, 0, 'a' },
	{ "deny-from",		   required_argument, 0, 'A' },
	{ "backend", 	 	   required_argument, 0, 'b' },
	{ "buffer-size",  	   required_argument, 0, 'B' },
	{ "checkup-interval",  	   required_argument, 0, 'c' },
	{ "close-sockets-fast",    no_argument,	      0, 'C' },
	{ "debug", 	 	   no_argument,       0, 'D' },
	{ "dispatch-mode", 	   required_argument, 0, 'd' },
	{ "hard-maxconn-excess",   required_argument, 0, 'E' },
	{ "soft-maxconn-excess",   required_argument, 0, 'e' },
	{ "dns-cache-timeout",	   required_argument, 0, 'F' },
	{ "foreground", 	   no_argument,       0, 'f' },
	{ "remove-reservations",   no_argument,	      0, 'G' },
	{ "backend-check",	   required_argument, 0, 'g' },
	{ "help", 		   no_argument,       0, 'h' },
	{ "add-server-header",     required_argument, 0, 'H' },
	{ "replace-host-header",   no_argument,       0, 'I' },
	{ "url-match",		   required_argument, 0, 'j' },
	{ "log-traffic-dir",	   required_argument, 0, 'l' },
	{ "max-connections", 	   required_argument, 0, 'm' },
	{ "host-match",		   required_argument, 0, 'M' },
	{ "tryout",		   no_argument,	      0, 'n' },
	{ "pidfile",		   required_argument, 0, 'p' },
	{ "prefix-timestamp",	   no_argument,       0, 'P' },
	{ "soft-maxconnrate",	   required_argument, 0, 'r' },
	{ "quit-after",		   required_argument, 0, 'Q' },
	{ "hard-maxconnrate",	   required_argument, 0, 'R' },
	{ "server", 		   required_argument, 0, 's' },
	{ "sticky-http", 	   no_argument,       0, 'S' },
	{ "backend-timeout", 	   required_argument, 0, 't' },
	{ "client-timeout", 	   required_argument, 0, 'T' },
	{ "time-interval",	   required_argument, 0, 'u' },
	{ "defer-time",		   required_argument, 0, 'U' },
	{ "verbose", 		   no_argument,       0, 'v' },
	{ "version", 		   no_argument,       0, 'V' },
	{ "wakeup-interval",	   required_argument, 0, 'w' },
	{ "web-interface",	   required_argument, 0, 'W' },
	{ "web-interface-auth",    required_argument, 0, 'Y' },
	{ "add-xr-version", 	   no_argument,       0, 'X' },
	{ "add-x-forwarded-for",   no_argument,       0, 'x' },
	{ "onfail",		   required_argument, 0, 'y' },
	{ "onstart",		   required_argument, 0, 'z' },
	{ "onend", 		   required_argument, 0, 'Z' },
	{ 0, 			   0, 		      0, 0 }
    };
#   endif

    int opt;
    bool backend_set = false;
    bool tryout = false, wakeup_used = false;
    string current_hostmatch = "";
    string current_urlmatch = "";
    BackendCheck current_backendcheck;
    vector<string> parts;

#   ifdef HAVE_GETOPT_LONG
    while ( (opt = getopt_long (ac, av, OPTSTRING, longopts, 0)) > 0)
#else
    while ( (opt = getopt (ac, av, OPTSTRING)) > 0 )
#endif
    {
	switch (opt) {
	case 'a':
	    addallow (optarg);
	    break;
	case 'A':
	    adddeny (optarg);
	    break;
	case 'b':
	    setbackend (optarg, current_hostmatch, current_urlmatch,
			current_backendcheck);
	    backend_set = true;
	    break;
	case 'B':
	    bufsize = (unsigned)setinteger (optarg);
	    break;
	case 'c':
	    checkup = setinteger (optarg);
	    break;
	case 'C':
	    fast_close = true;
	    break;
	case 'D':
	    verbose_flag = true;
	    debug_flag = true;
	    break;
	case 'd':
	    setdispatchmode (optarg);
	    break;
	case 'E':
	    hard_maxconn_excess_prog = optarg;
	    break;
	case 'e':
	    soft_maxconn_excess_prog = optarg;
	    break;
	case 'F':
	    dns_cache_timeout = (unsigned)setinteger(optarg);
	    break;
	case 'f':
	    foreground_mode = true;
	    break;
	case 'G':
	    removereservations(true);
	    break;
	case 'g':
	    current_backendcheck.parse(optarg);
	    break;
	case 'h':
	case '?':
	    throw Error(USAGE);
	    break;
	case 'H':
	    addserverheader (optarg);
	    break;
	case 'I':
	    replacehostheader(true);
	    break;
	case 'j':
	    current_urlmatch = optarg;
	    break;
	case 'l':
	    dumpdir (optarg);
	    break;
	case 'M':
	    current_hostmatch = optarg;
	    break;
	case 'm':
	    max_conn = (unsigned)setinteger (optarg);
	    break;
	case 'n':
	    tryout = true;
	    break;
	case 'P':
	    prefix_timestamp = true;
	    break;
	case 'p':
	    pidfile(optarg);
	    break;
	case 'Q':
	    quit_after = (unsigned) setinteger(optarg);
	    break;
	case 'r':
	    soft_maxconnrate = (unsigned) setinteger (optarg);
	    break;
	case 'R':
	    hard_maxconnrate = (unsigned) setinteger (optarg);
	    break;
	case 's':
	    setserver (optarg);
	    break;
	case 'S':
	    sticky_http = true;
	    break;
	case 't':
	    parts = str2parts(optarg, ':');
	    if (parts.size() == 1) {
		backend_read_timeout(atoi(parts[0].c_str()));
		backend_write_timeout(atoi(parts[0].c_str()));
	    } else if (parts.size() == 2) {
		backend_read_timeout(atoi(parts[0].c_str()));
		backend_write_timeout(atoi(parts[1].c_str()));
	    } else
		throw Error("Bad backend timeout specifier, "
			    "expected SEC or RSEC:WSEC");
	    break;
	case 'T':
	    parts = str2parts(optarg, ':');
	    if (parts.size() == 1) {
		client_read_timeout(atoi(parts[0].c_str()));
		client_write_timeout(atoi(parts[0].c_str()));
	    } else if (parts.size() == 2) {
		client_read_timeout(atoi(parts[0].c_str()));
		client_write_timeout(atoi(parts[1].c_str()));
	    } else
		throw Error("Bad backend timeout specifier, "
			    "expected SEC or RSEC:WSEC");
	    break;
	case 'U':
	    defer_time = (unsigned) setinteger(optarg);
	    break;
	case 'u':
	    connrate_timeinterval = (unsigned) setinteger (optarg);
	    break;
	case 'v':
	    verbose_flag = true;
	    break;
	case 'V':
	    cout << "XR version    : " << VER << "\n"
		 << "Written by    : " << AUTHOR << "\n"
		 << "Maintained by : " << MAINTAINER << "\n"
		 << "Primary site  : " << DISTSITE << "\n"
		 << "Compiled with : " << CONF_CC << "\n"
		 << "Optimization  : " << CONF_OPTFLAGS << "\n"
		 << "System        : " << SYS << "\n"
		 << "Libraries     : " << CONF_LIB << "\n"
		 << "Type sizes    : ssize_t=" << sizeof(ssize_t)
		 << 		    ", int=" << sizeof(int)
		 << 		    ", long=" << sizeof(long)
		 << 		    ", double=" << sizeof(double)
		 << 		    ", ptr=" << sizeof(char*) << "\n"
		;
#	    ifdef HAVE_GETOPT_H
	    cout << "getopt.h      : present\n";
#	    else
	    cout << "getopt.h      : absent\n";
#	    endif
#	    ifdef HAVE_INADDR_NONE
	    cout << "INADDR_NONE   : present\n";
#	    else
	    cout << "INADDR_NONE   : absent, defined to " << INADDR_NONE
		 << "\n";
#	    endif
#	    ifdef HAVE_GETOPT_LONG
	    cout << "getopt_long() : present\n";
#	    else
	    cout << "getopt_long() : absent (only short flags will work)\n";
#	    endif
#	    ifdef HAVE_INET_ATON
	    cout << "inet_aton()   : present\n";
#	    else	    	
	    cout << "inet_aton()   : absent\n";
#	    endif	    
#	    ifdef HAVE_STRNSTR
	    cout << "strnstr()     : present\n";
#	    else	    	
	    cout << "strnstr()     : absent\n";
#	    endif	    
	    exit (0);
	case 'W':
	    setwebinterface(optarg);
	    break;
	case 'w':
	    wakeup = setinteger (optarg);
	    wakeup_used = true;
	    break;
	case 'X':
	    add_xr_version = true;
	    break;
	case 'x':
	    add_x_forwarded_for = true;
	    break;
	case 'y':
	    onfail(optarg);
	    break;
	case 'Y':
	    webinterface_auth(optarg);
	    break;
	case 'z':
	    onstart(optarg);
	    break;
	case 'Z':
	    onend(optarg);
	    break;
	default:
	    throw Error("Unknown flag, try 'xr -h' for usage");
	    break;
	}
    }

    // Sanity checks.
    if (ac != optind)
	throw Error("Bad command line '" + cmdline + "'\n" + USAGE);

    if (!backend_set)
	throw Error("No backend defined, use '-b...' at least once, "
		    "or try 'xr -h' for usage");
    if (checkup && wakeup) {
	if (!wakeup_used)
	    wakeup = 0;
	else
	    throw Error("Use either --checkup-interval or --wakeup-interval, "
			"but not both");
    }

    // In tryout mode, stop now.
    if (tryout)
	exit (0);

    msg ("+--------------------------------------------+\n");
    msg ("| Welcome to xr V" VER "                        |\n");
    msg ("| Copyright (c) Karel Kubat <karel@kubat.nl> |\n");
    msg ("| Distributed under GPLV3.                   |\n");
    msg ("+--------------------------------------------+\n");

    msg ("Invoking command line: '" + cmdline + "'\n");
}
