/*
 *
 * $Header: /usr/u/wjr/src/ADT/RCS/image.c,v 1.16 1992/08/20 21:09:19 rucklidg Exp $
 *
 * Copyright (c) 1990, 1991, 1992 Cornell University.  All Rights Reserved.  
 *  
 * Copyright (c) 1991, 1992 Xerox Corporation.  All Rights Reserved.  
 *  
 * Use, reproduction, preparation of derivative works, and distribution
 * of this software is permitted.  Any copy of this software or of any
 * derivative work must include both the above copyright notices of
 * Cornell University and Xerox Corporation and this paragraph.  Any
 * distribution of this software or derivative works must comply with all
 * applicable United States export control laws.  This software is made
 * available AS IS, and XEROX CORPORATION DISCLAIMS ALL WARRANTIES,
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
 * AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
 * LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
 * EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
 * NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGES.
 */

static char rcsid[] = "@(#)$Header: /usr/u/wjr/src/ADT/RCS/vector.c,v 1.16 1992/08/20 21:09:19 rucklidg Exp $";


#include "misc.h"
#include "vector.h"
#include <stdio.h>
#include <ctype.h>


static void *allocVector(int length, int base, unsigned el_size)
{
  register void *vector;
  extern void *calloc();

  
  assert(length > 0);
  vector = calloc(1, length*el_size);	     /* Allocate the vector data area */
  if (vector == (void *) NULL) {
    return((void *) NULL);
  }

  return (vector - base);
}

static void freeVector(void *ptr, int base, unsigned el_size)
{
  /* Free the memory block. This should give the first element. */
  free((void *) (((char *) ptr) + base*((int) el_size)));
}

typedef struct {uchar x;} GenStruct;
vecDefStructVector(GenericStructVector, GenStruct, void *); /* a generic struct vector */

void *vec_New_(VectorType type, int length, int base,
	       int elemsize, int headsize, void *dupVec)
{
  void *vector;
  VectorHeader *newHeader;


  if (length <= 0) {
    return (void *) NULL;
  }
  
  /* Allocate and initialise the header */
  newHeader = (VectorHeader *) malloc(sizeof(* newHeader));
  if (newHeader == (VectorHeader *) NULL) {
    return (void *) NULL;
  }

  newHeader->length = length;
  newHeader->base   = base;
  newHeader->tag    = type;
  newHeader->elemsize = elemsize;
  newHeader->headsize = headsize;

  assert((elemsize == 0)  !=  (type == VECTOR_STRUCT));	/* XOR */

#define ALLOC_VEC(vectype, elemtype)                                                   \
    {                                                                                  \
      vectype newVec;                                                                  \
                                                                                       \
      newVec = (vectype)malloc(sizeof(* newVec));                                      \
      if (newVec == (vectype)NULL) {                                                   \
	free((void *)newHeader);                                                       \
	return((void *)NULL);                                                          \
      }                                                                                \
      vector = allocVector(length, base, sizeof(elemtype));                            \
      if (vector == (void **)NULL) {                                                   \
	free((void *)newHeader);                                                       \
	free((void *)newVec);                                                          \
	return((void *)NULL);                                                          \
      }                                                                                \
      newVec->header = newHeader;                                                      \
      newVec->data = (elemtype *)vector;                                               \
      newVec->userdata.ptr = ((dupVec == NULL)                                         \
			      ? (void *) NULL                                          \
			      : ((vectype) dupVec)->userdata.ptr);                     \
      newHeader->anyvec    = (AnyVector) newVec;                                       \
      return((void *)newVec);                                                          \
      break;                                                                           \
    }
    
  switch (type) {
  case VECTOR_BINARY:   ALLOC_VEC(BinaryVector,     char);
  case VECTOR_GRAY:  ALLOC_VEC(GrayVector,    uchar);
  case VECTOR_SHORT:  ALLOC_VEC(ShortVector,    short);
  case VECTOR_LONG:   ALLOC_VEC(LongVector,     long);
  case VECTOR_FLOAT:  ALLOC_VEC(FloatVector,    float);
  case VECTOR_DOUBLE: ALLOC_VEC(DoubleVector,   double);
  case VECTOR_PTR:    ALLOC_VEC(PtrVector,      void *);
  case VECTOR_RGB:    ALLOC_VEC(RGBVector,      RGB);
  case VECTOR_RGBF:   ALLOC_VEC(RGBFloatVector, RGBFloat);
  case VECTOR_HSV:    ALLOC_VEC(HSVVector,      HSV);

  case VECTOR_STRUCT: {
    GenericStructVector newVec;
    
    newVec = (GenericStructVector) malloc(headsize);
    if (newVec == (GenericStructVector) NULL) {
      free((void *)newHeader);
      return((void *)NULL);
    }
    vector = allocVector(length, base, elemsize);
    if (vector == (void **)NULL) {
      free((void *)newHeader);
      free((void *)newVec);
      return((void *)NULL);
    }

    if (dupVec != NULL) {
      int i;

      for (i = 0; i < headsize; i++) {
	((char *) newVec)[i] = ((char *) dupVec)[i];
      }
    }
    newVec->header = newHeader;
    newVec->data = (GenStruct *)vector;
    return((void *)newVec);
    break;
  }
    
  default: {
    panic("bad vector tag");
  }
  }
#undef ALLOC_VEC

  /*NOTREACHED*/
  return NULL;
}


