# include "dvomerge.h"

// delete images based on a text table of the target IDs
// * load the delete table file (validate format)
// * load the Images.dat file
// * create an index for imageID (seq = imageIDindex[i])
// * create an index of the imageIDs to be deleted (delete (T/F) = imageDELindex[i])
// * XXX need to find the DIS images for the WRP images to be deleted 
// ** BuildChipMatch
// ** copy ChipMatch, create index
// ** sort index, ChipMatch by ChipMatch
// ** loop over ChipMatch, counting delete/no delete for uniq values

void SortChipMatch (off_t *M, off_t *I, off_t N);
int MarkMosaicsToDelete(Image *image, off_t Nimage, int *deleteIndex);

int dvorepairFixImages (int argc, char **argv) {

  FITS_DB db;  // database handle pointing to input image table

  off_t i, Nimage, Nout, Nindex, *imageIdx, index;
  
  Image *image, *imageOut;

  char *imageFilenameOld = NULL;
  char *imageFilenameNew = NULL;

  if (argc != 3) {
    fprintf (stderr, "USAGE: dvorepair -fix-images (catdir) (deleteList)\n");
    fprintf (stderr, "  catdir : database of interest\n");
    fprintf (stderr, "  deleteList : table from dvorepair giving images to be deleted\n");
    exit (2);
  }

  fprintf (stderr, "is this mode tested?\n");
  exit (2);

  char *catdir = argv[1];
  char *delList = argv[2];

  // load the image data
  ALLOCATE(imageFilenameOld, char, strlen(catdir) + 12);
  sprintf (imageFilenameOld, "%s/Images.dat", catdir);

  ALLOCATE(imageFilenameNew, char, strlen(catdir) + 24);
  sprintf (imageFilenameNew, "%s/Images.dat.fixed", catdir);

  if ((image = LoadImages (&db, imageFilenameOld, &Nimage)) == NULL) {
    fprintf (stderr, "error loading images\n");
    exit (1);
  }
  BuildChipMatch (image, Nimage);

  // generate an index for imageIDs

  // find the full range of the imageID values
  Nindex = 0;
  for (i = 0; i < Nimage; i++) {
    Nindex = MAX(image[i].imageID, Nindex);
  }

  // create the index vector
  ALLOCATE (imageIdx, off_t, (Nindex + 1));
  memset (imageIdx, 0, (Nindex + 1)*sizeof(off_t));

  // assign the imageIDs to the index vector
  for (i = 0; i < Nimage; i++) {
    index = image[i].imageID;
    myAssert(index, "image index cannot be 0");
    myAssert(!imageIdx[index], "stomping on an earlier index");
    imageIdx[index] = i;
  }

  // generate an index of the deletion image
  int *deleteIndex;
  ALLOCATE (deleteIndex, int, Nindex + 1);
  memset (deleteIndex, 0, (Nindex + 1)*sizeof(int));

  int NdeleteIDs;
  int *deleteIDs = ReadDeleteList(delList, &NdeleteIDs);

  for (i = 0; i < NdeleteIDs; i++) {
    index = deleteIDs[i];
    myAssert(index > 0, "image index cannot be <= 0");
    myAssert(index <= Nindex, "delete index out of range of real data");
    myAssert(!deleteIndex[index], "stomping on an earlier index");
    deleteIndex[index] = TRUE;
  }

  MarkMosaicsToDelete(image, Nimage, deleteIndex);

  // now create a new output Image table with only the non-deletions
  ALLOCATE (imageOut, Image, Nimage);
  
  Nout = 0;
  for (i = 0; i < Nimage; i++) {
    index = image[i].imageID;
    if (deleteIndex[index]) continue;
    imageOut[Nout] = image[i];
    Nout++;
  }

  SaveImages(&db, imageFilenameNew, imageOut, Nout);

  free (imageFilenameOld);
  free (imageFilenameNew);
  free (imageIdx);
  free (deleteIndex);
  free (deleteIDs);

  exit (0);
}

