/** asciimatrix
 *
 *  hyperwerk z-ctrl
 *  2005-04-09
 *
 *  (C)2005 martin rumori
 */

#include "lo_parse_keyargs.h"
#include <string.h>


int
lo_parse_keyargs (const char *types, lo_arg **argv, int argc,
		  lo_keyarg **keyargv, int keyargc)
{
  int cur_argi = 0; /* current key arg's index in argv */
  int cur_argl; /* length of current key arg */
  int cur_keyargi = 0; /* current output key index in keyargv */

  /* non-key-args first */
  for (; ; cur_argi += cur_argl, ++cur_keyargi)
    {
      /* end of argv? */
      if (cur_argi == argc) return 0; /* partially parsed, o.k. */
      /* check for keyarg */
      if (types[cur_argi] == 's' && (&argv[cur_argi]->s)[0] == ':') break;
      /* more non-keyed args than allowed args? */
      if (cur_keyargi == keyargc) return -4; /* too many args */

      /* check for match with accepted type signatures */
      {
	const char **typi = keyargv[cur_keyargi]->types_acc;

	if (typi)
	  {
	    /* we take in account only accepted type signatures of
	     * length of types_acc[0] in non-keyed mode */
	    for (cur_argl = strlen(typi[0]); ; ++typi)
	      {
		if (! *typi) return -2; /* no matching type signature found */
		if ((int)strlen(*typi) != cur_argl) continue;
		if (! strncmp(types + cur_argi, *typi, cur_argl)) break; /* match */
	      }
	  }
	else cur_argl = 1; /* accept whichever one-char type signature */
      }

      /* set output keyargv fields */
      keyargv[cur_keyargi]->argc = cur_argl;
      keyargv[cur_keyargi]->types = types + cur_argi;
      keyargv[cur_keyargi]->argv = argv + cur_argi;
    }

  /* keyargs from here on */
  for (; cur_argi < argc; cur_argi += cur_argl)
    {
      /* locate keyarg in output keyargv */
      for (cur_keyargi = 0; ; ++cur_keyargi)
	{
	  if (cur_keyargi == keyargc) return -1; /* unknown keyarg */
	  if (! strcmp(&argv[cur_argi]->s, keyargv[cur_keyargi]->key)) break;
	}

      /* adjust index: point to first arg of keyarg, not key itself */
      ++cur_argi;

      /* determine key arg length */
      for (cur_argl = 0;
	   cur_argi + cur_argl < argc && /* end of argv */
	     (types[cur_argi + cur_argl] != 's' || /* next argv is string? */
	      (&argv[cur_argi + cur_argl]->s)[0] != ':'); /* startswith ':'? */
	   ++cur_argl);

      if (! cur_argl) return -3; /* key without arg */

      /* match type signature */
      {
	const char **typi = keyargv[cur_keyargi]->types_acc;

	if (typi) /* accept all types otherwise */
	  {
	    for (; ; ++typi)
	      {
		if (! *typi) return -2; /* end of types_acc */
		if ((int)strlen(*typi) != cur_argl) continue; /* length? */
		if (! strncmp(types + cur_argi, *typi, cur_argl)) break; /* match? */
	      }
	  }
      }

      /* set output keyargv fields */
      keyargv[cur_keyargi]->argc = cur_argl;
      keyargv[cur_keyargi]->types = types + cur_argi;
      keyargv[cur_keyargi]->argv = argv + cur_argi;
    }

  return 0;
} /* lo_parse_keyargs */

