
/*** still need to use the values from the header to define the offset values.
     make this a separate function (still divided into Chip/Cell Base versions
     where does PHU go?
***/

psFPA *pmFPAfromHeader (psMetadata *headers, psMetadata *camera) {

    char *extType = NULL;
    psFPA *fpa = NULL;

    extType = psMetadataLookup(camera, "EXT.TYPE");

    if (!strcasecmp (extType, "cell")) {
	fpa = pmFPAfromHeader_CellBase (headers, camera);
	return (fpa);
    } 
    if (!strcasecmp (extType, "chip")) {
	fpa = pmFPAfromHeader_ChipBase (headers, camera);
	return (fpa);
    } 
    fprintf (stderr, "unknown EXT.TYPE value %s\n", extType);
    return (NULL);
}

/** what do we do with the PHU?  **/

psFPA *pmFPAfromHeader_CellBase (psMetadata *headers, psMetadata *camera) {

    char *extID   = NULL;
    char *cellID  = NULL;

    int   nCellMD = psMetadataLookupS32(camera, "NCELL");       // number of cells in camera definition file 
    char *extKey  = psMetadataLookupPtr(camera, "EXT.KEY");     // identifier for a cell
    char *cellFmt = psMetadataLookupPtr(camera, "CELL.FORMAT"); // format used to define the CELL keywords

    psHash *chipsByID  = psHashAlloc ();		// this will store the list of cells for this chip
    psHash *cellsByExt = psHashAlloc ();		// this will store the list of cells for this chip

    psMetadata *cellMD     = NULL;
    psMetadata *header     = NULL;
    psListIterator *cursor = NULL;

    /* construct a hash of the EXT.KEY entries (one per cell) */
    cellsByExt = psHashAlloc ();
    for (int i = 0; i < nCellMD; i++) {
	sprintf (cell, cellFormat, i);
	cellMD = psMetadataLookupPtr(camera, cell);
	cellID = psMetadataLookupPtr(cellMD, "EXT.KEY");
	psHashAdd (cellsByExt, cellID, cellMD);
    }

    /* examine all headers, try to find them in the CAMERA.CELL definition list */
    cursor = psListIteratorAlloc(headers->list, PS_LIST_HEAD);
    while ((header = (psMetadata *)psListGetNext(cursor)) != NULL) {
	extID   = psMetadataLookupPtr(header, extKey);    // look up the EXTNAME keyword (or equiv) 
	cellMD  = psHashLookup (cellsByExt, extID);
	if (cellMD == NULL) {
	    continue;  			/* extension not a valid cell */
	}

	Nreadout = MAX (psMetadataLookupS32 (header, "NAXIS3"), 1);
	cell = psCellAlloc (Nreadout);
	cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "HEADER", PS_META_META, "header", header);
	cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "CELL", PS_META_META, "cell defs", cellMD);

	chipID = psMetadataLookupPtr(cellMD, "CHIP");  // look for the chip value in this cell MD
	cellArray = psHashLookup (chipsByID, chipID);
	if (list == NULL) {
	    cellArray = psArrayAlloc (10);
	    status = psHashAdd(chipsByID, chipID, list);
	}
	status = psArrayAdd (cellArray, cell, 10);
    }
    psFree (cursor);

    chipArray = psArrayFromHash (chipsByID);

    fpa = psFPAAlloc ();
    fpa->chips = psArrayAlloc(chipArray->n);
    for (int i = 0; i < chipArray->n; i++) {
	fpa->chips->data[i] = psChipAlloc ();
	chip = fpa->chips->data[i];
	chip->cells = chipArray->data[i];
    }
    fpa->metadata = psMetadataAdd (fpa->metadata, PS_LIST_TAIL, "CAMERA", PS_META_META, "camera definition data", camera);

    pmFPA_DefineOffsets_CellBase (fpa);
    return (fpa);
}

