#include <ctype.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "opt.h"

int opt_cli_defaultport = 7033;
// "<host:>port" tries to open a socket; descriptor goes in *target
// "<host> assumes port 7033 on the remote host
// "port" assumes localhost
// no option assumes localhost, port 7033
// port 7033 can be overridden
int opt_cli(char *arg,int *target,int opt) {
  char *sport, *host;
  int port;

  if (!arg) {
    host = "127.0.0.1";
    port = opt_cli_defaultport;
  } else {
    sport = strchr(arg,':');
    if (sport) { // <host>:<port>
      *(sport++) = 0;
      host = arg;
      port = strtoul(sport, NULL, 0);
    } else if (!strchr(arg,'.') && (port = atoi(arg))) {
      host = "127.0.0.1";
    } else {
      host = arg;
      port = opt_cli_defaultport;
    }
  }
  *target = create_client_socket(host,port);
  if (*target < 0) {
    perror("create_client_socket:");
    return 0;
  }
  return 1;
}

int opt_str(char *arg,char **target,int opt) {
  *target = malloc(strlen(arg)+1);
  strcpy(*target,arg);
  return 1;
}

int opt_bool(char *arg,int *target,int opt) {
  *target = 1;
  return 1;
}
			
int opt_long(char *arg,unsigned *target,int opt) {
  *target = strtoul(arg,NULL,0);
  return 1;
}

// replace the first occurence of <X> with X in str.
// if this character is upper-case, make it lower-case
char *unaccelerate(const char *str) {
  char *ret,*str2 = malloc(strlen(str)+1);
  int found = 0;
  
  ret = str2;
  while (*str) {
    if (found == 0 && *str == '<') {
      found = 1;
      str++;
    } else if (found == 1 && *str == '>') {
      found = 2;
      str++;
    } else {
      if (found != 1) {
	*str2++ = *str++;
      } else {
	*str2++ = tolower(*str++);
      }
    }
  }
  return ret;
}

// return X for the first occurence of <X> in str
// if there is none, return a special non-char value
int accelerate(const char *str) {
  static int nonchar = 0x1000;

  while (*str && *str != '<') {
    str++;
  }
  if (*str == '<') {
    return *(str+1);
  } else {
    return nonchar++;
  }
}

void process_options(int argc,char **argv,struct option2 *opts) {
  const struct option2 *opts0 = opts;
  struct option *long_options;
  char *optstr,*helpstr;
  const char *prolog="",*epilog="";
  int i,n,j,c;

  for (n=0;
       opts[n].has_arg||opts[n].f||opts[n].target;
       n++);
  if (opts[n].name) {
    prolog = opts[n].name;
  }
  if (opts[n].help) {
    epilog = opts[n].help;
  }
  long_options = malloc(sizeof(struct option) * (n+2));
  optstr = malloc(2+3*n);
  j=0;
  optstr[j] = 0;
  for (i=0;i<n;i++) {
    long_options[i].name = unaccelerate(opts[i].name);
    long_options[i].has_arg = opts[i].has_arg;
    long_options[i].flag = 0;
    long_options[i].val = accelerate(opts[i].name);
    if (long_options[i].val && long_options[i].val < 0xFF) {
      optstr[j++] = long_options[i].val;
      if (long_options[i].has_arg > 0) {
	optstr[j++] = ':';
      } 
      if (long_options[i].has_arg > 1) {
	optstr[j++] = ':';
      }
      optstr[j] = 0;
    }
  }
  long_options[i].name = "help";
  long_options[i].has_arg = 0;
  long_options[i].flag = 0;
  long_options[i].val = 0xFFF;
  long_options[i+1].name = 0;
  long_options[i+1].has_arg = 0;
  long_options[i+1].flag = 0;
  long_options[i+1].val = 0;
  optstr[j++] = 'h';
  optstr[j] = 0;

  while ((c = getopt_long(argc,argv,optstr,long_options,NULL)) != -1) {
    for (i=0;i<=n;i++) {
      if (c == long_options[i].val) {
	if (c == 0xFFF) {
	  fprintf(stderr,"%s",prolog);
	  for (j=0;j<n;j++) {
	    if (opts[j].help) {
	      if (long_options[j].val < 0x100) {
		fprintf(stderr,"-%c | --%s%s%s\n",long_options[j].val,
			long_options[j].name, 
			(long_options[j].has_arg>0 ? "=" : " "),
			opts[j].help);
		/*
		  To do: if has_arg == 2, then the parameter is optional.
		  we should show that somehow.
		 */
	      } else {
		fprintf(stderr,"--%s  %s\n",long_options[j].name, opts[j].help);
	      }
	    }
	  }
	  fprintf(stderr,"%s",epilog);
	} else if (!opts[i].f(optarg,opts[i].target,c)) {
	  return;
	}
      }
    }
  }
  free(long_options);
  free(optstr);
}
