# include <dvo.h>

off_t getDISentry (e_time start, e_time stop, e_time *startMos, off_t *indexMos, off_t Nmosaic);
void SortDISindex (e_time *S, off_t *I, off_t N);

/* chip-match table: j = ChipMatch[i], images[j] is DIS for images[i] WRP */

static off_t  iDIS = -1;      // DIS entry in ChipMatch[]
static off_t  Ndis = 0;
static off_t  *DISentry = NULL;
static e_time *DIStzero = NULL;
static off_t  *ChipMatch = NULL;

/* what is the currently registered mosaic image? */
off_t GetRegisteredMosaic () {
  return (iDIS);
}

/* given an image array and a current entry of type WRP, find matching DIS & Register it */
int FindMosaicForImage (Image *images, off_t Nimages, off_t entry) {

  int status;

  if (ChipMatch == NULL) {
    status = FindMosaicForImage_TableSearch (images, Nimages, entry);
    return (status);
  }
  status = FindMosaicForImage_MatchSearch (images, Nimages, entry);
  return (status);
}

/* given an image array and a current entry of type WRP, find matching DIS & Register it */
int FindMosaicForImage_TableSearch (Image *images, off_t Nimages, off_t entry) {

  e_time tzero;
  off_t i;

  /* search backwards for image with same time and type DIS */

  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
    /* not a wrp image, do nothing */
    return (TRUE); /* error or not */
  }

  tzero = images[entry].tzero;
  
  for (i = entry - 1; i >= 0; i--) {
    if (images[i].tzero != tzero) continue;
    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;

    /* found a valid match */
    RegisterMosaic (&images[i].coords);
    iDIS = i;
    return (TRUE);
  }

  for (i = entry + 1; i < Nimages; i++) {
    if (images[i].tzero != tzero) continue;
    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;

    /* found a valid match */
    RegisterMosaic (&images[i].coords);
    iDIS = i;
    return (TRUE);
  }
  return (FALSE);
}

/* given an image array and a current entry of type WRP, find matching DIS & Register it */
int FindMosaicForImage_MatchSearch (Image *images, off_t Nimages, off_t entry) {

  off_t N;

  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
    /* not a wrp image, do nothing */
    return (TRUE); /* error or not? */
  }

  if (ChipMatch == NULL) return (FALSE);

  N = ChipMatch[entry];
  if (N < 0) return (FALSE);
  if (N >= Nimages) return (FALSE);

  RegisterMosaic (&images[N].coords);
  iDIS = N;
  return (TRUE);
}

int BuildChipMatch_old (Image *images, off_t Nimages) {

  off_t i, j, NDIS;

  if (DISentry != NULL) free (DISentry);
  if (DIStzero != NULL) free (DIStzero);
  if (ChipMatch != NULL) free (ChipMatch);

  /* find all DIS images, save tzero (& instrument) */
  Ndis = 0;
  NDIS = 100;
  ALLOCATE (DISentry, off_t, NDIS);
  ALLOCATE (DIStzero, e_time, NDIS);

  for (i = 0; i < Nimages; i++) {
    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
    DISentry[Ndis] = i;
    DIStzero[Ndis] = images[i].tzero;
    Ndis ++;
    if (Ndis >= NDIS) {
      NDIS += 100;
      REALLOCATE (DISentry, off_t, NDIS);
      REALLOCATE (DIStzero, e_time, NDIS);
    }
  }

  /* find all matched WRP images */
  ALLOCATE (ChipMatch, off_t, Nimages);
  for (i = 0; i < Nimages; i++) {
    ChipMatch[i] = -1;
    if (strcmp(&images[i].coords.ctype[4], "-WRP")) continue;
    for (j = 0; j < Ndis; j++) {
      if (DIStzero[j] < images[i].tzero) continue;
      if (DIStzero[j] > images[i].tzero + (int) images[i].exptime) continue;
      ChipMatch[i] = DISentry[j];
      goto got_match;
    }

    fprintf (stderr, "WARNING: can't find matching mosaic \n");
    ChipMatch[i] = -2;

  got_match:
    continue;
  }
  return (TRUE);
}

int BuildChipMatch (Image *images, off_t Nimages) {

  off_t i, j, NDIS;

  if (DISentry != NULL) free (DISentry);
  if (DIStzero != NULL) free (DIStzero);
  if (ChipMatch != NULL) free (ChipMatch);

  // allocate containers for DIS indexing
  Ndis = 0;
  NDIS = 100;
  ALLOCATE (DISentry, off_t, NDIS);
  ALLOCATE (DIStzero, e_time, NDIS);

  // find all DIS images, save tzero (& instrument) 
  for (i = 0; i < Nimages; i++) {
    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
    DISentry[Ndis] = i;
    DIStzero[Ndis] = images[i].tzero;
    Ndis ++;
    if (Ndis >= NDIS) {
      NDIS += 100;
      REALLOCATE (DISentry, off_t, NDIS);
      REALLOCATE (DIStzero, e_time, NDIS);
    }
  }

  // sort the index, start, and stop by the start times:
  SortDISindex (DIStzero, DISentry, Ndis);

  /* find all matched WRP images */
  ALLOCATE (ChipMatch, off_t, Nimages);
  for (i = 0; i < Nimages; i++) {
    ChipMatch[i] = -1;
    if (strcmp(&images[i].coords.ctype[4], "-WRP")) continue;

    j = getDISentry (images[i].tzero, images[i].tzero + (int) images[i].exptime, DIStzero, DISentry, Ndis);
    if (j == -1) {
      fprintf (stderr, "WARNING: can't find matching mosaic \n");
      ChipMatch[i] = -2;
      continue;
    }
    if (j >= Nimages) abort();

    ChipMatch[i] = j;
  }
  return (TRUE);
}

off_t getDISentry (e_time start, e_time stop, e_time *startMos, off_t *indexMos, off_t Nmosaic) {

  // use bisection to find the overlapping mosaic

  off_t Nlo, Nhi, N;

  // find the last mosaic before start
  Nlo = 0; Nhi = Nmosaic;
  while (Nhi - Nlo > 10) {
    N = 0.5*(Nlo + Nhi);
    if (startMos[N] < start) {
      Nlo = MAX(N, 0);
    } else {
      Nhi = MIN(N + 1, Nmosaic);
    }
  }

  // check for the matched mosaic starting from Nlo 
  // we may have to go much beyond Nlo since stop is not sorted
  // can we use a sorted version of stop to check when we are beyond the valid range??
  for (N = Nlo; N < Nmosaic; N++) { 
    if (startMos[N] < start) continue;
    if (startMos[N] > stop) return (-1);
    return (indexMos[N]);
  }

  return (-1);
}

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

# define SWAPFUNC(A,B){ e_time tmp_t; off_t tmp_i; \
  tmp_t = S[A]; S[A] = S[B]; S[B] = tmp_t; \
  tmp_i = I[A]; I[A] = I[B]; I[B] = tmp_i; \
}
# define COMPARE(A,B)(S[A] < S[B])

  OHANA_SORT (N, COMPARE, SWAPFUNC);

# undef SWAPFUNC
# undef COMPARE

}

