# include "opihi.h"

// NOTE: we refer to elements.Ptr if we do not care about the actual type

static Vector **vectors;
static int     Nvectors;
  
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// this function is NOT thread protected : it is only used in startup and/or shutdown
void InitVectors () {
  Nvectors = 0;
  ALLOCATE (vectors, Vector *, 1);
}

// this function is NOT thread protected : it is only used in startup and/or shutdown
void FreeVectors () {

  int i;

  for (i = 0; i < Nvectors; i++) {
    if (vectors[i][0].elements.Int) {
      free (vectors[i][0].elements.Int);
    }
    free (vectors[i]);
  }
  free (vectors);
}

Vector *InitVector () {
  Vector *vec;

  ALLOCATE (vec, Vector, 1);

  vec[0].type = OPIHI_FLT;    // is a float unless specified otherwise
  ALLOCATE (vec[0].elements.Flt, opihi_flt, 1);

  bzero (vec[0].name, OPIHI_NAME_SIZE);
  vec[0].Nelements = 0;
  return (vec);
}

int IsVector (char *name) {
 
  int i;

  if (name == NULL) return (FALSE);

  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
  if (i == Nvectors) return (FALSE);
  return (TRUE);
}

int IsVectorPtr (Vector *vec) {
 
  int i;

  if (vec == NULL) return (FALSE);

  for (i = 0; (i < Nvectors) && (vectors[i] != vec); i++);
  if (i == Nvectors) return (FALSE);
  return (TRUE);
}

// XXX add TYPE to arguments?
Vector *SelectVector (char *name, int mode, int verbose) {

  int i;

  if (name == NULL) goto error;
  if (ISNUM(name[0])) goto error;
  if (IsBuffer(name)) goto error;

  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
  /* is a new vector */
  if (i == Nvectors) { 
    if (mode == OLDVECTOR) goto error;
    pthread_mutex_lock (&mutex);
    Nvectors ++;
    REALLOCATE (vectors, Vector *, Nvectors);
    vectors[i] = InitVector ();
    strcpy (vectors[i][0].name, name);
    pthread_mutex_unlock (&mutex);
    return (vectors[i]);
  } 
  /* is an old vector */
  if (mode == NEWVECTOR) goto error;
  return (vectors[i]);

 error:
  if (verbose) gprint (GP_ERR, "invalid vector %s\n", name);
  return (NULL);
}
  
/* delete by pointer */
int DeleteVector (Vector *vec) {

  int i, j;

  if (vec == NULL) return (FALSE);

  for (i = 0; (i < Nvectors) && (vec != vectors[i]); i++);
  if (i == Nvectors) return (FALSE);

  if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
  free (vectors[i]);

  pthread_mutex_lock (&mutex);
  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
  Nvectors --;
  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
  pthread_mutex_unlock (&mutex);
  return (TRUE);
}
  
/* delete by name */
int DeleteNamedVector (char *name) {

  int i, j;

  if (name == NULL) return (FALSE);
  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
  if (i == Nvectors) return (FALSE);

  if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
  free (vectors[i]);

  pthread_mutex_lock (&mutex);
  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
  Nvectors --;
  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
  pthread_mutex_unlock (&mutex);
  return (TRUE);
}

/* copy data from in to out - new memory space */
int CopyNamedVector (char *out, char *in) {
  Vector *In, *Out;
  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
  CopyVector (Out, In);
  return (TRUE);
}

int CopyVector (Vector *out, Vector *in) {
  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
  out[0].Nelements = in[0].Nelements;
  if (in[0].elements.Ptr) {
    if (in[0].type == OPIHI_FLT) {
      ALLOCATE (out[0].elements.Flt, opihi_flt, out[0].Nelements);
      memcpy (out[0].elements.Flt, in[0].elements.Flt, out[0].Nelements*sizeof(opihi_flt));
      out[0].type = OPIHI_FLT;
    } else {
      ALLOCATE (out[0].elements.Int, opihi_int, out[0].Nelements);
      memcpy (out[0].elements.Int, in[0].elements.Int, out[0].Nelements*sizeof(opihi_int));
      out[0].type = OPIHI_INT;
    }
  }
  return (TRUE);
}

