# include "imregister.h"
# include "spreg.h"

/* variables which describe the db */
char *dBFile = (char *) NULL;
FILE *f;
Header header;
Header theader;
Matrix matrix;
FTable table;
Spectrum *spectrum;
int Nspectrum;
int lockstate;
int dbstate = LCK_UNLOCK;

/* set the name of the currently active database table 
   be careful: don't change db without closing old db */
int set_db (char *filename) {
  dBFile = filename;
  return (TRUE);
}

/* load the complete db table into memory */
int load_db () {

  int Nx, Ny;

  /* database name must be set first */
  if (dBFile == (char *) NULL) {
    fprintf (stderr, "ERROR: db file is not set\n");
    exit (1);
  }

  /* set locking state */
  lockstate = (output.modify || output.delete) ? LCK_HARD : LCK_SOFT;
  header.buffer = matrix.buffer = theader.buffer = table.buffer = (char *) NULL;

  /* lock & open database */
  f = fsetlockfile (dBFile, 300.0, lockstate, &dbstate);
  if (dbstate == LCK_EMPTY) return (0);
  if (f == (FILE *) NULL) {
    fprintf (stderr, "ERROR: can't set lock on %s\n", dBFile);
    exit (1);
  }

  /* init & load in FITS table data */
  table.header = &theader;
  if (!fits_fread_header (f, &header))                     escape (UNLOCK, "ERROR: can't read primary header"); 
  if (!fits_fread_matrix (f, &matrix, &header))            escape (UNLOCK, "ERROR: can't read primary matrix");
  if (!fits_fread_ftable (f, &table, "SPECTRUM_DATABASE")) escape (UNLOCK, "ERROR: can't read table SPECTRUM_DATABASE");

  /* convert to internal format */
  spectrum = (Spectrum *) table.buffer;
  fits_scan (table.header, "NAXIS1", "%d", 1, &Nx);
  fits_scan (table.header, "NAXIS2", "%d", 1, &Ny);
  Nspectrum = Ny;

  if (!ConvertStruct ((char *) spectrum, sizeof (Spectrum), Nspectrum, "spectrum")) escape (UNLOCK, "ERROR: can't convert to internal format"); 
  return (TRUE);
}

/* save complete db file */
int save_db () {

  if (!ConvertStruct ((char *) spectrum, sizeof (Spectrum), Nspectrum, "spectrum")) escape (UNLOCK, "ERROR: can't convert from native format");

  /* write all spectra to file */
  make_backup (dBFile);
  Fseek (f, 0, SEEK_SET);
  if (!fits_fwrite_header  (f, &header))  escape (LOCK, "ERROR: can't write primary header");
  if (!fits_fwrite_matrix  (f, &matrix))  escape (LOCK, "ERROR: can't write primary matrix");
  if (!fits_fwrite_Theader (f, &theader)) escape (LOCK, "ERROR: can't write table header");
  if (!fits_fwrite_table   (f, &table))   escape (LOCK, "ERROR: can't write table data");

  fits_free_header (&header);
  fits_free_matrix (&matrix);
  fits_free_header (&theader);
  fits_free_table  (&table);

  fclearlockfile (dBFile, f, lockstate, &dbstate);
  return (TRUE);
}

/* write selected rows to db file 
   complete db must already be loaded in memory (table) */
int update_db (int *match, int Nmatch) {

  int i, N, Nx, Ny;
  VTable vtable;

  fits_scan (table.header, "NAXIS1", "%d", 1, &Nx);
  fits_scan (table.header, "NAXIS2", "%d", 1, &Ny);

  /* make empty vtable from table */
  vtable.header = table.header;
  vtable.size = table.size;
  vtable.Nrow = Ny;
  vtable.pad = vtable.size - Nx*Ny;

  /* insert selected rows in vtable */ 
  ALLOCATE (vtable.row, int, Nmatch);
  ALLOCATE (vtable.buffer, char *, Nmatch);
  for (N = i = 0; i < Nmatch; i++) {
    if (match[i] == -1) continue;
    vtable.row[N] = match[i];
    ALLOCATE (vtable.buffer[N], char, Nx);
    memcpy (vtable.buffer[N], &spectrum[match[i]], sizeof (Spectrum));
    ConvertStruct ((char *) vtable.buffer[N], sizeof (Spectrum), 1, "spectrum");
    N++;
  }
  vtable.Nrow = N;

  /* write subset to file */
  make_backup (dBFile);
  Fseek (f, 0, SEEK_SET);
  if (!fits_fwrite_header (f, &header))   escape (LOCK, "ERROR: can't update primary header");
  if (!fits_fwrite_matrix (f, &matrix))   escape (LOCK, "ERROR: can't update primary matrix");
  if (!fits_fwrite_Theader (f, &theader)) escape (LOCK, "ERROR: can't update table header");
  if (!fits_fwrite_vtable   (f, &vtable)) escape (LOCK, "ERROR: can't update table data");

  fits_free_header (&header);
  fits_free_matrix (&matrix);
  fits_free_header (&theader);
  fits_free_vtable (&vtable);
  fits_free_table  (&table);

  fclearlockfile (dBFile, f, lockstate, &dbstate);
  return (TRUE);
}