void vec_Free_(void *vec, VectorType type)
{
  void *vector=0;
  VectorHeader *header=0;
  unsigned el_size=0;


  assert(vec != (void *)NULL);

  /* Pull out the data area, the header, and the size of the element */

#define FREE_VEC(vectype, elemtype)             \
    {                                           \
      vector = (void *)(((vectype)vec)->data);  \
      header  = ((vectype)vec)->header;         \
      el_size = sizeof(elemtype);               \
      break;                                    \
    }

  switch(type) {
  case VECTOR_BINARY:   FREE_VEC(BinaryVector, char);
  case VECTOR_GRAY:  FREE_VEC(GrayVector, uchar);	
  case VECTOR_SHORT:  FREE_VEC(ShortVector, short);
  case VECTOR_LONG:   FREE_VEC(LongVector, long);
  case VECTOR_FLOAT:  FREE_VEC(FloatVector, float);	
  case VECTOR_DOUBLE: FREE_VEC(DoubleVector, double);
  case VECTOR_PTR:    FREE_VEC(PtrVector, void *);
  case VECTOR_RGB:    FREE_VEC(RGBVector, RGB);
  case VECTOR_RGBF:   FREE_VEC(RGBFloatVector, RGBFloat);
  case VECTOR_HSV:    FREE_VEC(HSVVector, HSV);

  case VECTOR_STRUCT: {
    vector = (void *) (((GenericStructVector) vec)->data);
    header = ((GenericStructVector) vec)->header;
    el_size = header->elemsize;
    break;
  }
    
  default: {
    panic("bad vector tag");
  }
  }
#undef FREE_VEC
    
  freeVector(vector, header->base, el_size); /* and get rid of all of them */
  free((void *)vec);
  free((void *)header);
}


void *vec_Dup_(void *vec, VectorHeader *header)
{
  void *dup;
  unsigned el_size=0;
  char *oldbase=0;
  char *newbase=0;

  assert(vec != (void *)NULL);
  assert(header != (VectorHeader *)NULL);

  /* Make a copy. */
  dup = vec_New_(header->tag, header->length, header->base,
		 header->elemsize, header->headsize, vec);
  if (dup == (void *)NULL) {
    return((void *)NULL);
  }

#define DUP_VEC(vectype, elemtype)      \
  {                                    \
    el_size = sizeof(elemtype);        \
    oldbase = (void *) &(((vectype)vec)->data[header->base]);  \
    newbase = (void *) &(((vectype)dup)->data[header->base]);  \
    break;                             \
  }

  switch(header->tag) {
  case VECTOR_BINARY:   DUP_VEC(BinaryVector, char);
  case VECTOR_GRAY:  DUP_VEC(GrayVector, uchar);
  case VECTOR_SHORT:  DUP_VEC(ShortVector, short);
  case VECTOR_LONG:   DUP_VEC(LongVector, long);
  case VECTOR_FLOAT:  DUP_VEC(FloatVector, float);
  case VECTOR_DOUBLE: DUP_VEC(DoubleVector, double);
  case VECTOR_PTR:    DUP_VEC(PtrVector, void *);
  case VECTOR_RGB:    DUP_VEC(RGBVector, RGB);
  case VECTOR_RGBF:   DUP_VEC(RGBFloatVector, RGBFloat);
  case VECTOR_HSV:    DUP_VEC(HSVVector, HSV);

  case VECTOR_STRUCT: {
    el_size = header->elemsize;
    oldbase = ((char *) (((GenericStructVector) vec)->data)) + el_size * header->base;
    newbase = ((char *) (((GenericStructVector) dup)->data)) + el_size * header->base;
    break;
  }
    
  default: {
    panic("bad vector tag");
  }
  }
#undef DUP_VEC
  
  /* Do the copy */
  (void)memcpy(newbase, oldbase, (int)(el_size * header->length));

  return(dup);
}


