/* @file pmConcepts.h
 * @brief Top-level functions for defining, registering, reading and writing concepts
 *
 * @author Paul Price, IfA
 *
 * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
 * @date $Date: 2008-06-30 00:53:45 $
 * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
 */

#ifndef PM_CONCEPTS_H
#define PM_CONCEPTS_H

#include <pmConfig.h>

/// @addtogroup Concepts Data Abstraction Concepts
/// @{

/// Source for concepts when reading and writing.
///
/// Since some sources become available at different times from others, we need to provide some specificity to
/// reading and writing concepts (or we're forced to wait until everything's available, which we don't want to
/// do).  Concepts may be read from or written to multiple sources at once by OR-ing them.
typedef enum {
    PM_CONCEPT_SOURCE_NONE     = 0x00,  ///< No concepts
    PM_CONCEPT_SOURCE_BLANK    = 0x01,  ///< Blank concepts defined, but not read
    PM_CONCEPT_SOURCE_CELLS    = 0x02,  ///< Concept comes from the camera information
    PM_CONCEPT_SOURCE_DEFAULTS = 0x04,  ///< Concept comes from defaults
    PM_CONCEPT_SOURCE_PHU      = 0x08,  ///< Concept comes from PHU
    PM_CONCEPT_SOURCE_HEADER   = 0x10,  ///< Concept comes from FITS header
    PM_CONCEPT_SOURCE_DATABASE = 0x20,  ///< Concept comes from database
    PM_CONCEPT_SOURCE_ALL      = 0xfe   ///< All concepts (exclude BLANK)
} pmConceptSource;

/// Function to call to parse a concept once it has been read
typedef psMetadataItem* (*pmConceptParseFunc)(const psMetadataItem *concept, ///< Concept to parse
        const psMetadataItem *pattern, ///< Pattern for parsing
                                              pmConceptSource source, ///< Source of concept
        const psMetadata *cameraFormat, ///< Camera format definition
        const pmFPA *fpa, ///< FPA for concept, or NULL
        const pmChip *chip, ///< Chip for concept, or NULL
        const pmCell *cell ///< Cell for concept, or NULL
                                             );

/// Function to call to format a concept for writing
typedef psMetadataItem* (*pmConceptFormatFunc)(const psMetadataItem *concept, ///< Concept to format
                                               pmConceptSource source, ///< Source of concept
        const psMetadata *cameraFormat, ///< Camera format definition
        const pmFPA *fpa, ///< FPA for concept, or NULL
        const pmChip *chip, ///< Chip for concept, or NULL
        const pmCell *cell ///< Cell for concept, or NULL
                                              );

/// A "concept" specification
///
/// Defines the name, default comment, blank value, and functions to parse (after reading) and format (before
/// writing) the concept.
typedef struct
{
    psMetadataItem *blank;              ///< Blank value of concept; also contains the name and comment
    pmConceptParseFunc parse;           ///< Function to call to read the concept
    pmConceptFormatFunc format;         ///< Function to call to write the concept
    bool required;                      ///< Is concept required (throw an error on problems)?
}
pmConceptSpec;

/// Allocator for pmConceptSpec
pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, ///< Blank value; contains the name
                                  pmConceptParseFunc parse, ///< Function to call to parse the concept
                                  pmConceptFormatFunc format, ///< Function to call to format the concept
                                  bool required ///< Is concept required?
                                 );

/// Get whether a particular concept is required
bool pmConceptGetRequired(const char *name, ///< Name of concept
                          pmFPALevel level ///< Level at which concept resides
    );

/// Set whether a particular concept is required
bool pmConceptSetRequired(const char *name, ///< Name of concept
                          pmFPALevel level, ///< Level at which concept resides
                          bool required ///< Whether concept is required or not
    );

/// Register a new concept for parsing and formatting
///
/// Defines a new concept, based on the blank value (with name and default comment), and functions to parse
/// and format the concept.  The new concept is registered at the specified level (FPA, chip or cell).  If the
/// parse function is NULL, then a default parse function is used, which performs minimal parsing.  Similarly
/// for the format function.
bool pmConceptRegister(psMetadataItem *blank, ///< Blank value; contains the name and default comment
                       pmConceptParseFunc parse, ///< Function to call to parse the concept, or NULL
                       pmConceptFormatFunc format, ///< Function to call to format the concept, or NULL
                       bool required,   ///< Is concept required?
                       pmFPALevel level ///< Level at which to store concept in the FPA hierarchy
                      );

/// Get a list of defined concepts for a particular level.
psList *pmConceptsList(pmFPALevel level);

/// Read the concepts for the given set of fpa, chip, cell
///
/// Attempts to read as many concepts as possible from the specified source for the specified FPA, chip and
/// cell.  That is, it will read chip- and cell-level concepts in addition to fpa-level concepts, if the chip
/// and cell are provided.
bool pmConceptsRead(pmFPA *fpa,         ///< FPA for which to read concepts
                    pmChip *chip,       ///< Chip for which to read concepts, or NULL
                    pmCell *cell,       ///< Cell for which to read concepts, or NULL
                    pmConceptSource source, ///< The source of the concepts to read
                    pmConfig *config    ///< Configuration
                   );

/// Set the concepts within the FPA to the blank value
bool pmConceptsBlankFPA(pmFPA *fpa      ///< FPA for which to set blank concepts
                       );

