# include "psastroInternal.h"
# define NONLIN_TOL 0.001 /* tolerance in pixels */

bool psastroChipAstrom (pmConfig *config) {

    pmChip *chip = NULL;
    pmCell *cell = NULL;
    pmReadout *readout = NULL;

    // select the current recipe
    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
    if (!recipe) {
        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
        return false;
    }

    // select the input data sources
    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
    if (!input) {
        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
        return false;
    }

    pmFPAview *view = pmFPAviewAlloc (0);
    pmFPA *fpa = input->fpa;

    int numGoodChips = 0;               // Number of chips for which astrometry succeeds

    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
        if (!chip->process || !chip->file_exists) { continue; }

        int numGoodCells = 0;           // Number of cells for which astrometry succeeds
        while ((cell = pmFPAviewNextCell (view, fpa, 1)) != NULL) {
            psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
            if (!cell->process || !cell->file_exists) { continue; }
            if (!chip->fromFPA) { continue; }

            // process each of the readouts
            int numGoodRO = 0;          // Number of readouts for which astrometry succeeds
            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
                if (! readout->data_exists) { continue; }

                // select the raw objects for this readout
                psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
                if (rawstars == NULL) { continue; }

                // select the raw objects for this readout
                psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
                if (refstars == NULL) { continue; }

                // the absolute minimum number of stars is 4 (for order = 1)
                if ((rawstars->n < 4) || (refstars->n < 4)) {
                    readout->data_exists = false;
                    psLogMsg ("psastro", 3, "insufficient rawstars (%ld) or refstars (%ld)",
                              rawstars->n, refstars->n);
                    continue;
                }

                // save WCS and analysis metadata in update header
                psMetadata *updates = psMetadataAlloc();

                // XXX update the header with info to reflect the failure
                if (!psastroOneChipGrid (fpa, chip, refstars, rawstars, recipe, updates)) {
                    readout->data_exists = false;
                    psLogMsg ("psastro", 3, "failed to find a solution\n");
                    psFree (updates);
                    continue;
                }
                // XXX update the header with info to reflect the failure
                if (!psastroOneChipFit (fpa, chip, refstars, rawstars, recipe, updates)) {
                    readout->data_exists = false;
                    psLogMsg ("psastro", 3, "failed to find a solution\n");
                    psFree (updates);
                    continue;
                }

                numGoodRO++;

                // write the elapsed time here; this will be updated in psastroMosaicAstrometry, if called
                psMetadataAddF32 (updates, PS_LIST_TAIL, "DT_ASTR", PS_META_REPLACE, "elapsed psastro time", psTimerMark ("psastroAnalysis"));

                pmAstromWriteWCS (updates, fpa, chip, NONLIN_TOL);
                psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_DATA_METADATA, "psastro header stats", updates);
                psFree (updates);

                if (psTraceGetLevel("psastro.dump") > 0) {

                    char *filename = NULL;
                    char *chipname = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");

                    psStringAppend (&filename, "rawstars.ch.%s.dat", chipname);
                    psastroDumpStars (rawstars, filename);
                    psFree (filename);
                    filename = NULL;

                    psStringAppend (&filename, "refstars.ch.%s.dat", chipname);
                    psastroDumpStars (refstars, filename);
                    psFree (filename);
                    filename = NULL;
                }
            }
            if (numGoodRO > 0) {
                numGoodCells++;
            }
        }
        if (numGoodCells > 0) {
            numGoodChips++;
        }
    }

    if (fpa->chips->n == 1 && numGoodChips == 0) {
        psError(PSASTRO_ERR_UNKNOWN, false, "Failed to fit single chip.");
        return false;
    }

    // psastroDumpCorners ("corners.chipAstrom.dat", input->fpa);

# if (0)
    if (!psastroFixChipsTest (config, recipe)) {
        psError(PSASTRO_ERR_UNKNOWN, false, "failed to align problematic chips");
        return false;
    }
# endif

    if (!psastroFixChips (config, recipe)) {
        psError(PSASTRO_ERR_UNKNOWN, false, "failed to align problematic chips");
        return false;
    }

    // psastroDumpCorners ("corners.fixChips.dat", input->fpa);

    psFree (view);
    return true;
}

/* coordinate frame hierachy
   pixels (on a given readout)
   cell
   chip
   FP (focal plane)
   TP (tangent plane)
   sky (ra, dec)
*/
