/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * ShapeTools/shape program - varsec.c
 *
 * Author: Axel Mahler (Axel.Mahler@cs.tu-berlin.de) and Wolfgang Obst
 *
 * $Header: varsec.c[8.0] Thu May 19 13:58:27 1994 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: varsec.c[8.0] Thu May 19 13:58:27 1994 axel@cs.tu-berlin.de frozen $";
#endif

#include "shape.h"

EXPORT struct vardef variantDefs[MAXVARDEFS];
EXPORT char *curvar[MAXVARIANTS] = {"","","","","","","","",
				    "","","","","","","","",
				    "","","","","","","","",
				    "","","","","","","",""};

EXPORT struct varclass variantClasses[MAXVCLASS];
EXPORT int lastVariantDef = 0;
EXPORT int variants_active = 0;
EXPORT int activeVariants[MAXVARIANTS] = { -1, -1, -1, -1, -1, 
					     -1, -1, -1, -1, -1, 
					     -1, -1, -1, -1, -1, 
					     -1, -1, -1, -1, -1,
					     -1, -1, -1, -1, -1, 
					     -1, -1, -1, -1, -1, 
					     -1, -1 };

LOCAL int vardef_position ();
LOCAL Bool attrvar ();

EXPORT void vardef_end () {

  if (variantDefs[lastVariantDef].name) {
    register int i;
    for (i = 0; cmd_line_vars[i] && (i < MAXCMDLINEVARS); i++) {
      if (strcmp (cmd_line_vars[i]+1, variantDefs[lastVariantDef].name))
	continue;
      activate_variant (cmd_line_vars[i]);
    }
  }
}
	    
EXPORT void vclassdef (name, varnames)
     char *name;
     struct stringlist *varnames;
{
  register int k, l;
  struct stringlist *nextvar;

  k = 0;
  while (variantClasses[k].name) {
    if (!strcmp (variantClasses[k].name, name))
	errexit (39, name);
    k++;
    if (k == MAXVCLASS)
      errexit (27, "vclass definitions");
  }
  variantClasses[k].name = check_strdup (name);
  variantClasses[k].active = -1;

  l = 0;
  nextvar = varnames;
  while (nextvar && nextvar->string) {
    variantClasses[k].variants[l] = check_strdup (nextvar->string);
    l++;
    nextvar = nextvar->next;
  }
  variantClasses[k].variants[l] = NIL;
}

	
EXPORT Bool check_vclass(varname)
     char *varname;
{
  register int i = 0, j = 0;
  
  while (variantClasses[i].name) {
    j = 0;
    while (variantClasses[i].variants[j] != NIL) {
      if (strcmp (variantClasses[i].variants[j],varname) == 0) {
	if ((variantClasses[i].active != -1) && 
	    (variantClasses[i].active != j)) {
	  if (novclassflg) {
	    stLog (" incompatible variant combination with ", 
		   ST_LOG_WARNING | ST_LOG_NONL);
	    stLog (varname, ST_LOG_MSGERR | ST_LOG_NONL);
	    stLog (", vclass ", ST_LOG_MSGERR | ST_LOG_NONL);
	    stLog (variantClasses[i].name, ST_LOG_MSGERR);
	  }
	  else	    
	    return FALSE;
	}
	variantClasses[i].active = j;
      }
      j++;
    }
    i++;
  }
  if (!variantClasses[i].name)
    return TRUE;
  
  /*NOTREACHED*/
  return FALSE;
}
  
EXPORT void reset_vclass() {
  register int i = 0;

  while (variantClasses[i].name) {
    variantClasses[i].active = -1;
    i++;
  }
}

EXPORT Bool is_varname (string)
     char *string;
{
  char *p;

  if(string == NIL)
    return(FALSE);

  if(string[0] != '+')
    return(FALSE);
  else
    if (string[1] == '\0')
      return(TRUE);

  p = &string[1];


  return (vardef_position (p) > -1);
}

