#include <stdio.h>
#include <string.h>
#include "pslib.h"
#include "papmodule.h"

// Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
// configuration; return the camera configuration
psMetadata *pmCameraFromHeader(const psMetadata *header, // The FITS header
			       const psMetadata *ipprc // The IPP configuration
    )
{
    bool mdStatus = false;		// Metadata lookup status
    psMetadata *instruments = psMetadataLookupMD(&mdStatus, ipprc, "INSTRUMENTS");
    if (mdStatus == false) {
	psError(PS_ERR_IO, false, "Unable to find INSTRUMENTS in the configuration.\n");
	return NULL;
    }

    psMetadata *winner = NULL;		// The instrument configuration whose rule first matches the supplied
					// header

    // Iterate over the instruments
    psMetadataIterator *iterator = psMetadataIteratorAlloc(instruments, PS_LIST_HEAD, NULL); // MD Iterator
    psMetadataItem *instrumentItem = NULL; // Item from the metadata
    while (instrumentItem = psMetadataGetAndIncrement(iterator)) {
	// Open the instrument information
	psTrace(__func__, 3, "Inspecting instrument %s (%s)\n", instrumentItem->name,
		instrumentItem->comment);
	psMetadata *instrument = NULL;	// The instrument metadata
	if (instrumentItem->type == PS_META_META) {
	    instrument = instrumentItem->data.md;
	} else if (instrumentItem->type == PS_META_STR) {
	    psTrace(__func__, 5, "Reading instrument configuration for %s...\n", instrumentItem->name);
	    int badLines = 0;		// Number of bad lines in reading instrument configuration
	    instrument = psMetadataConfigParse(NULL, &badLines, instrumentItem->data.V, true);
	    if (badLines > 0) {
		psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera"
			 "configuration %s\n", badLines, instrumentItem->name);
	    }
	}

	if (! instrument) {
	    psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret instrument configuration for %s (%s)\n",
		     instrumentItem->name, instrumentItem->comment);
	} else {
	    // Read the rule for that instrument
	    psMetadata *rule = psMetadataLookupMD(&mdStatus, instrument, "RULE");
	    if (! mdStatus) {
		psLogMsg(__func__, PS_LOG_WARN, "Unable to read rule for instrument %s (%s)\n",
			 instrumentItem->name, instrumentItem->comment);
	    } else {
		// Apply the rules
		psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule
											          // iterator
		bool match = true;	// The header matches the rule until we determine otherwise
		psMetadataItem *ruleItem = NULL; // Item from the metadata
		while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) {
		    // Check for the existence of the rule
		    psMetadataItem *headerItem = psMetadataLookup((psMetadata*)header, ruleItem->name);
		    if (! headerItem || headerItem->type != ruleItem->type) {
			match = false;
		    } else {
			// Check to see if the rule works
			switch (ruleItem->type) {
			  case PS_META_STR:
			    psTrace(__func__, 8, "Matching %s: '%s' vs '%s'\n", ruleItem->name,
				    ruleItem->data.V, headerItem->data.V);
			    match = (strncmp(ruleItem->data.V, headerItem->data.V,
					     strlen(ruleItem->data.V)) == 0) ? true : false;
			    break;
			  case PS_TYPE_S32:
			  case PS_TYPE_BOOL:
			    psTrace(__func__, 8, "Matching %s: %d vs %d\n", ruleItem->name,
				    ruleItem->data.S32, headerItem->data.S32);
			    if (ruleItem->data.S32 != headerItem->data.S32) {
				match = false;
			    }
			    break;
			  case PS_TYPE_F32:
			    psTrace(__func__, 8, "Matching %s: %f vs %f\n", ruleItem->name,
				    ruleItem->data.F32, headerItem->data.F32);
			    if (ruleItem->data.F32 != headerItem->data.F32) {
				match = false;
			    }
			    break;
			  case PS_TYPE_F64:
			    psTrace(__func__, 8, "Matching %s: %g vs %g\n", ruleItem->name,
				    ruleItem->data.F64, headerItem->data.F64);
			    if (ruleItem->data.F64 != headerItem->data.F64) {
				match = false;
			    }
			    break;
			  default:
			    psLogMsg(__func__, PS_LOG_WARN, "Ignoring invalid type in metadata: %x\n",
				     ruleItem->type);
			}
		    }
		} // Iterating through the rules for an instrument

		if (match) {
		    if (! winner) {
			// This is the first match
			winner = instrument;
			psLogMsg(__func__, PS_LOG_INFO, "FITS header matches instrument %s\n",
				 instrumentItem->name);
		    } else {
			// We have a duplicate match
			psLogMsg(__func__, PS_LOG_WARN,
				 "Additional instrument found that matches the rules: %s\n",
				 instrumentItem->name);
		    }
		}
	    }

	} // Done inspecting the instrument
		    
    } // Done looking at all instruments
    psFree(iterator);

    if (! winner) {
	psError(PS_ERR_IO, true, "Unable to find an instrument that matches input FITS header!\n");
    }

    return winner;
}
