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

#include "arg.h"

static int
argfind(const char **argv, const char *argname)
{
	int len = strlen(argname);
	int arg = 0;
	
	while (*argv)
	{
		if (strstr(*argv, argname) == *argv)
		{
			if ( ((*argv)[len] == '=') || ((*argv)[len] == '\0') )
				return (arg);
		}
		
		argv++;
		arg++;
	}
	
	return (ERR_ARG_NOTFOUND);
}

int 
arg2bool(const char **argv, const char *argname)
{
	const char *cs;
	int arg;
	
	arg = argfind(argv, argname);
	
	if (arg < 0)
		return (0);
	
	cs = *(argv + arg) + strlen(argname);
	
	if (*cs != '\0')
		return (ERR_ARG_SYNTAX);
	
	return (1);
}

int 
arg2int(const char **argv, const char *argname, int *value)
{
	const char *cs;
	int arg;
	
	arg = argfind(argv, argname);
	
	if (arg < 0)
		return (ERR_ARG_NOTFOUND);
	
	cs = *(argv + arg) + strlen(argname);
	
	if ((*cs != '=') || (*(cs + 1) == '\0'))
		return (ERR_ARG_SYNTAX);
	
	*value = atoi(cs + 1);
	
	return (ERR_ARG_OK);
}

int 
arg2cs(const char **argv, const char *argname, const char **value)
{
	const char *cs;
	int arg;
	
	arg = argfind(argv, argname);
	
	if (arg < 0)
		return (ERR_ARG_NOTFOUND);
	
	cs = *(argv + arg) + strlen(argname);
	
	if ((*cs != '=') || (*(cs + 1) == '\0'))
		return (ERR_ARG_SYNTAX);
	
	*value = cs + 1;
	
	return (ERR_ARG_OK);
}

int
arg_getall(const char **argv, const char *fmt, ...)
{
	const char *argname;
	
	int *arg_bool;
	int *arg_int;
	const char **arg_cs;
	
	int optional;
	int status;
	
	va_list ap;
	
	va_start(ap, fmt);
	
	while (*fmt)
	{
		optional = 1;
		
		if (*fmt == '!')
		{
			optional = 0;
			fmt++;
		}
		
		switch (*fmt)
		{
			case 'b':
			{
				argname = va_arg(ap, const char*);
				arg_bool = va_arg(ap, int*);
				
				if ( (status = arg2bool(argv, argname)) < 0 )
				{
					printf("%s '%s'\n", arg_error(status), argname);
					va_end(ap);
					return (status);
				}
				
				*arg_bool = status;
				
				break;
			}
			
			case 'i':
			{
				argname = va_arg(ap, const char*);
				arg_int = va_arg(ap, int*);
				
				if ( ((status = arg2int(argv, argname, arg_int)) < 0) 
					&& ((!optional) || (status != ERR_ARG_NOTFOUND)) )
				{
					printf("%s '%s'\n", arg_error(status), argname);
					va_end(ap);
					return (status);
				}
				
				break;
			}
			
			case 's':
			{
				argname = va_arg(ap, const char*);
				arg_cs = va_arg(ap, const char**);
				
				if ( ((status = arg2cs(argv, argname, arg_cs)) < 0)
					&& ((!optional) || (status != ERR_ARG_NOTFOUND)) )
				{
					printf("%s '%s'\n", arg_error(status), argname);
					va_end(ap);
					return (status);
				}
				
				break;
			}
			
			default:
			{
				printf("internal error\n");
				va_end(ap);
				return (ERR_ARG_FORMAT);
			}
		}
		
		fmt++;
	}
	
	va_end(ap);
	
	return (0);
}

const char*
arg_error(int err)
{
	const char *syntax = 
		"wrong argument syntax";
	const char *notfound = 
		"argument not found";
	const char *range = 
		"wrong argument range";
	const char *format = 
		"unsupported parameter format";
	const char *unknown =
		"unknown error";
		
	switch (err)
	{
		case ERR_ARG_SYNTAX:
			return (syntax);
			
		case ERR_ARG_NOTFOUND:
			return (notfound);
			
		case ERR_ARG_RANGE:
			return (range);
		
		case ERR_ARG_FORMAT:
			return (format);
	}
	
	return (unknown);
}