EXPORT int activate_variant (varname) char *varname; {
  char lvarname[64];
  int curi, new_activations = 0, varpos;

  if (varname && (*varname == '+'))
    strcpy (lvarname, varname+1);
  else
    return new_activations;

  curi = 0;
  while (curi < variants_active) {
    if (!strcmp (curvar[curi], lvarname))
      return new_activations;
    curi++;
    if (curi == MAXVARIANTS)
      errexit (37,NIL);
  }

  if ((curvar[curi] = 
       malloc ((unsigned) (strlen (lvarname) + sizeof (char)))) == NIL)
    errexit (10,"malloc");

  varpos = vardef_position (lvarname);
  if ( varpos > -1 ) {
    activeVariants[variants_active] = varpos;
    variants_active++;
    new_activations = 1;
    strcpy (curvar[curi], lvarname);
    attrvar (lvarname, NIL, (Af_set *) NIL);
  }
  
  return new_activations;
}

EXPORT void de_activate (varname)
     char *varname;
{
  register int i = 0, j;
  int varpos = vardef_position (varname);
  struct vardef *this_vardef = (void *)NIL;

  if (varpos < 0) return;
  this_vardef = &variantDefs[varpos];

  while(variantClasses[i].name) {
    if ((variantClasses[i].active != -1) &&
       (!strcmp(variantClasses[i].variants[variantClasses[i].active],
		varname)))
      variantClasses[i].active = -1;
    i++;
  }

  for ( j = 0; j < variants_active; j++ ) {
    if (activeVariants[j] == varpos) {
      register int k;
      for (k = j; k < variants_active; k++) {
	activeVariants[k] = activeVariants[k+1];
      }
      variants_active--;
      activeVariants[variants_active] = -1;
    }
  }
      
  if (this_vardef->vpath) {
    /*
     * The following block (kind of) fixes a bug
     * that leads to eventual corruption of the curvpath
     * variable. However, the fix introduces a new bug:
     * the vpath element of a de_activated variant is
     * completely wiped out, even if another, still
     * active variant has the same vpath. We hope that
     * this case is unlikely.
     */
    register int yucc, urgh; /* yes, YUCC! URGH! */

    for (yucc = 0; curvpath[yucc] && (yucc < MAXVPATH); yucc++) {
      if (strcmp (this_vardef->vpath, curvpath[yucc]))
	continue;
      if (yucc == MAXVPATH-1) {
	curvpath[yucc] = NIL;
      }
      else {
	for (urgh = yucc+1; curvpath[urgh-1]; urgh++)
	  curvpath[urgh-1] = curvpath[urgh];
	yucc--;
      }
    }
  }
}

EXPORT char *active_variants () {
  /*
   * returns a space separated list of all currently active variants.
   * The value is maintained as static memory.
   */

  static sb_ptr varbuf;
  char *av;
  register int i;

  if (!varbuf) varbuf = sbnew (1);
  varbuf = sbinit (varbuf, "", 0);
  for (i = 0; i < variants_active; i++) {
    varbuf = sbcat (varbuf, i ? " +" : "+", 1);
    varbuf = sbcat (varbuf, curvar[i], strlen (curvar[i]));
  }
  av = (av=sbstr (varbuf)) ? check_strdup (av) : av;
  sbfree (varbuf);
  return av;
}

EXPORT void print_variant_class (dest, vcdef_p)
     FILE * dest;
     struct varclass * vcdef_p;
{
  /*
   * print a Shapefile variant class definition to the file pointed to
   * by "dest". "dest" must point to an open FILE. The pointer to 
   * the variant class definition structure shall be non-nil, and shall
   * contain a valid variant class definition.
   */

  register int v_count = 0;
  register char * vn_p;

  if ((vcdef_p == NULL) || (vcdef_p->name == NULL)) return;

  (void) fprintf (dest, "vclass %s ::= (", vcdef_p->name);
  if ((vn_p = vcdef_p->variants[v_count])) {
    (void) fputs (vn_p, dest);
    v_count++;
  }
  while ((vn_p = vcdef_p->variants[v_count])) {
    (void) fprintf (dest, ", %s", vn_p);
    v_count++;
  }
  (void) fputs (")\n", dest);
}

