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

/*********************** fits read table ***********************************/
int gfits_read_ftable (char *filename, FTable *table, char *extname) {

  int status;
  FILE *f;

  f = fopen (filename, "r");
  if (f == NULL) return (FALSE);

  status = gfits_fread_ftable (f, table, extname);
  fclose (f);
  return (status);
}	

/*********************** fits read table ***********************************/
int gfits_fread_ftable (FILE *f, FTable *table, char *extname) {

  int j, found;
  off_t Nbytes;
  Header *header;
  char tname[80];

  header = table[0].header;
  fseeko (f, 0, SEEK_SET);

  found = FALSE;
  for (j = -1; !found; j++) {
    /* load data for this header */
    if (!gfits_load_header (f, header)) return (FALSE);

    /* check if this is the correct extension or not */
    bzero (tname, 80);
    gfits_scan (header, "EXTNAME", "%s", 1, tname);
    if (!strcmp (tname, extname)) {
      if (gfits_fread_ftable_data (f, table)) return (TRUE);
      gfits_free_header (header);
      return (FALSE);
    }

    /* skip to next header */
    Nbytes = gfits_data_size (header);
    fseeko (f, Nbytes, SEEK_CUR);
    gfits_free_header (header);
  }
  return (TRUE);
}	

/*********************** fits read ftable data ***********************************/
int gfits_fread_ftable_data (FILE *f, FTable *table) {

  off_t Nbytes, Nread;
  char string[128];

  /* find buffer size */
  Nbytes = gfits_data_size (table[0].header);
  ALLOCATE (table[0].buffer, char, Nbytes);

  Nread = fread (table[0].buffer, sizeof (char), Nbytes, f);
  if (Nread != Nbytes) {
    snprintf (string, 128, "FITS file is short (%s)", __func__);
    perror (string);
    if (Nread < gfits_data_min_size (table[0].header)) {
      fprintf (stderr, "error: fits read error in %s, read %lld, need %lld\n", __func__, (long long) Nread, (long long) gfits_data_min_size (table[0].header));
      gfits_free_table  (table);
      return (FALSE);
    }
    fprintf (stderr, "warning: file missing pad\n");
  }
  table[0].datasize = Nbytes;
  return (TRUE);
}	

/*********************** fits read ftable data ***********************************/
int gfits_fread_ftable_range (FILE *f, FTable *table, off_t start, off_t Nrows) {

  off_t Nbytes, Nread, Nskip, Nx, Ny;

  /* find disk table size */
  Nx = table[0].header[0].Naxis[0];
  Ny = table[0].header[0].Naxis[1];

  // it is an error to ask for data starting out-of-bounds
  if (start < 0) return (FALSE);
  if (start >= Ny) return (FALSE);
  
  // if we request more data than is available, we will stop at the table end.
  Nrows = MIN (Nrows, Ny - start);

  Nskip = start * Nx;
  Nbytes = Nrows * Nx;
  ALLOCATE (table[0].buffer, char, MAX (Nbytes, 1));

  fseeko (f, Nskip, SEEK_CUR);

  Nread = fread (table[0].buffer, sizeof (char), Nbytes, f);
  if (Nread != Nbytes) {
    if (ferror (f)) {
      perror ("fits read error");
    } else {
      fprintf (stderr, "unexpected eof\n");
    }      
    free (table[0].buffer);
    return (FALSE);
  }

  /* modify structure and header to match actual read rows Ny */
  table[0].header[0].Naxis[1] = Nrows;
  gfits_modify (table[0].header, "NAXIS2",  "%lld", 1, (long long) Nrows);
  table[0].datasize = gfits_data_size (table[0].header);

  return (TRUE);
}	

