/*
 *                **** PRODLIST ****
 *
 * This file contains routines that process ProdList structures
 *
 */

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

#include "prodlist.h"

ProdList *
create_prodlist(int nitems)
{
  ProdList *ret;

  ret = (ProdList *)malloc(sizeof(ProdList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list to be created - get memory for it */
      ret->item = (char **)malloc(nitems * sizeof(char *));
      if (ret->item == NULL)
	{ /* out of memory */
	  free(ret);
	  return NULL;
	}
      ret->desc = (char **)malloc(nitems * sizeof(char *));
      if (ret->desc == NULL)
	{ /* out of memory */
	  free(ret->item);
	  free(ret);
	  return NULL;
	}
    }
  else
    { /* empty list - make pointers NULL */
      ret->item = ret->desc = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdAlphaList *
create_alpha_prodlist(int nitems)
{
  ProdAlphaList *ret;

  ret = (ProdAlphaList *)malloc(sizeof(ProdList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list */
      ret->item = (char **)malloc(nitems * sizeof(char *));
      if (ret->item == NULL)
	{ /* out of memory */
	  free(ret);
	  return NULL;
	}
    }
  else
    { /* empty list - make pointer NULL */
      ret->item = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdNumList *
create_num_prodlist(int nitems)
{
  ProdNumList *ret;

  ret = (ProdNumList *)malloc(sizeof(ProdList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list - get memory for lists */
      ret->item = (long *)malloc(nitems * sizeof(long));
      if (ret->item == NULL)
	{ /* out of memory */
	  free(ret);
	  return NULL;
	}
    }
  else
    { /* empty list - set pointer to NULL */
      ret->item = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdUNumList *
create_unum_prodlist(int nitems)
{
  ProdUNumList *ret;

  ret = (ProdUNumList *)malloc(sizeof(ProdList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list - get memory for lists */
      ret->item = (unsigned long *)malloc(nitems * sizeof(unsigned long));
      if (ret->item == NULL)
	{ /* out of memory */
	  free(ret);
	  return NULL;
	}
    }
  else
    { /* empty list - set pointer to NULL */
      ret->item = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdANumList *
create_anum_prodlist(int nitems)
{
  ProdANumList *ret;

  ret = (ProdANumList *)malloc(sizeof(ProdANumList));
  if (ret == NULL) return NULL;
  if (nitems > 0)
    { /* non-empty list - get memory */
      ret->num = (long *)malloc(nitems * sizeof(long));
      ret->alpha = (char **)malloc(nitems * sizeof(char *));
      if (ret->num == NULL || ret->alpha == NULL)
	{ /* out of memory - return NULL */
	  if (ret->num != NULL) free(ret->num);
	  if (ret->alpha != NULL) free(ret->alpha);
	  free(ret);
	  return NULL;
	}
    }
  else
    { /* empty list - set pointers to NULL */
      ret->num = NULL;
      ret->alpha = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdAPtrList *
create_aptr_prodlist(int nitems)
{
  ProdAPtrList *ret;

  ret = (ProdAPtrList *)malloc(sizeof(ProdList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list - get memory for lists */
      ret->item = (void **)malloc(nitems * sizeof(void *));
      if (ret->item == NULL)
		{ /* out of memory */
	  	free(ret);
	  	return NULL;
		}
      ret->key = (char **)malloc(nitems * sizeof(char *));
      if (ret->key == NULL)
      {
         free(ret->item);
         free(ret);
         return NULL;
      }
    }
  else
    { /* empty list - set pointer to NULL */
      ret->item = NULL;
      ret->key = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

ProdPtrList *
create_ptr_prodlist(int nitems)
{
  ProdPtrList *ret;

  ret = (ProdPtrList *)malloc(sizeof(ProdPtrList));
  if (ret == NULL) return NULL;	/* can't allocate */
  if (nitems > 0)
    { /* non-empty list - get memory for lists */
      ret->item = (void **)malloc(nitems * sizeof(void *));
      if (ret->item == NULL)
		{ /* out of memory */
	  	free(ret);
	  	return NULL;
		}
    }
  else
    { /* empty list - set pointer to NULL */
      ret->item = NULL;
    }
  ret->nitems = 0;
  ret->maxitems = nitems;
  /* set default growth factor */
  if (nitems > 0) ret->delta = nitems;
  else ret->delta = 10;
  return ret;
}

void
free_prodlist(ProdList *list)
{
  if (list == NULL) return;
  clear_prodlist(list);
  if (list->item != NULL) free(list->item);
  if (list->desc != NULL) free(list->desc);
  free(list);
}

void
clear_prodlist(ProdList *list)
{
  int i;

  if (list == NULL) return;

  for (i=0; i < list->nitems; i++)
    {
      if (list->item[i] != NULL) free(list->item[i]);
      if (list->desc[i] != NULL) free(list->desc[i]);
    }
  list->nitems = 0;
}


void
free_alpha_prodlist(ProdAlphaList *list)
{
  if (list == NULL) return;
  clear_alpha_prodlist(list);
  if (list->item != NULL) free(list->item);
  free(list);
}

void
clear_alpha_prodlist(ProdAlphaList *list)
{
  int i;

  if (list == NULL) return;

  for (i=0; i < list->nitems; i++)
    {
      if (list->item[i] != NULL) free(list->item[i]);
    }
  list->nitems = 0;
}

void
free_ptr_prodlist(ProdPtrList *list)
{
  if (list == NULL) return;
  clear_ptr_prodlist(list);
  if (list->item != NULL) free(list->item);
  free(list);
}

void
clear_ptr_prodlist(ProdPtrList *list)
{
  int i;

  if (list == NULL) return;

  for (i=0; i < list->nitems; i++)
    {
      if (list->item[i] != NULL) free(list->item[i]);
    }
  list->nitems = 0;
}

void
free_aptr_prodlist(ProdAPtrList *list)
{
  if (list == NULL) return;
  clear_aptr_prodlist(list);
  if (list->item != NULL) free(list->item);
  if (list->key != NULL) free(list->key);
  free(list);
}

void
clear_aptr_prodlist(ProdAPtrList *list)
{
  int i;

  if (list == NULL) return;

  for (i=0; i < list->nitems; i++)
    {
      if (list->item[i] != NULL) free(list->item[i]);
      if (list->key[i] != NULL) free(list->key[i]);
    }
  list->nitems = 0;
}

void
free_anum_prodlist(ProdANumList *list)
{
  if (list == NULL) return;
  clear_anum_prodlist(list);
  if (list->num != NULL) free(list->num);
  if (list->alpha != NULL) free(list->alpha);
  free(list);
}

void
clear_anum_prodlist(ProdANumList *list)
{
  int i;

  if (list == NULL) return;

  for (i=0; i < list->nitems; i++)
    {
      if (list->alpha[i] != NULL) free(list->alpha[i]);
    }
  list->nitems = 0;
}

void
free_num_prodlist(ProdNumList *list)
{
  if (list == NULL) return;
  if (list->item != NULL) free(list->item);
  free(list);
}

void
clear_num_prodlist(ProdNumList *list)
{
  list->nitems = 0;
}

void
free_unum_prodlist(ProdUNumList *list)
{
  if (list == NULL) return;
  if (list->item != NULL) free(list->item);
  free(list);
}

void
clear_unum_prodlist(ProdUNumList *list)
{
  list->nitems = 0;
}

void
display_prodlist(ProdList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %s\t%s\n", i+1, list->item[i], list->desc[i]);
    }
}

void
display_alpha_prodlist(ProdAlphaList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %s\n", i+1, list->item[i]);
    }
}

void
display_ptr_prodlist(ProdPtrList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %x\n", i+1, list->item[i]);
    }
}

void
display_aptr_prodlist(ProdAPtrList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: (%s) %x\n", i+1, list->key[i], list->item[i]);
    }
}

void
display_anum_prodlist(ProdANumList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %ld\t%s\n", i+1, list->num[i], list->alpha[i]);
    }
}

void
display_num_prodlist(ProdNumList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %ld\n", i+1, list->item[i]);
    }
}

void
display_unum_prodlist(ProdUNumList *list)
{
  int i;

  if (list == NULL)
    {
      printf("display_list: Empty list\n");
      return;
    } else printf("%d Items in list:\n", list->nitems);

  for (i=0; i < list->nitems; i++)
    {
      printf("Item %3d: %lu\n", i+1, list->item[i]);
    }
}

ProdList *
resize_prodlist(ProdList *list, int m)
{
  if (list == NULL) return NULL;

  if (list->item != NULL)
    list->item = (char **)realloc(list->item, m * sizeof(char *));
  else list->item = (char **) malloc(m * sizeof(char *));

  if (list->desc != NULL)
    list->desc = (char **)realloc(list->desc, m * sizeof(char *));
  else list->desc = (char **) malloc(m * sizeof(char *));

  return list;
}

void
resize_alpha_prodlist(ProdAlphaList *list, int m)
{
  if (list == NULL) return;

  if (list->item != NULL)
    list->item = (char **)realloc(list->item, m * sizeof(char *));
  else list->item = (char **)malloc(m * sizeof(char *));

  list->maxitems = m;		/* new max items */
}

ProdPtrList *
resize_ptr_prodlist(ProdPtrList *list, int m)
{
  if (list == NULL) return NULL;

  if (list->item != NULL)
    list->item = (void **)realloc(list->item, m * sizeof(void *));
  else list->item = (void **)malloc(m * sizeof(char *));

  list->maxitems = m;		/* new max items */
  return list;
}

ProdAPtrList *
resize_aptr_prodlist(ProdAPtrList *list, int m)
{
  if (list == NULL) return NULL;

  if (list->item != NULL)
  {
    list->item = (void **) realloc(list->item, m * sizeof(void *));
    list->key = (char **) realloc(list->key, m * sizeof(char *));
  }
  else
  {
  	list->item = (void **)malloc(m * sizeof(void *));
  	list->key = (char **)malloc(m * sizeof(char *));
  }

  list->maxitems = m;		/* new max items */
  return list;
}

ProdNumList *
resize_num_prodlist(ProdNumList *list, int m)
{
  if (list == NULL) return NULL;

  if (list->item != NULL)
    list->item = (long *)realloc(list->item, m * sizeof(long));
  else list->item = (long *)malloc(m * sizeof(long));

  list->maxitems = m;
  return list;
}

ProdUNumList *
resize_unum_prodlist(ProdUNumList *list, int m)
{
  if (list == NULL) return NULL;

  if (list->item != NULL)
    list->item =
      (unsigned long *)realloc(list->item, m * sizeof(unsigned long));
  else list->item = (unsigned long *)malloc(m * sizeof(unsigned long));

  list->maxitems = m;
  return list;
}

int
resize_anum_prodlist(ProdANumList *list, int m)
{
  if (list->nitems > m)
    {
      fprintf(stderr, "Attempting to shrink ProdANumList from %d to %d\n",
	      list->nitems, m);
      return 0;
    }

  if (list->num != NULL)
    list->num = (long *) realloc(list->num, sizeof(long) * m);
  else list->num = (long *) malloc(sizeof(long) * m);

  if (list->alpha != NULL)
    list->alpha = (char **) realloc(list->alpha, sizeof(char *) * m);
  else list->alpha = (char **) malloc(sizeof(char *) * m);

  list->maxitems = m;

  return 1;			/* normal return */
}

void
sort_anum_prodlist(ProdANumList *index)
{ /* sort by alphabetic field, as in an index */
  char *tchar;
  int i, j, sw;
  long tnum;

  for (i=0; i < index->nitems; i++)
    { /* upper limit of loop */
      sw = 0;			/* no switches yet */
      for (j=index->nitems-1; j > 0; j--)
	{ /* ripple up from bottom */
	  if (strcmp(index->alpha[j], index->alpha[j-1]) < 0)
	    { /* out of order - swap them */
	      tchar = index->alpha[j]; /* save second */
	      tnum = index->num[j];
	      index->alpha[j] = index->alpha[j-1]; /* move first down */
	      index->num[j] = index->num[j-1];
	      index->alpha[j-1] = tchar;
	      index->num[j-1] = tnum;
	      sw++;
	    }
	} /* end ripple */
    } /* end sort loop */
}

/****************************************************************
 *
 *                **** BSEARCH PTR PRODLIST ****
 *
 * This function performs a binary search of a sorted ProdPtrList
 *
 ****************************************************************/

int
bsearch_ptr_prodlist(ProdPtrList *list, void *ent,
		     int (*strcmp)(void *first, void *second))
{
  int i, ret;
  int low, high;

  if (list == NULL) return -1;

  low = 0;
  high = list->nitems - 1;

  while ((high - low) > 10)
    { /* narrow in until we are really close */
      i = low + (high - low)/2;	/* pick the middle point */
      ret = strcmp(ent, list->item[i]);
      if (ret == 0) return i;	/* found it */
      else if (ret > 0) low = i; /* entry in upper half */
      else high = i;		/* entry in lower half */
    }

  for (i=low; i <= high; i++)
    {
      if ((ret = strcmp(ent, list->item[i])) > 0) continue;
      if (ret == 0) return i;	/* found - return location */
      return -1;		/* not found */
    }
  return -1;			/* not found */
}

/****************************************************************
 *
 *                **** BUPDATE PTR PRODLIST ****
 *
 * This function updates (or inserts if necessary) an entry into
 * a sorted list.
 *
 * This function returns the old entry if upated, otherwise NULL
 *
 ****************************************************************/

void *
bupdate_ptr_prodlist(ProdPtrList *list, void *ent,
		     int (*strcmp)(void *first, void *second))
{
  void *ret = NULL;
  int i, j, k;
  int loc;			/* location in table */
  int high, low;

  if (list == NULL || ent == NULL || strcmp == NULL) return 0;

  loc = bsearch_ptr_prodlist(list, ent, strcmp);

  if (loc >= 0)
    { /* entry already in file - update it */
      ret = list->item[loc];
      list->item[loc] = ent;
      return ret;
    }

  low = 0;
  high = list->nitems - 1;

  while ((high - low) > 10)
    { /* narrow search */
      i = low + (high - low)/2;	/* pick the middle point */
      j = strcmp(ent, list->item[i]);
      if (j == 0)
	{ /* entry found - return old location */
	  ret = list->item[loc];
	  list->item[loc] = ent;
	  return ret;
	}
      else if (j > 0) low = i;	/* in upper half */
      else high = i;		/* in lower half */
    }

  for (i=low; i <= high; i++)
    { /* search until list item >= entry */
      if ((k = strcmp(list->item[i], ent)) < 0) continue;
      if (k == 0)
	{ /* entry found - replace and return old version */
	  ret = list->item[loc];
	  list->item[loc] = ent;
	  return ret;
	}

      /* not found, but should be inserted here */
      if (list->nitems >= list->maxitems)
	{ /* not enough room - expand it */
	  resize_ptr_prodlist(list, list->maxitems + list->delta);
	}

      /* shift remaining entries down */
      for (j=list->nitems-1; j >= i; j--) list->item[j+1] = list->item[j];
      list->nitems++;		/* one more entry */

      list->item[i] = ent;
      return NULL;		/* inserted */
    }

  /* not found - add to end */
  if (list->nitems >= list->maxitems)
    {
      resize_ptr_prodlist(list, list->maxitems + list->delta);
    }

  list->item[list->nitems++] = ent;

  return NULL;
}


/****************************************************************
 *
 *                **** BSEARCH APTR PRODLIST ****
 *
 * This function performs a binary search of a sorted ProdAPtrList
 *
 ****************************************************************/

int
bsearch_aptr_prodlist(ProdAPtrList *list, char *key)
{
  int i, ret;
  int low, high;

  if (list == NULL) return -1;

  low = 0;
  high = list->nitems - 1;

  while ((high - low) > 10)
    { /* narrow in until we are really close */
      i = low + (high - low)/2;	/* pick the middle point */
      ret = strcmp(key, list->key[i]);
      if (ret == 0) return i;	/* found it */
      else if (ret > 0) low = i; /* entry in upper half */
      else high = i;		/* entry in lower half */
    }

  for (i=low; i <= high; i++)
    {
      if ((ret = strcmp(key, list->key[i])) > 0) continue;
      if (ret == 0) return i;	/* found - return location */
    }
  return -1;			/* not found */
}

/****************************************************************
 *
 *                **** BUPDATE APTR PRODLIST ****
 *
 * This function updates (or inserts if necessary) an entry into
 * a sorted list.
 *
 * This function returns the old entry if upated, otherwise NULL
 *
 ****************************************************************/

void *
bupdate_aptr_prodlist(ProdAPtrList *list, char *key, void *ent)
{
  void *ret = NULL;
  int i, j, k;
  int loc;			/* location in table */
  int high, low;

  if (list == NULL || key == NULL) return 0;

  low = 0;
  high = list->nitems - 1;

  /*
   * Do a binary search until we have reduced the list size
   * then switch to a sequential search
   */
  while ((high - low) > 10)
  { /* narrow search */
    i = low + (high - low)/2;	/* pick the middle point */
	j = strcmp(key, list->key[i]);
    if (j == 0)
    { /* entry found - return old location */
      ret = list->item[i];
	  list->item[i] = ent;
	  return ret;
	}
    if (j > 0) low = i;	/* in upper half */
	else high = i;		/* in lower half */
  } /* end binary search */

  /*
   * The list is now at a small size,
   * so we switch to a sequential search
   */
  for (i=low; i <= high; i++)
  { /* search until list item >= entry */
    if ((k = strcmp(list->key[i], key)) > 0) break;
    if (k == 0)
    { /* entry found - replace and return old version */
	  ret = list->item[i];
	  list->item[i] = ent;
	  return ret;
	}
  }

  /*
   *  not found, but should be inserted at loc. 'i'
   */
   /* make sure we have room in the list for another entry */
  if (list->nitems >= list->maxitems)
  { /* not enough room - expand it */
	resize_aptr_prodlist(list, list->maxitems + list->delta);
  }

  /* shift remaining entries down */
  for (j=list->nitems; j > i; j--) 
  {
    list->item[j] = list->item[j-1];
    list->key[j] = list->key[j-1];
  }
  list->nitems++;		/* one more entry */
     /* insert new entry */
  list->item[i] = ent;
  list->key[i] = key;

  return NULL;		/* inserted */
}

void
add_ptr_prodlist(ProdPtrList *list, void *ent)
{
	if (list->nitems >= list->maxitems)
	{ /* list full */
		if (list->delta <= 0) list->delta = 10;	/* default delta */
		resize_ptr_prodlist(list, list->maxitems + list->delta);
	}

	list->item[list->nitems++] = ent;
}

void
insert_sorted_alpha_prodlist(ProdAlphaList *list, char *str)
{
	int i, cmp, ins;

	/* find out where to insert this new entry */
	for (i=0; i < list->nitems; i++)
	{
		cmp = strcmp(str, list->item[i]);
		if (cmp == 0) return;	/* already in list */
		if (cmp < 0) break;		/* insert here */
	}
	ins = i;	/* where to insert */

	/* make sure we have enough room */
	if (list->nitems >= list->maxitems)
		resize_alpha_prodlist(list, list->maxitems + 10);

	/* shift higher entries down */
	for (i=list->nitems; i > ins; i--)
	{
		list->item[i] = list->item[i-1];	/* shift up one slot */
	}
	list->nitems++;		/* one more entry */

	list->item[ins] = strdup(str);	/* insert new item */
}