// sort two times vectors and an index by first time vector
void SortChipMatch (off_t *M, off_t *I, off_t N) {

# define SWAPFUNC(A,B){ off_t tmp_m; off_t tmp_i;	\
    tmp_m = M[A]; M[A] = M[B]; M[B] = tmp_m;		\
    tmp_i = I[A]; I[A] = I[B]; I[B] = tmp_i;		\
  }
# define COMPARE(A,B)(M[A] < M[B])
  OHANA_SORT (N, COMPARE, SWAPFUNC);
# undef SWAPFUNC
# undef COMPARE
}

int MarkMosaicsToDelete(Image *image, off_t Nimage, int *deleteIndex) {
  OHANA_UNUSED_PARAM(image);
  OHANA_UNUSED_PARAM(Nimage);
  OHANA_UNUSED_PARAM(deleteIndex);

  myAbort ("this function needs to be redone with a concept to match the old BuildChipMatch");

# if (0)  

  off_t i;
  
  off_t *ChipByMosaicID, *ChipIndex, *mosaicIDs;
  off_t mosaic, Nmosaic, NMOSAIC, chipSeq, chipID, mosaicID;
  int *nChildren, *nToDelete;
  int Na, Nb, Nc;

  /* We now have a table of images to delete by image ID : but only WRP images have been
     marked.  Now find all WRP images for which none/some/all images are to be deleted.
  */

  

  // make a copy of the ChipMatch table (generated by BuildChipMatch, see libdvo/src/mosaic_astrom.c) 
  // generate an associated image index and sort by the mosaic IDs (value of ChipMatch table)
  ALLOCATE(ChipByMosaicID, off_t, Nimage);
  off_t *ChipMatchRaw = GetChipMatch();
  myAssert(ChipMatchRaw, "need to run BuildChipMatch first");

  memcpy (ChipByMosaicID, ChipMatchRaw, sizeof(off_t)*Nimage);
  ALLOCATE (ChipIndex, off_t, Nimage);
  for (i = 0; i < Nimage; i++) {
    ChipIndex[i] = i;
  }
  SortChipMatch(ChipByMosaicID, ChipIndex, Nimage);

  // now count the number of children images with none/some/all marked for deletion
  // we don't know the number of mosaic images, so allocate as we go

  Nmosaic = 0;
  NMOSAIC = 1000;
  ALLOCATE (nChildren, int,   NMOSAIC); memset(&nChildren[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(int));
  ALLOCATE (nToDelete, int,   NMOSAIC); memset(&nToDelete[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(int));
  ALLOCATE (mosaicIDs, off_t, NMOSAIC); memset(&mosaicIDs[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(off_t));

  // scan past the mosaic (-3), unassigned (-2) and non-chip (-1) entries 
  for (i = 0; (i < Nimage) && (ChipByMosaicID[i] == -3); i++);
  Na = i;
  fprintf (stderr, "skipping %d DIS entries\n", Na);

  for (; (i < Nimage) && (ChipByMosaicID[i] == -2); i++);
  Nb = i - Na;
  fprintf (stderr, "skipping %d failed entries\n", Nb);
  
  for (; (i < Nimage) && (ChipByMosaicID[i] == -1); i++);
  Nc = i - Na - Nb;
  fprintf (stderr, "skipping %d single-chip entries\n", Nc);
  
  if (i == Nimage) {
    fprintf (stderr, "there are no mosaic images in this database\n");
    free (nChildren);
    free (nToDelete);
    free (mosaicIDs);
    
    free (ChipIndex);
    free (ChipByMosaicID);
    
    return TRUE;
  }

  // mosaicIDs[] actually contains the sequence in images[] of the mosaic images
  // do not confuse the sequence numbers and the imageID values

  mosaic = ChipByMosaicID[i];
  mosaicIDs[Nmosaic] = mosaic;
  for (; i < Nimage; i++) {
    if (ChipByMosaicID[i] != mosaic) {
      // time for the next entry
      Nmosaic ++;
      if (Nmosaic == NMOSAIC - 1) {
	NMOSAIC += 1000;
	REALLOCATE (nChildren, int,   NMOSAIC); memset(&nChildren[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(int));
	REALLOCATE (nToDelete, int,   NMOSAIC); memset(&nToDelete[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(int));
	REALLOCATE (mosaicIDs, off_t, NMOSAIC); memset(&mosaicIDs[Nmosaic], 0, (NMOSAIC-Nmosaic)*sizeof(off_t));
      }
      mosaic = ChipByMosaicID[i];
      mosaicIDs[Nmosaic] = mosaic;
    }
    nChildren[Nmosaic] ++;
    chipSeq = ChipIndex[i];
    chipID = image[chipSeq].imageID;
    if (deleteIndex[chipID]) {
      nToDelete[Nmosaic] ++;
    }
  }
  Nmosaic ++;

  // XXX test:
  for (i = 0; i < Nmosaic; i++) {
    if (nToDelete[i]) {
      fprintf (stderr, "mosaic %lld (%lld) : %s : %d children : %d to delete\n",
	       (long long) i, (long long) mosaicIDs[i], image[mosaicIDs[i]].name, nChildren[i], nToDelete[i]);
    }

    // mark the mosaic for deletion IFF all children are to be deleted
    if (nToDelete[i] == nChildren[i]) {
      mosaic = mosaicIDs[i];
      mosaicID = image[mosaic].imageID;
      deleteIndex[mosaicID] = TRUE;
    }	    
  }

  free (nChildren);
  free (nToDelete);
  free (mosaicIDs);

  free (ChipIndex);
  free (ChipByMosaicID);

# endif

  return TRUE;
}

int SaveImages(FITS_DB *oldDB, char *filename, Image *imageOut, off_t Nout) {

  int status, Nx, nbytes;
  off_t IDstart;
  FITS_DB db;

  /* setup image table format and lock */
  gfits_db_init (&db);
  db.mode   = oldDB->mode;
  db.format = oldDB->format;
  status    = dvo_image_lock (&db, filename, 3600.0, LCK_XCLD);  // shorter timeout?
  if (!status) {
    fprintf (stderr, "ERROR: failure to lock image catalog %s\n", db.filename);
    exit (1);
  }

  /* load or create the image table */
  myAssert (db.dbstate == LCK_EMPTY, "do not overwrite exiting image table");
  // myAssert (db.format == DVO_FORMAT_PS1_V2, "format mismatch");

  dvo_image_create (&db, GetZeroPoint());
  gfits_copy_header (&oldDB->header, &db.header);
  
  // replace the existing buffer with the image array
  gfits_scan (db.ftable.header, "NAXIS1", "%d", 1,  &Nx);
  gfits_modify (db.ftable.header, "NAXIS2",  OFF_T_FMT, 1, Nout);
  db.ftable.header[0].Naxis[1] = Nout;

  nbytes = gfits_data_size (db.ftable.header);
  db.ftable.datasize = nbytes;

  free (db.ftable.buffer);
  ALLOCATE (db.ftable.buffer, char, MAX (nbytes, 1));
  memcpy (db.ftable.buffer, imageOut, nbytes);
  memset (&db.ftable.buffer[Nx*Nout], ' ', nbytes - Nx*Nout);

  db.nativeOrder = TRUE; // internal representation
  db.scaledValue = TRUE; // internal representation

  gfits_modify (&db.header, "NIMAGES", OFF_T_FMT, 1,  Nout);

  status = gfits_scan (&oldDB->header, "IMAGEID", OFF_T_FMT, 1, &IDstart);
  myAssert(status, "db image table lacks IMAGEID");

  gfits_modify (&db.header, "IMAGEID", OFF_T_FMT, 1,  IDstart);

  dvo_image_save (&db, TRUE);

  dvo_image_unlock (&db);

  gfits_db_free (&db); 

  return TRUE;
}