EXPORT void print_variant_definition (dest, vardef_p)
     FILE * dest;
     struct vardef * vardef_p;
{
  /*
   * print the full Shapefile variant definition to the file pointed to
   * by "dest". "dest" must point to an open FILE. The pointer to 
   * the variant definition structure shall be non-nil, and shall
   * contain a valid variant definition.
   */

  
  register int vm_count = 0;
  register char * vm_p;

  if ((vardef_p == NULL) || (vardef_p->name == NULL)) return;

  (void) fprintf (dest, "%s:+\n", vardef_p->name);
  (void) fprintf (dest, "\tvflags = %s\n", 
		  (vm_p = vardef_p->vflags) ? vm_p : "");
  (void) fprintf (dest, "\tvpath = %s\n", 
		  (vm_p = vardef_p->vpath) ? vm_p : "");
  while ((vm_p = vardef_p->vmacros[vm_count])) {
    (void) fprintf (dest, "\t%s\n", vm_p);
    vm_count++;
  }
  fputs ("\n", dest);
}

EXPORT void print_variant_names (destination) FILE * destination;
{
  /*
   * print a list with the names of all known variants to the
   * file pointed to by "destination".
   */

  register int vd_count = 0;
  register char * varname_p;

  (void) fputs ("#\n#\tVariant Definitions\n#\n\n", destination);

  if ((varname_p = variantDefs[vd_count].name)) {
    (void) fputs (varname_p, destination);
    vd_count++;
  };
  while ((varname_p = variantDefs[vd_count].name)) {
    (void) fputs (" ", destination);
    (void) fputs (varname_p, destination);
    vd_count++;
  };
  (void) fputs ("\n", destination);
}
  
LOCAL int vardef_position (varname) char *varname; {
  register int k = 0;

  if(varname == NIL)
    return -1;

  while ((k < MAXVARDEFS) && variantDefs[k].name) {
    if (!strcmp(variantDefs[k].name, varname))
      return k;
    k++;
  }
  return -1;
}  


LOCAL Bool attrvar(name, value, set)
     char *name;
     /*ARGSUSED*/
     char *value;
     Af_set *set;
{
  /* set variant path & variant flags */
  register int varpos = vardef_position (name), i = 0;
  register char *ind;
  char pathlist[PATH_MAX];
  
  if (check_vclass(name) != TRUE)
    errexit(30,name);

  if (varpos < 0)
    errexit (19, name);
  
  while (curvpath[i] != NIL)
    i++;
    
  if (variantDefs[varpos].vpath != NIL) {
    strcpy (pathlist, variantDefs[varpos].vpath);
        
    while(( ind = strchr(pathlist,':')) != NIL) {
      *ind = '\0';
      if((pathlist[0] == '.') && (pathlist[1] == '.')) {
	if((curvpath[i] = 
	    malloc((unsigned) (strlen(prev_dir) + sizeof(char)))) == NIL)
	  errexit(10,"malloc");
	strcpy(curvpath[i],prev_dir);
      }
      else {
	if ((curvpath[i] = 
	     malloc((unsigned) (strlen(pathlist) + sizeof(char)))) == NIL)
	  errexit(10,"malloc");
	strcpy(curvpath[i],pathlist);
      }
      ind++;
      strcpy(pathlist,ind);
      i++;
    }
    if((pathlist[0] == '.') && (pathlist[1] == '.')) {
      if((curvpath[i] = 
	  malloc((unsigned) (strlen(prev_dir) + sizeof(char)))) == NIL)
	errexit(10,"malloc");
      strcpy(curvpath[i],prev_dir);
    }
    else {
      if ((curvpath[i] =
	   malloc((unsigned) (strlen(pathlist) + sizeof(char)))) == NIL)
	errexit(10,"malloc");
      strcpy(curvpath[i],pathlist);
    }
  }
  return TRUE;
}