void vec_Init_(void *vec, VectorHeader *header, void *val)
{
  assert(vec != (void *)NULL);
  assert(header != (VectorHeader *)NULL);

#define INIT_VEC(arraytype, elemtype)               \
  {                                                 \
    elemtype *begin;                                \
    elemtype *end;                                  \
    elemtype  value;                                \
                                                    \
    begin = vecGetStore((arraytype) vec);           \
    end   = begin + vecGetLength((arraytype) vec);  \
    value = *((elemtype *) val);                    \
                                                    \
    while (begin < end)                             \
      *(begin++) = value;                           \
    break;                                          \
  }

  switch(header->tag) {
  case VECTOR_BINARY:   INIT_VEC(BinaryVector, char);
  case VECTOR_GRAY:  INIT_VEC(GrayVector, uchar);
  case VECTOR_SHORT:  INIT_VEC(ShortVector, short);
  case VECTOR_LONG:   INIT_VEC(LongVector, long);
  case VECTOR_FLOAT:  INIT_VEC(FloatVector, float);
  case VECTOR_DOUBLE: INIT_VEC(DoubleVector, double);
  case VECTOR_PTR:    INIT_VEC(PtrVector, void *);
  case VECTOR_RGB:    INIT_VEC(RGBVector, RGB);
  case VECTOR_RGBF:   INIT_VEC(RGBFloatVector, RGBFloat);
  case VECTOR_HSV:    INIT_VEC(HSVVector, HSV);

  case VECTOR_STRUCT: {
    char *begin, *end, *value;
    int elsize, i;

    elsize = header->elemsize;
    begin = ((char *) (((GenericStructVector) vec)->data)) + elsize * header->base;
    end   = begin + elsize * vecGetLength((GenericStructVector) vec);
    value = (char *) val;

    while (begin < end)
      for (i = 0; i < elsize; i++)
	*(begin++) = value[i];
    break;
  }
    
  default: {
    panic("bad vector tag");
  }
  }
#undef INIT_VEC
  
  return;
}


void vec_SetOffset_(void *vec, VectorHeader *header, int base)
{
  int oldbase;

  
  assert(vec != (void *)NULL);
  assert(header != (VectorHeader *)NULL);

  oldbase      = header->base;
  header->base = base;
  
#define REBASE_VEC(vectype)                    \
  { ((vectype) vec)->data += (oldbase - base); \
    break;                                     \
  }
  
  switch(header->tag) {
  case VECTOR_BINARY:   REBASE_VEC(BinaryVector);
  case VECTOR_GRAY:  REBASE_VEC(GrayVector);
  case VECTOR_SHORT:  REBASE_VEC(ShortVector);
  case VECTOR_LONG:   REBASE_VEC(LongVector);
  case VECTOR_FLOAT:  REBASE_VEC(FloatVector);
  case VECTOR_DOUBLE: REBASE_VEC(DoubleVector);
  case VECTOR_PTR:    REBASE_VEC(PtrVector);
  case VECTOR_RGB:    REBASE_VEC(RGBVector);
  case VECTOR_RGBF:   REBASE_VEC(RGBFloatVector);
  case VECTOR_HSV:    REBASE_VEC(HSVVector);

  case VECTOR_STRUCT: {
    ((char *) (((GenericStructVector) vec)->data)) +=
      header->elemsize * (oldbase - base);
    break;
  }
    
  default: {
    panic("bad vector tag");
  }
  }
#undef REBASE_VEC

  return;
}


void vec_Apply_(void *f, void *vec, VectorHeader *header)
{
  int i, i0, im;

  
  assert(vec != (void *)NULL);
  assert(header != (VectorHeader *)NULL);

#define APPLY_VEC(arraytype, elemtype)               \
  {                                                  \
    arraytype vect;                                  \
    elemtype (*func)(elemtype *valp, int i);         \
    elemtype *valp;                                  \
                                                     \
    vect = (arraytype) vec;                          \
    func = (typeof (func)) f;                        \
                                                     \
    i0 = vect->header->base;                         \
    im = i0 + vect->header->length;                  \
                                                     \
    valp = vect->data + i0;                          \
    for (i = i0; i < im; i++)                        \
      (*func)(valp, i);                              \
    break;                                           \
  }

  switch(header->tag) {
  case VECTOR_BINARY:   APPLY_VEC(BinaryVector, char);
  case VECTOR_GRAY:  APPLY_VEC(GrayVector, uchar);
  case VECTOR_SHORT:  APPLY_VEC(ShortVector, short);
  case VECTOR_LONG:   APPLY_VEC(LongVector, long);
  case VECTOR_FLOAT:  APPLY_VEC(FloatVector, float);
  case VECTOR_DOUBLE: APPLY_VEC(DoubleVector, double);
  case VECTOR_PTR:    APPLY_VEC(PtrVector, void *);
  case VECTOR_RGB:    APPLY_VEC(RGBVector, RGB);
  case VECTOR_RGBF:   APPLY_VEC(RGBFloatVector, RGBFloat);
  case VECTOR_HSV:    APPLY_VEC(HSVVector, HSV);

  case VECTOR_STRUCT: {
    GenericStructVector vect = (GenericStructVector) vec;
    void (*func)(char *valp, int i) = (typeof (func)) f;
    char *valp;
    int elemsize = header->elemsize;

    i0 = vect->header->base;
    im = i0 + vect->header->length;

    valp = ((char *) (vect->data))  +  elemsize * i0;
    for (i = i0; i < im; i++, valp += elemsize)
      (*func)(valp, i);
    
    break;
  }

  default: {
    panic("bad vector tag");
  }
  }
#undef APPLY_VEC
  
  return;
}