psFPA *pmFPAfromHeader_ChipBase (psMetadata *headers, psMetadata *camera) {

    char *extID   = NULL;
    char *cellID  = NULL;

    int   nCellMD = psMetadataLookupS32(camera, "NCELL");
    char *extKey  = psMetadataLookupPtr(camera, "EXT.KEY");
    char *cellFmt = psMetadataLookupPtr(camera, "CELL.FMT");

    psHash *chipsByID  = psHashAlloc ();		// this will store the list of cells for this chip
    psHash *cellsByExt = psHashAlloc ();		// this will store the list of cells for this chip

    psMetadata *cellMD     = NULL;
    psMetadata *header     = NULL;
    psListIterator *cursor = NULL;

    /* construct a hash of the EXT.KEY entries (one per chip) */
    chipsByExt = psHashAlloc ();
    for (int i = 0; i < nCellMD; i++) {
	char cell[64];
	sprintf (cell, cellFormat, i);
	cellMD = psMetadataLookupPtr(camera, cell);
	chipID = psMetadataLookupPtr(cellMD, "EXT.KEY");
	cells  = psHashLookup (chipsByExt, chipID);
	if (cells == NULL) {
	    cells = psListAlloc ();
	    psHashAdd (chipsByExt, cellID, cells);
	}
	psListAdd (cells, PS_LIST_TAIL, cellMD);
    }

    chipArray = psArrayAlloc (10);
    chipArray->n = 0;

    /* examine all headers, try to find them in the CAMERA.CELL definition list */
    cursor = psListIteratorAlloc(headers->list, PS_LIST_HEAD);
    while ((header = (psMetadata *)psListGetNext(cursor)) != NULL) {
	extID = psMetadataLookupPtr(header, extKey);
	cells = psHashLookup(chipsByExt, extID);
	if (cells == NULL) {
	    continue;  			/* extension not a valid cell */
	}

	cellArray = psArrayFromList (cells);
	Nreadout = MAX (psMetadataLookupS32 (header, "NAXIS3"), 1);

	chip = psChipAlloc ();
	chipArray = psArrayAdd (chipArray, chip, 10);

	chip->cells = psArrayAlloc(cellArray->n);
	for (i = 0; i < cellArray->n; i++) {
	    chip->cells->data[i] = psCellAlloc (Nreadout);
	    cell = chip->cells->data[i];
	    cell->metadata = psMetadataAdd (cell->metadata, PS_LIST_TAIL, "CELL", PS_META_META, "cell defs", cellArray->data[i]);
	}	    
	chip->metadata = psMetadataAdd (chip->metadata, PS_LIST_TAIL, "HEADER", PS_META_META, "header", header);
    }
    psFree (cursor);
    pmFPA_DefineOffsets_CellBase (fpa);
    return (fpa);
}

/* define the values for fpa->chip.x0,y0, etc */
bool pmFPADefineOffsets_CellBase (psFPA fpa) {

    for (i = 0; i < fpa->chips->n; i++) {
	chip = fpa->chips->data[i];
	chip.x0 = 0;
	chip.y0 = 0;
	
	for (j = 0; j < chip->cells->n; j++) {
	    cell = chip->cells->data[i];

	    header  = psMetadataLookupPtr(cell->metadata, "HEADER");
	    cellMD  = psMetadataLookupPtr(cell->metadata, "CELL.MD");
	    detsec  = pmRegionFromConfig(cellMD, header, "DETSEC");
	    datasec = pmRegionFromConfig(cellMD, header, "DATASEC");

	    xBin = pmConfigLookupS32(cellMD, header, "CCDBIN1");
	    yBin = pmConfigLookupS32(cellMD, header, "CCDBIN2");

	    cell.xP = (datasec.x0 > datasec.x1) ? -1 : 1;
	    cell.yP = (datasec.y0 > datasec.y1) ? -1 : 1;
	    
	    cell.x0 = f(datasec,detsec);
	    cell.y0 = f(datasec,detsec);
	}
    }
}

/* define the values for fpa->chip.x0,y0, etc */
bool pmFPADefineOffsets_ChipBase (psFPA fpa) {

    for (i = 0; i < fpa->chips->n; i++) {
	chip = fpa->chips->data[i];
	chip.x0 = 0;
	chip.y0 = 0;
	
	/** need to get the cell IDs from AMPLIST **/
	header = psMetadataLookupPtr(chip->metadata, "HEADER");

	for (j = 0; j < chip->cells->n; j++) {
	    cell = chip->cells->data[i];

	    cellMD  = psMetadataLookupPtr(cell->metadata, "CELL.MD");
	    detsec  = pmConfigFindRegion(cellMD, header, "DETSEC");
	    datasec = pmConfigFindRegion(cellMD, header, "DATASEC");

	    xBin = pmConfigLookupS32(cellMD, header, "CCDBIN1");
	    yBin = pmConfigLookupS32(cellMD, header, "CCDBIN2");

	    cell.xP = (datasec.x0 > datasec.x1) ? -1 : 1;
	    cell.yP = (datasec.y0 > datasec.y1) ? -1 : 1;
	    
	    cell.x0 = f(datasec,detsec);
	    cell.y0 = f(datasec,detsec);
	}
    }
}