/// Read concepts for an FPA; optionally, read concepts at all lower levels.
///
/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
/// concepts from the specified source.  It also allows concepts to be read at lower levels by iterating over
/// the components.
bool pmConceptsReadFPA(pmFPA *fpa,      ///< FPA for which to read concepts
                       pmConceptSource source, ///< Source for concepts
                       bool propagateDown, ///< Propagate to lower levels?
                       pmConfig *config         ///< Configuration
                      );

/// Write concepts for an FPA; optionally, write concepts at all lower levels.
///
/// This function writes all concepts for the FPA to the specified "source".  It also allows concepts to be
/// written for all lower levels by iterating over the components.
bool pmConceptsWriteFPA(const pmFPA *fpa,     ///< FPA for which to write concepts
                        pmConceptSource source, ///< Source for concepts
                        bool propagateDown, ///< Propagate to lower levels?
                        pmConfig *config        ///< Configuration
                       );

/// Set the concepts within the chip to the blank value
bool pmConceptsBlankChip(pmChip *chip   ///< FPA for which to set blank concepts
                        );

/// Read concepts for a chip; optionally, read concepts at the FPA and cell levels.
///
/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
/// concepts from the specified source.  It also allows concepts to be read at the fpa level (through the
/// parent), and the cell level by iterating over the components.
bool pmConceptsReadChip(pmChip *chip,   ///< Chip for which to read concepts
                        pmConceptSource source, ///< Source for concepts
                        bool propagateUp, ///< Propagate to higher levels?
                        bool propagateDown, ///< Propagate to lower levels?
                        pmConfig *config        ///< Configuration
                       );

/// Write concepts for a chip; optionally, write concepts at the FPA and cell levels.
///
/// This function writes all concepts for the chip to the specified "source".  It also allows concepts to be
/// written for the FPA, and the cell level by iterating over the components.
bool pmConceptsWriteChip(const pmChip *chip,  ///< Chip for which to write concepts
                         pmConceptSource source, ///< Source for concepts
                         bool propagateUp,///< Propagate to higher levels?
                         bool propagateDown, ///< Propagate to lower levels?
                         pmConfig *config       ///< Configuration
                        );

/// Set the concepts within the cell to the blank value
bool pmConceptsBlankCell(pmCell *cell   ///< Cell for which to set blank concepts
                        );

/// Read concepts for a cell; optionally, read concepts for the parents.
///
/// Once concepts should be available for reading at the FPA-level, this function attempts to read the
/// concepts from the specified source.  It also allows concepts to be read at upper levels through the
/// parents (note, it would not read concepts for all chips, but only the parent of this cell).
bool pmConceptsReadCell(pmCell *cell,   ///< Cell for which to read concepts
                        pmConceptSource source, ///< Source for concepts
                        bool propagateUp, ///< Propagate to higher levels?
                        pmConfig *config        ///< Configuration
                       );

/// Write concepts for a cell; optionally, write concepts for the parents.
///
/// This function writes all concepts for the chip to the specified "source".  It also allows concepts to be
/// written for the upper levels through the parents (note, it would not write concepts for all chips, but
/// only the parent of this cell).
bool pmConceptsWriteCell(const pmCell *cell,  ///< FPA for which to write concepts
                         pmConceptSource source, ///< Source for concepts
                         bool propagateUp, ///< Propagate to higher levels?
                         pmConfig *config ///< Configuration
                        );

/// Initialise the concepts system.
///
/// Register the standard concepts, so that concepts may be read and written.  This function is called
/// automatically the first time the concepts functions are used.
bool pmConceptsInit(void);

/// Signifies that the user is done with the concepts system.
///
/// Frees the registered concepts so there is no memory leak when the user checks "persistent" memory.
void pmConceptsDone(void);

/// Copy all the concepts within an FPA to another FPA
///
/// Iterates over all components of the FPA, and copies the concepts metadata from the source to the target.
bool pmFPACopyConcepts(pmFPA *target,   ///< The target FPA
                       const pmFPA *source    ///< The source FPA
                      );

/// Copy the concepts within an FPA to another FPA; optionally recurse to lower levels
bool pmConceptsCopyFPA(pmFPA *target,   ///< Target FPA
                       const pmFPA *source, ///< Source FPA
                       bool chips,      ///< Recurse to chips level?
                       bool cells       ///< Recurse to cells level?
    );

/// Copy the concepts within a chip to another chip; optionally recurse to lower level
bool pmConceptsCopyChip(pmChip *target, ///< Target chip
                        const pmChip *source, ///< Source chip
                        bool cells      ///< Recurse to cells level?
    );

/// Copy the concepts within a cell to another cell
bool pmConceptsCopyCell(pmCell *target, ///< Target cell
                        const pmCell *source ///< Source cell
    );

/// Interpolate a concept name to the actual value
///
/// Concepts enclosed within braces {}, are replaced with the value of the concept
psString pmConceptsInterpolate(const char *input, ///< Input string
                               const pmFPA *fpa, ///< FPA with concept values, or NULL
                               const pmChip *chip, ///< Chip with concept values, or NULL
                               const pmCell *cell ///< Cell with concept values, or NULL
    );

/// Look up a dependency menu to get a concept's value
///
/// Returns a psMetadataItem with the concept value
psMetadataItem *p_pmConceptsDepend(const char *name, ///< Name of concept for which to get dependent value
                                   const psMetadata *menu, ///< Menu in which to look up key
                                   const psMetadata *source, ///< Source metadata with CONCEPT.DEPEND
                                   const pmFPA *fpa, ///< FPA for dependency
                                   const pmChip *chip, ///< Chip for dependency
                                   const pmCell *cell ///< Cell for dependency
    );

/// @}
#endif
