enum ParamsType { pstring, pint, punsigned, pbool, pfloat, pdouble, pcolormap };
typedef struct {
  char * key;
  enum ParamsType type;
  void * val;
  int n;
} Params;
static bool atobool (char * s)
{
  if (!strcmp (s, "true"))
    return true;
  if (!strcmp (s, "false"))
    return false;
  return atoi (s) != 0;
}
  
static bool args (Params * p, char * val)
{
  static char * name[] = { "string", "int", "unsigned",
			   "bool", "float", "double", "colormap" };  
  switch (p->type) {
  case pstring:
    if (val[0] != '"') {
      fprintf (stderr, "expecting a string for '%s' got '%s'\n", p->key, val);
      return false;
    }
    if (val[strlen(val) - 1] != '"') {
      fprintf (stderr, "unterminated quoted string '%s'\n", val);
      return false;
    }
    val[strlen(val) - 1] = '\0';
    char * s = &val[1];
    int nc = 0; // number of non-blank characters
    while (*s != '\0') {
      if (!strchr (" \t\n\r", *s))
	nc++;
      s++;
    }
    *((char **)p->val) = nc > 0 ? &val[1] : NULL;
    break;
  case pcolormap:
    if (!strcmp (val, "jet"))
      *((Colormap *)p->val) = jet;
    else if (!strcmp (val, "cool_warm"))
      *((Colormap *)p->val) = cool_warm;
    else if (!strcmp (val, "gray"))
      *((Colormap *)p->val) = gray;
    else if (!strcmp (val, "randomap"))
      *((Colormap *)p->val) = randomap;
    else {
      fprintf (stderr, "unknown colormap '%s'\n", val);
      return false;
    }
    break;
  case pint: case punsigned: case pbool: case pdouble: case pfloat:
    if (val[0] == '"') {
      fprintf (stderr, "expecting a %s for '%s' got %s\n",
	       name[p->type], p->key, val);
      return false;
    }
    if (!p->n) {
      switch (p->type) {
      case pint: *((int *)p->val) = atoi(val); break;
      case punsigned: *((unsigned *)p->val) = atoi(val); break;
      case pbool: *((bool *)p->val) = atobool(val); break;
      case pfloat: *((float *)p->val) = atof(val); break;
      case pdouble: *((double *)p->val) = atof(val); break;
      default: assert (false);
      }
    }
    else {
      if (val[0] != '{') {
	fprintf (stderr, "expecting an array for '%s' got %s\n", p->key, val);
	return false;
      }
      val++;
      int i = 0;
      char c = ',';
      while (i < p->n && c != '}') {
	char * s = strchr (val, ',');
	if (!s)
	  s = strchr (val, '}');
	if (!s) {
	  fprintf (stderr, "expecting an array for '%s' got %s\n", p->key, val);
	  return false;
	}
	c = *s;
	*s++ = '\0';
	switch (p->type) {
	case pint: ((int *)p->val)[i++] = atoi (val); break;
	case punsigned: ((unsigned *)p->val)[i++] = atoi (val); break;
	case pbool: ((bool *)p->val)[i++] = atobool (val); break;
	case pfloat: ((float *)p->val)[i++] = atof (val); break;
	case pdouble: ((double *)p->val)[i++] = atof (val); break;
	default: assert (false);
	}
	val = s;
      }
      if (c != '}') {
	fprintf (stderr, "expecting '}' for '%s' got %s\n", p->key, val);
	return false;
      }
    }
    break;
  default:
    assert (false);
  }
  return true;
}
static char * find_comma (char * s)
{
  int par = 0;
  while (*s != '\0') {
    if (*s == ',' && par == 0) {
      *s = '\0';
      return s + 1;
    }
    if (*s == '{')
      par++;
    else if (*s == '}')
      par--;
    s++;
  }
  return NULL;
}
static char * mystrtok (char * str, const char * delim)
{
  static char * s = NULL;
  char * start = str ? str : s;
  bool string = false;
  s = start;
  while (*s != '\0') {
    if (*s == '"')
      string = !string;
    if (!string && strchr(delim, *s))
      break;
    s++;
  }
  if (*s != '\0')
    *s++ = '\0';
  return start;
}
int parse_params (Params * params)
{
  char * s;
  int i = 0, n = 0;
  Params * p = params;
  while (p->key) p++, n++;
  if (!(s = mystrtok (NULL, ");")) || s[0] == '\n')
    return false;
  while (s && *s != '\0') {
    char * next = find_comma (s), * key = s;
    if ((s = strchr (key, '='))) {
      s[0] = '\0', s++;
      i = -1;
      Params * p = params;
      while (p->key && strcmp(p->key, key)) p++;
      if (!p->key) {
	fprintf (stderr, "unknown key '%s'\n", key);
	return false;
      }
      if (!args (p, s))
	return false;
    }
    else {
      if (i < 0) {
	fprintf (stderr, "anonymous value '%s' after keys\n", key);
	return false;
      }
      if (i >= n) {
	fprintf (stderr, "too many parameters: '%s' %d %d\n", key, i, n);
	return false;
      }
      if (!args (¶ms[i], key))
	return false;
      i++;
    }
    s = next;
  }
  return true;
}