#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "cmdline.h"

#define MAX_LENGTH_PER_PARAMETER_FORMAT 128
#define MAX_PARAMETERS_PER_OPTION 8

static int parse_option_parameters(const char **params, const char *format, void *storage)
{
	int error = 0;
	int fmt_pos=0;
	int pa_num=0;
	//fprintf (stderr, "parse_option_parameters !\n");
	while (1) {
		char s[MAX_LENGTH_PER_PARAMETER_FORMAT];
		int s_pos=0;
		while (format[fmt_pos] == ' ') fmt_pos++;
		while ((format[fmt_pos] != ' ') &&
		       (format[fmt_pos] != 0))
			s[s_pos++] = format[fmt_pos++];
		if (s_pos==0) break; /* end of format string */
		s[s_pos++] = 0;
		if (params[pa_num]==NULL) {error++;break;}
		//fprintf (stderr, "option parameter tag %s value %s\n", s, params[pa_num]);
		if (storage!=NULL) sscanf(params[pa_num], s, &(((int*)storage)[pa_num]));
		pa_num++;
	}
	if (error!=0) return -error;
	return pa_num;
}


/* return 1 if help mode was active
   return negative number if an error occured
   return 0 if everything went fine */
int parse_arguments(int argc, const char **argv, const struct CmdLineOption *options, char **moreParameters, const char *helpText)
{
	int error = 0, helpmode = 0;
	int opt, arg;
	int numMoreParameters = 0;
	if (options==NULL) return -1;

	for (opt=0; options[opt].name != NULL; opt++) {
		if (options[opt].storage != NULL) {
			if (options[opt].parameters==NULL) *(int*)options[opt].storage = 0;
			else if (options[opt].default_value != NULL) {
				const char *defaults = options[opt].default_value;
				const char *default_argv[MAX_PARAMETERS_PER_OPTION];
				int arg = 0, pos = 0;
				while (1) {
					int len = 0;
					while (defaults[pos] == ' ') pos++;
					default_argv[arg] = &(defaults[pos]);
					while ((defaults[pos] != ' ')&&
					       (defaults[pos] != 0)) {pos++;len++;}
					if (len==0) {
						default_argv[arg] = NULL;
						break;
					}
					arg++;
				}
				parse_option_parameters(default_argv, options[opt].parameters, options[opt].storage);
			}
			
		}
	}

	for (arg=1; arg<argc; arg++) {
		const struct CmdLineOption *foundOption = NULL;
		int minuses = 0;
		while ((argv[arg][minuses]=='-')&&(minuses<2)) minuses++;

		if (minuses > 0) {
			if ((!strcmp("help", &(argv[arg][minuses])))||
			    (!strcmp("h", &argv[arg][minuses]))) {
				helpmode = 1;
				break;
			}
			for (opt=0; options[opt].name != NULL; opt++) {
				if (!strcmp(options[opt].name, &(argv[arg][minuses]))) {
					foundOption = &(options[opt]);
					break;
				}
			}
		}

		if (foundOption!=NULL) {
			//fprintf(stderr, "option %s was recognized\n",  (argv[arg]));
			if (foundOption->parameters!=NULL) {
				int ret = parse_option_parameters(&(argv[arg+1]), foundOption->parameters, foundOption->storage);
				if (ret<0) { fprintf (stderr, "cmdline: error reading this option!\n"); error++; break; }
				arg+=ret;
			} else { /* there are no parameters */
				if (foundOption->storage!=NULL) *(int*)(foundOption->storage) = 1;
			}
		} else { /* option not recognized */
			if (moreParameters) {
				moreParameters[numMoreParameters++] = (char*)argv[arg];
			}
			/* else fprintf(stderr, "option %s not recognized\n",  (argv[arg]));*/
		}
	}
	moreParameters[numMoreParameters] = NULL;

	if (helpmode)
		return print_help ( options, helpText );
	return -error;
}

int print_help ( const struct CmdLineOption *options, const char *helpText )
{
       if(helpText!=NULL) fprintf(stdout, "%s\n",helpText);
       fprintf(stdout, "Options:\n");
       int opt;
       for (opt=0; options[opt].name != NULL; opt++) {
		if (options[opt].description!=NULL) {
			fprintf(stdout, "  -%-20s%s", options[opt].name, options[opt].description);
			if (options[opt].default_value!=NULL)
				fprintf(stdout, " (dflt: %s)",options[opt].default_value);
			fprintf(stdout, "\n");
		}
	}
	return 1;
}