/* write a row to the end of the existing table 
   file must be open, but need not be in memory */
int append_db (Spectrum *new, int Nnew) {

  VTable vtable;

  /* add error checks! */
  fits_table_to_vtable (&table, &vtable, 0, 0);

  ConvertStruct ((char *) new, sizeof (Spectrum), Nnew, "spectrum");
  fits_vadd_rows (&vtable, new, Nnew, sizeof(Spectrum));

  /* write subset to file */
  Fseek (f, 0, SEEK_SET);
  if (!fits_fwrite_header (f, &header))   escape (LOCK, "ERROR: can't append to db");
  if (!fits_fwrite_matrix (f, &matrix))   escape (LOCK, "ERROR: can't append to db");
  if (!fits_fwrite_Theader (f, &theader)) escape (LOCK, "ERROR: can't append to db");
  if (!fits_fwrite_vtable   (f, &vtable)) escape (LOCK, "ERROR: can't append to db");

  fits_free_header (&header);
  fits_free_matrix (&matrix);
  fits_free_header (&theader);
  fits_free_vtable (&vtable);

  fclearlockfile (dBFile, f, lockstate, &dbstate);
  return (TRUE);
}

/* create a new (empty) db table in memory. 
   dBFile already set with set_db (filename);
   f already set with load_db () */
int create_db () {

  /* define table layout */
  define_table (&header, &matrix, &theader, &table);

  /* convert to internal format */
  spectrum = (Spectrum *) table.buffer;
  Nspectrum = 0;

  return (TRUE);
}

/* close the db files, free header data
   table data remains alloc'ed */
int close_db () {

  fits_free_header (&header);
  fits_free_matrix (&matrix);
  fits_free_header (&theader);

  if (dbstate == LCK_UNLOCK) return (TRUE);
  fclearlockfile (dBFile, f, lockstate, &dbstate);
  return (TRUE);
}  

/* close the db files, free header data
   table data remains alloc'ed 
   clear soft/xcld locks, but keep a hard lock on the db, if it exists */
int close_lock_db () {

  fits_free_header (&header);
  fits_free_matrix (&matrix);
  fits_free_header (&theader);

  if (dbstate == LCK_UNLOCK) return (TRUE);
  fclearlockfile (dBFile, f, LCK_XCLD, &dbstate);
  return (TRUE);
}  

int print_db_status (char *message) {

  int fd;

  if (!output.verbose) return (0);
  if (f == (FILE *) NULL) {
    fprintf (stderr, "%s - db: %s - fd: %d\n", message, "null", -1);
    return (0);
  }
  fd = fileno (f);
  fprintf (stderr, "%s - db: %s - fd: %d\n", message, dBFile, fd);
  return (TRUE);
}

int escape (int mode, char *message) {
  
  if (mode == UNLOCK) close_db ();
  fprintf (stderr, "%s\n", message);
  exit (1);
}

/* return the current table data */
Spectrum *get_spectra (int *N) {
  *N = Nspectrum;
  return (spectrum);
}

/* set the table to the specified data */
int set_spectra (Spectrum *new, int Nnew) {

  int Nbytes;

  /* assign pointers to new data block */
  free (spectrum);
  Nspectrum = Nnew;
  spectrum = new;

  /* update header, table structures */
  fits_modify (table.header, "NAXIS2", "%d", 1, Nnew);
  table.header[0].Naxis[1] = Nnew;
  Nbytes = fits_matrix_size (table.header);

  /* add padding space to buffer */
  REALLOCATE ((char *) spectrum, char, Nbytes);
  table.buffer = (char *) spectrum;
  table.size = Nbytes;

  return (TRUE);
}

