# include "dvo2.h"

/* variables which describe the skydb table */
char *dBFile = (char *) NULL;
FILE *f;
Header header;
Header theader;
Matrix matrix;
FTable table;
SkyRegions *region;
int Nregion;
int lockstate;
int dbstate = LCK_UNLOCK;

SkyRegions *get_regions (int *N) {
  *N = Nregion;
  return (region);
}

int set_regions (SkyRegions *new, int Nnew) {

  int Nbytes;

  /* assign pointers to new data block */
  free (region);
  Nregion = Nnew;
  region = 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 *) region, char, Nbytes);
  table.buffer = (char *) region;
  table.size = Nbytes;

  return (TRUE);
}

int load_db () {

  int Nx, Ny;

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

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

  /* lock database (soft) */
  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 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, "SKY_REGIONS"))     escape (UNLOCK, "ERROR: can't read table");

  /* convert to internal format */
  region = (SkyRegions *) table.buffer;
  fits_scan (table.header, "NAXIS1", "%d", 1, &Nx);  /* double check that Nx matches structure? */
  fits_scan (table.header, "NAXIS2", "%d", 1, &Ny);
  Nregion = Ny;

  if (!ConvertStruct ((char *) region, sizeof (SkyRegions), Nregion, "skyregions"))
    escape (UNLOCK, "ERROR: can't convert data"); 

  return (1);
}

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

  if (!ConvertStruct ((char *) region, sizeof (SkyRegions), Nregion, "skyregions")) 
    escape (UNLOCK, "ERROR: can't convert from native format");

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

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

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

/* save subset in db file */
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], &region[match[i]], sizeof (SkyRegions));
    ConvertStruct ((char *) vtable.buffer[N], sizeof (SkyRegions), 1, "skyregions");
    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 db");
  if (!fits_fwrite_matrix (f, &matrix))   escape (LOCK, "ERROR: can't update db");
  if (!fits_fwrite_Theader (f, &theader)) escape (LOCK, "ERROR: can't update db");
  if (!fits_fwrite_vtable   (f, &vtable)) escape (LOCK, "ERROR: can't update db");

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

int append_db (SkyRegions *new, int Nnew) {

  VTable vtable;

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

  ConvertStruct ((char *) new, sizeof (SkyRegions), Nnew, "skyregions");
  fits_vadd_rows (&vtable, new, Nnew, sizeof(SkyRegions));

  /* 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);
}

int create_db () {

  /* f & dBFile set by load_db */

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

  /* convert to internal format */
  region = (SkyRegions *) table.buffer;
  Nregion = 0;

  return (TRUE);
}

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

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 escape (int mode, char *message) {
  
  if (mode == UNLOCK) close_db ();
  fprintf (stderr, "%s\n", message);
  exit (1);
}