/*********************** fits read ftable data ***********************************/
int gfits_fread_vtable_range (FILE *f, VTable *table, off_t start, off_t Nrows) {

  off_t i, Nbytes, Nread, Nskip, Nx, Ny;
  char *buffer;

  /* find buffer size */
  Nx = table[0].header[0].Naxis[0];
  Ny = table[0].header[0].Naxis[1];
  table[0].datasize = gfits_data_size (table[0].header);
  table[0].pad = table[0].datasize - Nx*Ny;

  if (start < 0) return (FALSE);
  if (start + Nrows >= Ny) return (FALSE);
  
  Nskip = start * Nx;
  Nbytes = Nrows * Nx;
  ALLOCATE (buffer, char, MAX (Nbytes, 1));

  fseeko (f, Nskip, SEEK_CUR);
  Nread = fread (buffer, sizeof (char), Nbytes, f);
  if (Nread != Nbytes) {
    perror ("fits read error");
    free (buffer);
    return (FALSE);
  }

  ALLOCATE (table[0].row, off_t, MAX (Nrows, 1));
  ALLOCATE (table[0].buffer, char *, MAX (Nrows, 1));
  for (i = 0; i < Nrows; i++) {
    ALLOCATE (table[0].buffer[i], char, MAX (Nx, 1));
    memcpy (table[0].buffer[i], &buffer[i*Nx], Nx);
    table[0].row[i] = start + i;
  }
  free (buffer);

  return (TRUE);
}	

/*********************** fits read virtual table ***********************************/
int gfits_fread_vtable (FILE *f, VTable *table, char *extname, off_t Nrow, off_t *row) {

  off_t i, j;
  off_t Nbytes, Nread;
  off_t start, Nx, Ny, offset;
  Header *header;
  char tname[80];

  header = table[0].header;
  fseeko (f, 0, SEEK_SET);

  for (j = -1; TRUE; j++) {
    /* load data for this header */
    if (!gfits_load_header (f, header)) return (FALSE);

    /* find buffer size */
    Nbytes = gfits_data_size (header);

    /* check if this is the correct extension or not */
    bzero (tname, 80);
    gfits_scan (header, "EXTNAME", "%s", 1, tname);
    if (strcmp (tname, extname)) {
      /* skip to next header */
      fseeko (f, Nbytes, SEEK_CUR);
      gfits_free_header (header);
      continue;
    }

    /* file pointer is at beginning of desired table data */
    start = ftello (f);
    gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
    gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
    for (i = 0; i < Nrow; i++) {
      if (row[i] > Ny) { return (FALSE); }
    }

    ALLOCATE (table[0].buffer, char *, MAX (1, Nrow));
    for (i = 0; i < Nrow; i++) {
      ALLOCATE (table[0].buffer[i], char, MAX (1, Nx));
      offset = start + Nx*row[i];
      fseeko (f, offset, SEEK_SET);
      Nread = fread (table[0].buffer[i], sizeof (char), Nx, f);
      if (Nread != Nx) { 
	perror ("fits read error");
	return (FALSE); 
      }
    }

    table[0].Nrow  = Nrow;
    ALLOCATE (table[0].row, off_t, MAX (1, Nrow));    
    for (i = 0; i < Nrow; i++) table[0].row[i] = row[i];
    table[0].datasize = gfits_data_size (table[0].header);
    table[0].pad      = table[0].datasize - Nx*Ny;
    return (TRUE);
  }
}	

int gfits_fread_header_extname (FILE *f, Header *header, char *extname) {

  off_t Nbytes;
  char current[80];

  fseeko (f, 0, SEEK_SET);
  gfits_fread_header (f, header);

  if (!strcasecmp (extname, "PHU")) return (TRUE);

  Nbytes = gfits_data_size (header);
  fseeko (f, Nbytes, SEEK_CUR);

  while (gfits_fread_header (f, header)) {
    gfits_scan (header, "EXTNAME", "%s", 1, current);
    if (!strcmp (current, extname)) return (TRUE);
    Nbytes = gfits_data_size (header);
    fseeko (f, Nbytes, SEEK_CUR);
  }
  return (FALSE);
}
