# include <dvo.h>

/* lock the image table */
int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate) {

  int READWRITE;

  // default access control options:
  READWRITE = TRUE;

  // in read-only mode, do not backup or require write access
  if (lockstate == LCK_SOFT) {
    READWRITE = FALSE;
  }

  // do not perform a backup here
  if (!check_file_access (filename, FALSE, READWRITE, TRUE)) return (FALSE);

  db[0].lockstate = lockstate;
  db[0].timeout   = timeout;
  gfits_db_init (db);

  if (!gfits_db_lock (db, filename)) {
    fprintf (stderr, "can't lock image catalog\n");
    return (FALSE);
  }
  return (TRUE);
}

int dvo_image_unlock (FITS_DB *db) {

  mode_t mode;

  gfits_db_close (db);

  /* force permissions to 666 */
  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
  chmod (db[0].filename, mode);
  return (TRUE);
}

/* determine db mode (RAW/MEF) and read the complete table */
int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ) {

  int Naxis, Nread, status;
  char buffer[241];

  /* database name must be set first */
  if (db == NULL) return (FALSE);
  if (db[0].f == NULL) return (FALSE);

  /* test for NAXIS == 2 in line 3 */
  bzero (buffer, 241);
  fseeko (db[0].f, 0, SEEK_SET);
  Nread = fread (buffer, 1, 240, db[0].f);
  if (Nread != 240) {
    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
    return (FALSE);
  }
  Nread = sscanf (&buffer[160], "NAXIS   = %d", &Naxis);
  if (Nread != 1) {
    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
    return (FALSE);
  }
  fseeko (db[0].f, 0, SEEK_SET);

  /* default values */
  db[0].mode = DVO_MODE_MEF;
  db[0].format = DVO_FORMAT_UNDEF;
  if (Naxis == 2) db[0].mode = DVO_MODE_RAW; /* image table can only be RAW or MEF */

  switch (db[0].mode) {
    case DVO_MODE_MEF:
      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_MEF)\n");
      status = gfits_db_load (db);
      break;
    case DVO_MODE_RAW:
      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_RAW)\n");
      status = dvo_image_load_raw (db, VERBOSE, FORCE_READ);
      break;
    default:
      fprintf (stderr, "error getting image mode\n");
      exit (2);
  }
  if (!status) return (FALSE);
  FtableToImage (&db[0].ftable, &db[0].theader, &db[0].format);
  db[0].swapped = TRUE;  /* table has internal byte-order */
  return (TRUE);
}

/* write the complete image table to disk in the requested mode */
int dvo_image_save (FITS_DB *db, int VERBOSE) {

  int status;

  /* convert from internal to requested external format */
  ImageToFtable (&db[0].ftable, &db[0].theader, db[0].format);
  db[0].swapped = FALSE;

  /* write data in appropriate mode */
  switch (db[0].mode) {
    case DVO_MODE_MEF:
    case DVO_MODE_SPLIT:
      status = gfits_db_save (db);
      break;
    case DVO_MODE_RAW:
      status = dvo_image_save_raw (db, VERBOSE);
      break;
    default:
      fprintf (stderr, "error writing image mode\n");
      exit (2);
  }
  return (TRUE);
}

int dvo_image_addrows (FITS_DB *db, Image *new, off_t Nnew) {

  off_t Nimages;

  /* adjust header */
  Nimages = 0;
  gfits_scan (&db[0].header, "NIMAGES", "%lld", 1, (long long *) &Nimages);
  Nimages += Nnew;
  gfits_modify (&db[0].header, "NIMAGES", "%lld", 1, (long long) Nimages);

  gfits_table_to_vtable (&db[0].ftable, &db[0].vtable, 0, 0);
  gfits_vadd_rows (&db[0].vtable, (char *) new, Nnew, sizeof(Image));

  /* check that primary header and table header agree */
  if (Nimages != db[0].theader.Naxis[1]) {
    fprintf (stderr, "header / table length mismatch!\n");
  }
  return (TRUE);
}  

/* write the vtable data to disk in the requested mode */
int dvo_image_update (FITS_DB *db, int VERBOSE) {

  int status;

  /* convert from internal to requested external format */
  /* theader is modified to match output format */
  ImageToVtable (&db[0].vtable, &db[0].theader, db[0].format);
  db[0].swapped = FALSE;

  /* write data in appropriate mode */
  switch (db[0].mode) {
    case DVO_MODE_MEF:
    case DVO_MODE_SPLIT:
      status = gfits_db_update (db);
      break;
    case DVO_MODE_RAW:
      status = dvo_image_update_raw (db, VERBOSE);
      break;
    default:
      fprintf (stderr, "error writing image mode\n");
      exit (2);
  }
  return (TRUE);
}

void dvo_image_create (FITS_DB *db, double ZeroPoint) {

  /* create new header */
  gfits_init_header (&db[0].header);

  /* check the mode */
  switch (db[0].mode) {
    case DVO_MODE_RAW:
      /* make header a fake image */
      db[0].header.bitpix   = 16;
      db[0].header.Naxes    = 2;
      db[0].header.Naxis[0] = 1;
      db[0].header.Naxis[1] = 1;
      gfits_create_header (&db[0].header);
      break;
    case DVO_MODE_MEF:
    case DVO_MODE_SPLIT:
      db[0].header.extend   = TRUE;
      gfits_create_header (&db[0].header);
      db[0].mode = DVO_MODE_MEF;
      break;
    default:
      fprintf (stderr, "invalid output catalog mode\n");
      exit (1);
  }

  /* check the format */
  if (db[0].format == DVO_FORMAT_UNDEF) {
    fprintf (stderr, "invalid output catalog format\n");
    exit (1);
  }

  gfits_create_matrix (&db[0].header, &db[0].matrix);
  gfits_table_set_Image (&db[0].ftable, NULL, 0);

  gfits_modify (&db[0].header, "NIMAGES", "%lld", 1, 0LL);
  gfits_modify (&db[0].header, "ZERO_PT", "%lf", 1, ZeroPoint);

  if (db[0].format == DVO_FORMAT_INTERNAL)  	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "INTERNAL");
  if (db[0].format == DVO_FORMAT_LONEOS)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "LONEOS");
  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "ELIXIR");
  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_0");
  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_1");
  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_1");
  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_2");
  if (db[0].format == DVO_FORMAT_PS1_DEV_3)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_3");
  if (db[0].format == DVO_FORMAT_PS1_V1)          gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_V1");
  if (db[0].format == DVO_FORMAT_PS1_V2)          gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_V2");
  if (db[0].format == DVO_FORMAT_PS1_REF)         gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_REF");
  
  return;
}