psReadout *pmReadoutLoad (psReadout *input, psFits *f, psCell *cell, int plane) {

    psReadout *readout;
    psRegion *region;

    readout = input;
    if (readout == NULL) {
	readout = psReadoutAlloc();
    }

    region = psRegionFromConfig (cell, header, "READOUT");
    readout->image = psFitsReadImageSection(NULL, f, region, plane);
    // does this read the header again?  if not, it needs the header

    return (readout);
}

psRegion *pmConfigLookupRegion (psMetadata *config, psMetadata *header, char *name) {

    char *source, *string;
    psRegion *region;

    source = psMetadataLookupPtr (config, name);

    if (!strcasecmp (source, "NONE")) {
	region = psRegionAlloc (0, 0, 0, 0); // the NULL section
	return (region);
    }

    if (!strncasecmp (source, "HD:", 7)) {
	char *keyword;
    
	keyword = &source[7]; 
	string = psMetadataLookupPtr (readout[0].metadata, keyword);
	region = psRegionFromString (string);
	psFree (string);
	psFree (source);
	return (region);
    }

    if (!strncasecmp (source, "CF:", 7)) {
	string = &source[7]; 
	region = psRegionFromString (string);
	psFree (source);
	return (region);
    }

    psError ("invalid section");
    return (NULL);

}

psS32 pmConfigLookupS32 (psMetadata *config, psMetadata *header, char *name) {

    char *source, *string;
    psS32 value;

    source = psMetadataLookupPtr (config, name);

    if (!strcasecmp (source, "NONE")) {
	psFree (source);
	return (0);
    }

    if (!strncasecmp (source, "HD:", 7)) {
	char *keyword;
    
	keyword = &source[7]; 
	value = psMetadataLookupS32 (readout[0].metadata, keyword);
	psFree (source);
	return (value);
    }

    if (!strncasecmp (source, "CF:", 7)) {
	value = atoi (&source[7]);
	return (value);
    }

    psError ("invalid key");
    return (0);

}

psF64 pmConfigLookupF64 (psMetadata *config, psMetadata *header, char *name) {

    char *source, *string;
    psF64 value;

    source = psMetadataLookupPtr (config, name);

    if (!strcasecmp (source, "NONE")) {
	psFree (source);
	return (0);
    }

    if (!strncasecmp (source, "HD:", 7)) {
	char *keyword;
    
	keyword = &source[7]; 
	value = psMetadataLookupF64 (readout[0].metadata, keyword);
	psFree (source);
	return (value);
    }

    if (!strncasecmp (source, "CF:", 7)) {
	value = atof (&source[7]);
	return (value);
    }

    psError ("invalid key");
    return (0);

}

void *psMetdataLookupPtr (psMetadata *md, char *key) {

    void *value = NULL;
    psMetadataItem *item = psMetadataLookup (md, key);

    if (item == NULL) {
	return (NULL);
    }

    value = item->data.V;
    psFree (item);

    return (value);
}

psS32 psMetdataLookupS32 (psMetadata *md, char *key) {

    psS32 value;
    psMetadataItem *item = psMetadataLookup (md, key);

    if (item == NULL) {
	return (0.0);
    }

    value = item->data.S32;
    psFree (item);

    return (value);
}

psF64 psMetdataLookupF64 (psMetadata *md, char *key) {

    psF64 value;
    psMetadataItem *item = psMetadataLookup (md, key);

    if (item == NULL) {
	return (0.0);
    }

    value = item->data.F64;
    psFree (item);

    return (value);
}

psArray psArrayAdd (psArray *array, void *value, int delta) {

    if (delta < 1) {
	delta = 5;
    }

    N = array->n;
    if (N >= array->nAlloc) {
	array = psArrayRealloc (array, array->nAlloc + delta);
    }
  
    array->data[N] = value;
    return (array);
}

