# 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, Nbytes;
  Header *header;
  char tname[80];

  header = table[0].header;
  fseek (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);
    fseek (f, Nbytes, SEEK_CUR);
    gfits_free_header (header);
  }
  return (TRUE);
}	

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

  int 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 %d, need %d\n", __func__, Nread, gfits_data_min_size (table[0].header));
      gfits_free_table  (table);
      return (FALSE);
    }
    fprintf (stderr, "warning: file missing pad\n");
  }
  table[0].size = Nbytes;
  return (TRUE);
}	

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

  int 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));

  fseek (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",  "%d", 1, Nrows);
  table[0].size = gfits_data_size (table[0].header);

  return (TRUE);
}	

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

  int 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].size = gfits_data_size (table[0].header);
  table[0].pad = table[0].size - 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));

  fseek (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, int, 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, int Nrow, int *row) {

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

  header = table[0].header;
  fseek (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 */
      fseek (f, Nbytes, SEEK_CUR);
      gfits_free_header (header);
      continue;
    }

    /* file pointer is at beginning of desired table data */
    start = ftell (f);
    gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
    gfits_scan (header, "NAXIS2", "%d", 1, &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];
      fseek (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, int, MAX (1, Nrow));    
    for (i = 0; i < Nrow; i++) table[0].row[i] = row[i];
    table[0].size   = gfits_data_size (table[0].header);
    table[0].pad    = table[0].size - Nx*Ny;
    return (TRUE);
  }
}	

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

  int Nbytes;
  char current[80];

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

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

  Nbytes = gfits_data_size (header);
  fseek (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);
    fseek (f, Nbytes, SEEK_CUR);
  }
  return (FALSE);
}
