# include <dvo.h>

int AstromOffsetTableMatchChips (Image *images, off_t Nimages, AstromOffsetTable *table) {

  // we have a table of astrometry offset maps.  we want to find the chips that match to
  // each of the maps so we can assign the map to the image.coords.offsetMap entry

  // we have the lookup table of table->imageIDtoTableSeq to find the match by imageID
  
  // we are just going to assume that table->imageIDtoTableSeq is correct and complete
  off_t i;
  for (i = 0; i < Nimages; i++) {
    int imageID = images[i].imageID;
    if (imageID < 0) continue;
    if (imageID > table->MaxImageID) continue;
    
    int seq = table->imageIDtoTableSeq[imageID];
    if (seq < 0) continue;
    if (seq >= table->Nmap) continue; // this one is probably not valid, right?

    images[i].coords.offsetMap = table->map[seq];    
  }
  return (TRUE);
}

AstromOffsetMap *AstromOffsetMapInit (int Nx, int Ny) {

  AstromOffsetMap *map = NULL;
  ALLOCATE (map, AstromOffsetMap, 1);

  map->dX = NAN; 
  map->dY = NAN; 
  map->Nx = Nx; // output map size
  map->Ny = Ny; // output map size
 
  map->keep = TRUE; // output map size

  ALLOCATE (map->dXv, float *, map->Nx);
  ALLOCATE (map->dYv, float *, map->Nx);

  int j, k;
  for (j = 0; j < map->Nx; j++) {
    ALLOCATE (map->dXv[j], float, map->Ny);
    ALLOCATE (map->dYv[j], float, map->Ny);

    for (k = 0; k < map->Ny; k++) {
      map->dXv[j][k] = 0.0;
      map->dYv[j][k] = 0.0;
    }
  }

  return map;
}

void AstromOffsetMapFree (AstromOffsetMap *map) {

  if (!map) return;

  int j;
  for (j = 0; j < map->Nx; j++) {
    if (map->dXv) free (map->dXv[j]);
    if (map->dYv) free (map->dYv[j]);
  }

  free (map->dXv);
  free (map->dYv);

  free (map);

  return;
}

void AstromOffsetMapSetOrder (AstromOffsetMap *map, int Nx, int Ny, Image *image) {

  int j, k;

  // rather than try to figure out how to resize, and free/allocate, lets just free the old arrays and make new ones
  for (j = 0; j < map->Nx; j++) {
    if (map->dXv) free (map->dXv[j]);
    if (map->dYv) free (map->dYv[j]);
  }
  free (map->dXv);
  free (map->dYv);

  map->Nx = Nx; // output map size
  map->Ny = Ny; // output map size
  map->dX = Nx / (float) image->NX;
  map->dY = Ny / (float) image->NY;
 
  map->keep = TRUE; // output map size

  ALLOCATE (map->dXv, float *, map->Nx);
  ALLOCATE (map->dYv, float *, map->Nx);

  for (j = 0; j < map->Nx; j++) {
    ALLOCATE (map->dXv[j], float, map->Ny);
    ALLOCATE (map->dYv[j], float, map->Ny);

    for (k = 0; k < map->Ny; k++) {
      map->dXv[j][k] = 0.0;
      map->dYv[j][k] = 0.0;
    }
  }
  return;
}

AstromOffsetMap *AstromOffsetMapCopy (AstromOffsetMap *map) {

  if (!map) return NULL;

  AstromOffsetMap *tgt = AstromOffsetMapInit (map->Nx, map->Ny);

  tgt->dX = map->dX; 
  tgt->dY = map->dY; 
  tgt->imageID = map->imageID; 
  tgt->tableID = map->tableID; 

  int j, k;
  for (j = 0; j < tgt->Nx; j++) {
    for (k = 0; k < map->Ny; k++) {
      tgt->dXv[j][k] = map->dXv[j][k];
      tgt->dYv[j][k] = map->dYv[j][k];
    }
  }
  return tgt;
}

// copy the data from one to another, assuming a pre-allocated structure
void AstromOffsetMapCopyData (AstromOffsetMap *tgt, AstromOffsetMap *src) {

  myAssert (tgt->Nx == src->Nx, "programming error");
  myAssert (tgt->Ny == src->Ny, "programming error");

  tgt->dX = src->dX; 
  tgt->dY = src->dY; 
  tgt->imageID = src->imageID; 
  tgt->tableID = src->tableID; 

  tgt->keep = src->keep; 

  int j, k;
  for (j = 0; j < tgt->Nx; j++) {
    for (k = 0; k < src->Ny; k++) {
      tgt->dXv[j][k] = src->dXv[j][k];
      tgt->dYv[j][k] = src->dYv[j][k];
    }
  }
  return;
}

