import getopt, sys

NO_ARGS='this option takes no arguments'

def parse_args(options, name=sys.argv[0], usage=None, description=None, 
	       comments=None, version=None):
    """Parse the arguments to the command.
    options is a list with one item for each option.  
    The items are of two forms (depending if the option takes an argument):
    with an argument:
      (['a', 'long-op-a'], default_argument, ('ARG_NAME', 'description'))
    without an argument:
      (['long-op-b', 'another-version'], dt_common.NO_ARGS, 'description')
    The rest of the options are for generating the standard --help and
    --version messages.  --version is simple.  --help looks like:
    <usage>
    <description>

      -a, --long-op-a=ARG_NAME  description
          --long-op-b           description

    <comments>

    The return value is a tuple, (ops, args).  ops is a dictionary, where
    the keys are the first option listed ('a' or 'long-op-b').
    op.has_key(key) tests if a non-argument option was entered, and
    op[key] for options that take arguments.

    Order is not preserved at all.  To preserve order, use getopt.
    """
    longopts=[]
    shortopts=''
    key={}
    defaults={}
    results={}
    for o in options:
	op_key=o[0][0]
	if o[1]!=NO_ARGS:
	    defaults[op_key]=o[1]
	else:
	    defaults[op_key]=None
	for o_arg in o[0]:
	    key[o_arg]=op_key
	    if len(o_arg)>1:
		if o[1]==NO_ARGS:
		    longopts.append(o_arg)
		else:
		    longopts.append(o_arg+"=")
	    else:
		if o[1]==NO_ARGS:
		    shortopts=shortopts+o_arg
		else:
		    shortopts=shortopts+o_arg+":"
    try:
	oplist, args=getopt.getopt(sys.argv[1:], shortopts, longopts)
    except getopt.error, value:
	sys.stderr.write(name+": "+value+"\n")
	sys.stderr.write("Try `"+name+" --help' for more information\n")
	sys.exit(1)
    for op, value in oplist:
	if op[0]=='-': op=op[1:]
	if op[0]=='-': op=op[1:]
	op_key=key[op]
	if results.has_key(op_key):
	    sys.stderr.write(name+": the option `"+op+"' can only appear once with any of the forms:\n")
	    for k, v in key.values():
		if k==op_key:
		    if len(v)>1:
			sys.stderr.write("--"+v+" ")
		    else:
			sys.stderr.write("-"+v+" ")
	    sys.stderr.write("\n")
	    exit(1)
	results[op_key]=value
    for k in key.values():
	if not results.has_key(k):
	    if defaults[k]:
		results[k]=defaults[k]
    if key.has_key('help') and results.has_key(key['help']):
	print_help(options, name=name, usage=usage, 
		   description=description, comments=comments)
	sys.exit(0)
    if key.has_key('version') and results.has_key(key['version']):
	print_version(name, version)
	sys.exit(0)
    return results, args

def print_help(options, name=None, usage=None, description=None, 
	       comments=None):
    if usage:
	sys.stdout.write(usage+"\n")
    if description:
	sys.stdout.write(description+"\n")
    if not usage and not description:
	sys.stdout.write(name+"\n")
    sys.stdout.write("\n")
    ops=[]
    max_long_op_length=0
    for o in options:
	short_op=None
	long_op=None
	long_op_arg=None
	desc=None
	for op_form in o[0]:
	    if len(op_form)>1:
		if not long_op: 
		    long_op=op_form
		    max_long_op_length=max(max_long_op_length, len(long_op))
	    else:
		if not short_op: short_op=op_form
	if type(o[2])==type((0,0)):
	    long_op_arg=o[2][0]
	    max_long_op_length=max(max_long_op_length, 
				   len(long_op)+1+len(long_op_arg))
	    desc=o[2][1]
	else:
	    if o[1]!=NO_ARGS:
		long_op_arg="X"
	    desc=o[2]
	ops.append((short_op, long_op, long_op_arg, desc))
    ops.sort(sort_print_ops)
    for o in ops:
	if o[0]:
	    sys.stdout.write("  -%s" % o[0])
	    if o[1]: sys.stdout.write(", ")
	    else: sys.stdout.write("  ")
	else:
	    sys.stdout.write(" " * 6)
	long_op_print=""
	if o[1]:
	    long_op_print="--"+o[1]
	if o[2]:
	    long_op_print=long_op_print+"="+o[2]
	sys.stdout.write(long_op_print)
	sys.stdout.write(" "*(3+max_long_op_length-len(long_op_print)))
	if o[3]:
	    sys.stdout.write(o[3])
	sys.stdout.write("\n")
    if comments:
	sys.stdout.write("\n")
	sys.stdout.write(comments+"\n")

def sort_print_ops(x, y):
    if x[0] and not y[0]: return -1
    if y[0] and not x[0]: return 1
    if not x[0] and not y[0]:
	return cmp(x[1], y[1])
    return cmp(x[0], y[0])
	    
def print_version(name, version):
    sys.stdout.write(name+" "+version+"\n")