int MatchVector(Vector *out, Vector *in, char type) {
  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
  out[0].Nelements = in[0].Nelements;
  if (type == OPIHI_FLT) {
    ALLOCATE (out[0].elements.Flt, opihi_flt, out[0].Nelements);
    out[0].type = OPIHI_FLT;
  } else {
    ALLOCATE (out[0].elements.Int, opihi_int, out[0].Nelements);
    out[0].type = OPIHI_INT;
  }
  return (TRUE);
}

// ResetVector (vecx, OPIHI_FLT, MAX (Npts, 1));
int ResetVector (Vector *vec, char type, int Nelements) {

  vec[0].Nelements = Nelements;
  if (type == OPIHI_FLT) {
    REALLOCATE (vec[0].elements.Flt, opihi_flt, Nelements);
    vec[0].type = OPIHI_FLT;
  } else {
    REALLOCATE (vec[0].elements.Int, opihi_int, Nelements);
    vec[0].type = OPIHI_INT;
  }
  return TRUE;
}

// SetVector (vecx, OPIHI_FLT, MAX (Npts, 1));
int SetVector (Vector *vec, char type, int Nelements) {

  vec[0].Nelements = Nelements;
  if (type == OPIHI_FLT) {
    ALLOCATE (vec[0].elements.Flt, opihi_flt, Nelements);
    vec[0].type = OPIHI_FLT;
  } else {
    ALLOCATE (vec[0].elements.Int, opihi_int, Nelements);
    vec[0].type = OPIHI_INT;
  }
  return TRUE;
}

// recast the vector to the specified type
int CastVector (Vector *vec, char type) {

  int i;

  // the trivial case
  if (vec[0].type == type) return TRUE;

  if (type == OPIHI_FLT) {
    opihi_flt *temp;
    ALLOCATE (temp, opihi_flt, vec[0].Nelements);
    opihi_flt *vo = temp;
    opihi_int *vi = vec[0].elements.Int;
    for (i = 0; i < vec[0].Nelements; i++, vo++, vi++) {
      *vo = *vi;
    }
    free (vec[0].elements.Int);
    vec[0].elements.Flt = vo;
    vec[0].type = OPIHI_FLT;
  } else {
    opihi_int *temp;
    ALLOCATE (temp, opihi_int, vec[0].Nelements);
    opihi_int *vo = temp;
    opihi_flt *vi = vec[0].elements.Flt;
    for (i = 0; i < vec[0].Nelements; i++, vo++, vi++) {
      *vo = *vi;
    }
    free (vec[0].elements.Flt);
    vec[0].elements.Int = vo;
    vec[0].type = OPIHI_INT;
  }
  return TRUE;
}

/* move data from in to out - use old memory space */
int MoveNamedVector (char *out, char *in) {
  Vector *In, *Out;
  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
  MoveVector (Out, In);
  return (TRUE);
}

int MoveVector (Vector *out, Vector *in) {
  int i, j;

  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
  out[0].Nelements    = in[0].Nelements;
  out[0].elements.Ptr = in[0].elements.Ptr;
  out[0].type         = in[0].type;

  /* delete vector entry from vector list, if it exists */
  for (i = 0; (i < Nvectors) && (in != vectors[i]); i++);
  if (i == Nvectors) {
    free (in);
    return (TRUE);
  }

  pthread_mutex_lock (&mutex);
  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
  Nvectors --;
  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
  free (in);
  pthread_mutex_unlock (&mutex);
  return (TRUE);
}

int ListVectors () {

  int i;

  if (Nvectors == 0) {
    gprint (GP_ERR, "No defined vectors\n");
    return (FALSE);
  }

  gprint (GP_LOG, "    N       name      size\n");
  for (i = 0; i < Nvectors; i++) {
    if (vectors[i][0].type == OPIHI_FLT) {
      gprint (GP_LOG, "%5d %10s %10d (FLT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
    } else {
      gprint (GP_LOG, "%5d %10s %10d (INT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
    }
  }
  return (TRUE);
}
  