void AstromOffsetTableFree (AstromOffsetTable *table) {

  if (!table) return;

  int i, j;
  for (i = 0; i < table->Nmap; i++) {
    for (j = 0; j < table->map[i][0].Nx; j++) {
      free (table->map[i][0].dXv[j]);
      free (table->map[i][0].dYv[j]);
    }
    if (!table->map[i]) continue;
    FREE (table->map[i][0].dXv);
    FREE (table->map[i][0].dYv);
    FREE (table->map[i]);
  }
  FREE (table->imageIDtoTableSeq);
  FREE (table->map);
}

int AstromOffsetTableNewMap (AstromOffsetTable *table, int Nx, int Ny, Image *image) {

  off_t i;
  if (image->imageID > table->MaxImageID) {
    int oldMaxID = table->MaxImageID;
    table->MaxImageID = image->imageID;
    REALLOCATE (table->imageIDtoTableSeq, int, table->MaxImageID + 1);
    for (i = oldMaxID + 1; i < table->MaxImageID + 1; i++) {
      table->imageIDtoTableSeq[i] = -1;
    }
  }
  int seq = table->imageIDtoTableSeq[image->imageID];
  if (seq != -1) {
    // already assigned (but not used)
    AstromOffsetMapSetOrder (table->map[seq], Nx, Ny, image);
    image[0].coords.offsetMap = table->map[seq];    
    return TRUE;
  }

  int Nmap = table->Nmap;

  table->Nmap++;
  REALLOCATE (table->map, AstromOffsetMap *, table->Nmap);

  myAssert (table->imageIDtoTableSeq[image->imageID] == -1, "table IDtoSeq not initiazed or image collision");
  table->imageIDtoTableSeq[image->imageID] = Nmap;
  
  table->map[Nmap] = AstromOffsetMapInit (Nx, Ny);

  table->MaxTableID ++;
  table->map[Nmap][0].tableID = table->MaxTableID;
  table->map[Nmap][0].imageID = image->imageID;
  
  table->map[Nmap][0].dX = Nx / (float) image->NX;
  table->map[Nmap][0].dY = Ny / (float) image->NY;

  image[0].coords.offsetMap = table->map[Nmap];
  return TRUE;    
}

int AstromOffsetTableAddMapFromImage (AstromOffsetTable *table, Image *image) {

  int Nmap = table->Nmap;

  table->Nmap++;
  REALLOCATE (table->map, AstromOffsetMap *, table->Nmap);

  // find the imageID and update the imageIDtoTableSeq allocation if needed
  off_t i;
  if (image->imageID > table->MaxImageID) {
    int oldMaxID = table->MaxImageID;
    table->MaxImageID = image->imageID;
    REALLOCATE (table->imageIDtoTableSeq, int, table->MaxImageID + 1);
    for (i = oldMaxID + 1; i < table->MaxImageID + 1; i++) {
      table->imageIDtoTableSeq[i] = -1;
    }
  }
  myAssert (table->imageIDtoTableSeq[image->imageID] == -1, "table IDtoSeq not initiazed or image collision");
  table->imageIDtoTableSeq[image->imageID] = Nmap;
  
  table->map[Nmap] = image[0].coords.offsetMap;
  return TRUE;    
}

AstromOffsetTable *AstromOffsetTableInit() {

  AstromOffsetTable *table = NULL;
  ALLOCATE (table, AstromOffsetTable, 1);

  table->Nmap = 0;
  ALLOCATE (table->map, AstromOffsetMap *, 1);

  table->MaxTableID = 0;
  table->MaxImageID = 0;

  // generate the index and init values to -1
  ALLOCATE (table->imageIDtoTableSeq, int, 1);
  table->imageIDtoTableSeq[0] = -1;

  return table;
}

void AstromOffsetMapPrint (AstromOffsetMap *map, char *filename) {

  FILE *f = stderr;
  if (strcasecmp(filename, "stderr") && strcasecmp(filename, "stdout")) {
    f = fopen (filename, "w");
    if (!f) {
      fprintf (stderr, "failed to open output file %s\n", filename);
    }
  }

  fprintf (f, "imageID: %d, tableID: %d (dX: %f, dY: %f), keep: %d\n", map->imageID, map->tableID, map->dX, map->dY, map->keep);
  int ix, iy;
  fprintf (f, "dXv map:\n");
  for (ix = 0; ix < map->Nx; ix++) {
    for (iy = 0; iy < map->Ny; iy++) {
      fprintf (f, "%9.5f ", map->dXv[ix][iy]);
    }
    fprintf (f, "\n");
  }
  fprintf (f, "dYv map:\n");
  for (ix = 0; ix < map->Nx; ix++) {
    for (iy = 0; iy < map->Ny; iy++) {
      fprintf (f, "%9.5f ", map->dYv[ix][iy]);
    }
    fprintf (f, "\n");
  }
  if (f != stderr) fclose (f);
  return;
}
