# include <ohana.h>
# include <gfitsio.h>

int gfits_varlength_column_define (FTable *ftable, VarLengthColumn *def, int column) {

  int i;
  int Nv, Nb;
  char *p1, *p2, *p3;
  char field[81];
  char format[81];
  char tmpline[81];

  // grab the value of TFORMn for this column
  snprintf (field, 80, "TFORM%d", column);
  if (!gfits_scan (ftable->header, field, "%s", 1, format)) return (FALSE);

  // find and remove the max field length element
  p1 = strchr (format, '(');
  p2 = strchr (format, ')');

  if (!p1 || !p2) return (FALSE); // not a valid varlength column -- missing (e_max)
  if (p2 - p1 < 2) return (FALSE); // not a valid varlength column -- contains ()

  def->maxlen = strtol (p1 + 1, &p3, 10);

  if (p3 != p2) return (FALSE); // not a valid varlength column -- (e_max) contains extra chars
  *p1 = 0; // make the format string end here for the rest of the function

  // first char may optionally be 0, 1
  p1 = format;
  if ((*p1 == '0') || (*p1 == '1')) p1 ++;

  // now p1 must be 'P';
  if (*p1 == 0) return (FALSE);
  if ((*p1 != 'P') && (*p1 != 'Q')) return (FALSE);
  def->mode = *p1;

  // next value is the actual varlength column format
  p1 ++;
  if (*p1 == 0) return (FALSE);
  if (*p1 == 'P') { return (FALSE); }

  def->nbytes = 0;
  if (*p1 == 'X') { def->nbytes = 1;  def->format = *p1; }
  if (*p1 == 'L') { def->nbytes = 1;  def->format = *p1; }
  if (*p1 == 'A') { def->nbytes = 1;  def->format = *p1; }
  if (*p1 == 'B') { def->nbytes = 1;  def->format = *p1; }
  if (*p1 == 'I') { def->nbytes = 2;  def->format = *p1; }
  if (*p1 == 'J') { def->nbytes = 4;  def->format = *p1; }
  if (*p1 == 'E') { def->nbytes = 4;  def->format = *p1; }
  if (*p1 == 'D') { def->nbytes = 8;  def->format = *p1; }
  if (*p1 == 'C') { def->nbytes = 8;  def->format = *p1; }
  if (*p1 == 'M') { def->nbytes = 16; def->format = *p1; }
  if (!def->nbytes) { return (FALSE); }
  
  /* scan columns to find column offset */
  def->Nstart = 0;
  for (i = 1; i < column; i++) {
    sprintf (field, "TFORM%d", i);
    gfits_scan (ftable->header, field, "%s", 1, format);
    gfits_bintable_format (format, tmpline, &Nv, &Nb);
    def->Nstart += Nv*Nb;
  }

  // heap_start must be long long so file may be very large
  if (!gfits_scan (ftable->header, "THEAP", "%lld", 1, (long long *) &def->heap_start)) {
    def->heap_start = ftable->header->Naxis[0]*ftable->header->Naxis[1];
  }

  return TRUE;
}

// return the data and length for row 'row' of a variable length column
// is this capable of handling large files (value holding length and offset is an int)?
void *gfits_varlength_column_pointer (FTable *ftable, VarLengthColumn *column, off_t row, off_t *length) {

  void *result;
  off_t offset, Nx;

  if ((column->mode != 'P') && (column->mode != 'Q')) abort();

  // find the values for the specified row
  // the values in the main table for this row and varlength column:
  Nx = ftable->header->Naxis[0];

# define SWAP_WORD \
  tmp = pchar[0]; pchar[0] = pchar[3]; pchar[3] = tmp; \
  tmp = pchar[1]; pchar[1] = pchar[2]; pchar[2] = tmp;

# ifdef BYTE_SWAP
  char *pchar, tmp;
  pchar = &ftable->buffer[row*Nx + column->Nstart];
  SWAP_WORD;
  pchar = &ftable->buffer[row*Nx + column->Nstart + 4];
  SWAP_WORD;
# endif

  if (column->mode == 'P') {
    int *ptr;
    ptr = (int *) &ftable->buffer[row*Nx + column->Nstart];
    *length = ptr[0];
    offset = ptr[1];
  } else if (column->mode == 'Q') {
    off_t *ptr;
    ptr = (off_t *) &ftable->buffer[row*Nx + column->Nstart];
    *length = ptr[0];
    offset = ptr[1];
  } else {
  	// this was actually trapped above
  	abort();
  }

  result = (void *) (ftable->buffer + column->heap_start + offset);
  return result;
}
