#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <pslib.h>

#include "ppMops.h"
#include "ppTranslateVersion.h"

bool ppMopsWrite(const psArray *detections, const ppMopsArguments *args)
{
    psTrace("ppMops.write", 1, "Writing %ld extensions to %s", detections->n, args->output);

    psFits *fits = psFitsOpen(args->output, "w"); // FITS file
    if (!fits) {
        psError(PS_ERR_IO, false, "Unable to open output file.");
        return false;
    }

    // Primary header
    {
        psMetadata *header = psMetadataAlloc(); // Header to write
        ppTranslateVersionHeader(header);
        psMetadataAddStr(header, PS_LIST_TAIL, "EXP_NAME", 0, "Exposure name", args->exp_name);
        psMetadataAddS64(header, PS_LIST_TAIL, "EXP_ID", 0, "Exposure identifier", args->exp_id);
        psMetadataAddS64(header, PS_LIST_TAIL, "CHIP_ID", 0, "Chip stage identifier", args->chip_id);
        psMetadataAddS64(header, PS_LIST_TAIL, "CAM_ID", 0, "Cam stage identifier", args->cam_id);
        psMetadataAddS64(header, PS_LIST_TAIL, "FAKE_ID", 0, "Fake stage identifier", args->fake_id);
        psMetadataAddS64(header, PS_LIST_TAIL, "WARP_ID", 0, "Warp stage identifier", args->warp_id);
        psMetadataAddS64(header, PS_LIST_TAIL, "DIFF_ID", 0, "Diff stage identifier", args->diff_id);
        psMetadataAddBool(header, PS_LIST_TAIL, "DIFF_POS", 0, "Positive subtraction?", args->positive);
        psMetadataAddF32(header, PS_LIST_TAIL, "MAGZP", 0, "Magnitude zero point", args->zp);
        psMetadataAddF32(header, PS_LIST_TAIL, "MAGZPERR", 0, "Error in magnitude zero point", args->zpErr);
        psMetadataAddF32(header, PS_LIST_TAIL, "ASTRORMS", 0, "RMS of astrometric fit", args->rmsAstrom);
        psMetadataAddStr(header, PS_LIST_TAIL, "OBSCODE", 0, "IAU Observatory code", OBSERVATORY_CODE);

        ppMopsDetections *det = detections->data[0]; // Representative set of detections
        psMetadataAddStr(header, PS_LIST_TAIL, "RA", 0, "Right Ascension of boresight", det->raBoresight);
        psMetadataAddStr(header, PS_LIST_TAIL, "DEC", 0, "Declination of boresight", det->decBoresight);
        psMetadataAddStr(header, PS_LIST_TAIL, "FILTER", 0, "Filter name", det->filter);
        psMetadataAddF32(header, PS_LIST_TAIL, "AIRMASS", 0, "Airmass at boresight", det->airmass);
        psMetadataAddF32(header, PS_LIST_TAIL, "EXPTIME", 0, "Exposure time (sec)", det->exptime);
        psMetadataAddF64(header, PS_LIST_TAIL, "POSANG", 0, "Position angle", det->posangle);
        psMetadataAddF64(header, PS_LIST_TAIL, "ALT", 0, "Altitude of boresight", det->alt);
        psMetadataAddF64(header, PS_LIST_TAIL, "AZ", 0, "Azimuth of boresight", det->az);
        psMetadataAddF64(header, PS_LIST_TAIL, "MJD-OBS", 0, "MJD of observation", det->mjd);

        if (!psFitsWriteBlank(fits, header, NULL)) {
            psError(psErrorCodeLast(), false, "Unable to write primary header");
            return false;
        }
        psFree(header);
    }

    for (int i = 0; i < detections->n; i++) {
        ppMopsDetections *det = detections->data[i]; // Detections for extension
        psTrace("ppMops.write", 1, "Writing extension %d to %s", i, args->output);
        psString hdrName = NULL, psfName = NULL, deteffName = NULL;
        psStringAppend(&hdrName, "%s.hdr", det->component);
        psStringAppend(&psfName, "%s.psf", det->component);
        psStringAppend(&deteffName, "%s.deteff", det->component);

        if (!psFitsWriteBlank(fits, det->header, hdrName)) {
            psError(psErrorCodeLast(), false, "Unable to write header %d", i);
            return false;
        }
        if (!psFitsWriteTableAllColumns(fits, det->psfHeader, det->table, psfName)) {
            psError(psErrorCodeLast(), false, "Unable to write table %d", i);
            return false;
        }
        if (det->deteffHeader && det->deteffTable &&
            !psFitsWriteTableAllColumns(fits, det->deteffHeader, det->deteffTable, deteffName)) {
            psError(psErrorCodeLast(), false, "Unable to write detection efficiency %d", i);
            return false;
        }
        psFree(hdrName);
        psFree(psfName);
        psFree(deteffName);
    }
    psFitsClose(fits);

    psTrace("ppMops.write", 1, "Done writing to %s", args->output);

    return true;
}
