SUBROUTINE BDI1_CFIND( MID, HID, ITEM, CREATE, DELETE, CLOC,
     :                       CNDIM, CDIMS, STATUS )
*+
*  Name:
*     BDI1_CFIND

*  Purpose:
*     Locate HDS component for a given item, creating/deleting if required

*  Language:
*     Starlink Fortran

*  Invocation:
*     CALL BDI1_CFIND( MID, HID, ITEM, CREATE, DELETE, CLOC, CNDIM, CDIMS, STATUS ) 

*  Description:
*     Locate HDS component for a given item, creating if required. If the
*     object does not exist and creation is not allowed then CLOC is set
*     to a flag value. The routine returns the shape of the object, whether
*     or not it is created, which is defined by the NDF data model.

*  Arguments:
*     MID = INTEGER (given)
*        Model data object
*     HID = INTEGER (given)
*        HDSfile data object
*     ITEM = CHARACTER*(*) (given)
*        BDI data item 
*     CREATE = LOGICAL (given)
*        Create structures if they don't exist?
*     DELETE = LOGICAL (given)
*        Delete named item?
*     CLOC = CHARACTER*(DAT__SZLOC) (returned)
*        Locator to object matching item. If the item does not exist
*        the CLOC is set to the symbolic value DAT__NOLOC
*     CNDIM = INTEGER (returned)
*        The dimensionality of the object according to the data model. Note
*        that this not necessarily the dimensionality of the actual HDS
*        component
*     CDIMS[] = INTEGER (returned)
*        The dimensions of the object according to the data model. Note
*        that these are not necessarily the dimensions of the actual HDS
*        component
*     STATUS = INTEGER (given and returned)
*        The global status.

*  Examples:
*     {routine_example_text}
*        {routine_example_description}

*  Pitfalls:
*     {pitfall_description}...

*  Notes:
*     {routine_notes}...

*  Prior Requirements:
*     {routine_prior_requirements}...

*  Side Effects:
*     {routine_side_effects}...

*  Algorithm:
*     {algorithm_description}...

*  Accuracy:
*     {routine_accuracy}

*  Timing:
*     {routine_timing}

*  External Routines Used:
*     {name_of_facility_or_package}:
*        {routine_used}...

*  Implementation Deficiencies:
*     {routine_deficiencies}...

*  References:
*     BDI Subroutine Guide : http://www.sr.bham.ac.uk/asterix-docs/Programmer/Guides/bdi.html

*  Keywords:
*     package:bdi, usage:private

*  Copyright:
*     Copyright (C) University of Birmingham, 1995

*  Authors:
*     DJA: David J. Allan (Jet-X, University of Birmingham)
*     {enter_new_authors_here}

*  History:
*     10 Aug 1995 (DJA):
*        Original version.
*     18 Jan 1996 (DJA):
*        Make sure axis values/widths and error quantities are expressed
*        in max( REAL, dataset preferred type )
*     22 Feb 1996 (DJA):
*        Changes in string concatenation for Linux port
*      4 Mar 1996 (DJA):
*        Added Grouping element and deletion facility
*     {enter_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
      
*  Type Definitions:
      IMPLICIT NONE              ! No implicit typing

*  Global Constants:
      INCLUDE 'SAE_PAR'          ! Standard SAE constants
      INCLUDE 'DAT_PAR'

*  Arguments Given:
      INTEGER			MID, HID
      CHARACTER*(*)		ITEM
      LOGICAL			CREATE, DELETE

*  Arguments Returned:
      CHARACTER*(DAT__SZLOC)	CLOC
      INTEGER			CNDIM, CDIMS(*)

*  Status:
      INTEGER 			STATUS             	! Global status

*  Local Variables:
      CHARACTER*(DAT__SZLOC)	ALOC			! AXIS container
      CHARACTER*(DAT__SZLOC)	ELOC			! ERROR container
      CHARACTER*(DAT__SZLOC)	LOC			! Top level locator
      CHARACTER*(DAT__SZLOC)	QLOC			! QUALITY container
      CHARACTER*(DAT__SZTYP)	RTYPE			! Data type for axes
							! and errors
      CHARACTER*(DAT__SZTYP)	TYPE			! Data type
 
      INTEGER			DIMS(DAT__MXDIM)	! Dimensions
      INTEGER			IAX			! An axis number
      INTEGER			NDIM			! Dimensionality

      LOGICAL			ISBIND			! Binned dataset
      LOGICAL			THERE			! Object exists?
*.

*  Check inherited global status.
      IF ( STATUS .NE. SAI__OK ) RETURN

*  Default return value
      CLOC = DAT__NOLOC

*  Extract file locator
      CALL ADI1_GETLOC( HID, LOC, STATUS )

*  Get model dimensions
      CALL ADI_THERE( MID, 'SHAPE', THERE, STATUS )
      IF ( THERE ) THEN
        CALL BDI_GETSHP( MID, DAT__MXDIM, DIMS, NDIM, STATUS )
      ELSE
        NDIM = -1
      END IF

*  Get dimensions and basic type in create mode. If no values replace
*  with nulls in the hope that we can get away with it! These nulls
*  must be trapped by BDI1_CFIND1
      IF ( CREATE ) THEN
        TYPE(1:1) = '_'
        CALL BDI_GETTYP( MID, TYPE(2:), STATUS )
        IF ( STATUS .NE. SAI__OK ) THEN
          CALL ERR_ANNUL( STATUS )
          TYPE = '*unknown*'
        END IF

*    Derive the type for axes and errors. This must be floating point. 
*    Use single precision normalling, but double if that's the maain
*    dataset type
        IF ( TYPE .EQ. '_DOUBLE' ) THEN
          RTYPE = TYPE
        ELSE
          RTYPE = '_REAL'
        END IF

      END IF

*  Object is derived from BinDS?
      CALL ADI_DERVD( MID, 'BinDS', ISBIND, STATUS )

*  Top-level data array
      IF ( ITEM .EQ. 'Data' ) THEN

*    Define dimensions
        CALL BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )

*    Should create structure array object depending on presence
*    of magic flag
        IF ( ISBIND ) THEN
          CALL BDI1_CFIND1( LOC, 'DATA_ARRAY', CREATE, DELETE, TYPE, 
     :                      NDIM, DIMS, THERE, CLOC, STATUS )
        ELSE
          CALL DAT_CLONE( LOC, CLOC, STATUS )
        END IF

*  If not structured objects all subsequent requests will fail
      ELSE IF ( .NOT. ISBIND ) THEN

*    Do nothing unless we have been told to create something which
*    we aren't allowed to do
        IF ( CREATE ) THEN
          STATUS = SAI__ERROR
          CALL MSG_SETC( 'IT', ITEM )
          CALL ERR_REP( ' ', 'Cannot create item ^IT in non-'/
     :                          /'structured output', STATUS )
        END IF

*  Top-level variance
      ELSE IF ( ITEM .EQ. 'Variance' ) THEN

*    Define dimensions
        CALL BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )

        CALL BDI1_CFIND1( LOC, 'VARIANCE', CREATE, DELETE, RTYPE, NDIM,
     :                    DIMS, THERE, CLOC, STATUS )

*  Axis container
      ELSE IF ( ITEM .EQ. 'Axes' ) THEN
 
*    Define dimensions
        CALL BDI1_CFIND0( 1, NDIM, CNDIM, CDIMS )

        CALL BDI1_CFIND1( LOC, 'AXIS', CREATE, DELETE, 'AXIS', 1,
     :                    NDIM, THERE, CLOC, STATUS )

*  Top-level text components whose names are the same as their HDS counterparts
      ELSE IF ( (ITEM.EQ.'Units') .OR. (ITEM.EQ.'Label') .OR. 
     :          (ITEM.EQ.'Title') ) THEN

*    Define dimensions
        CALL BDI1_CFIND0( 0, 0, CNDIM, CDIMS )

*    Does it exist? If not, and we can create it, do so
        CALL DAT_THERE( LOC, ITEM, THERE, STATUS )
        IF ( CREATE .AND. .NOT. THERE ) THEN
          IF ( ITEM .EQ. 'Units' ) THEN
            CALL DAT_NEW0C( LOC, ITEM, 40, STATUS )
          ELSE
            CALL DAT_NEW0C( LOC, ITEM, 80, STATUS )
          END IF
          THERE = (STATUS.EQ.SAI__OK)
        END IF

*    If it now exists, locate it
        IF ( THERE ) THEN
          CALL DAT_FIND( LOC, ITEM, CLOC, STATUS )
        END IF

*  Asymmetric data errors
      ELSE IF ( (ITEM .EQ. 'LoError') .OR. (ITEM .EQ. 'HiError') ) THEN

*    Define dimensions
        CALL BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )

*    Look for container
        CALL BDI1_CFIND1( LOC, 'ERROR', CREATE, .FALSE., 'ERROR', 0, 0,
     :                    THERE, ELOC, STATUS )

*    If present, look for lower or upper component
        IF ( THERE ) THEN
          IF ( ITEM .EQ. 'LoError' ) THEN
            CALL BDI1_CFIND1( ELOC, 'LOWER', CREATE, DELETE, RTYPE, 
     :                        NDIM, DIMS, THERE, CLOC, STATUS )
          ELSE
            CALL BDI1_CFIND1( ELOC, 'UPPER', CREATE, DELETE, RTYPE, 
     :                        NDIM, DIMS, THERE, CLOC, STATUS )
          END IF
          CALL DAT_ANNUL( ELOC, STATUS )

        END IF

*  Axis items
      ELSE IF ( ITEM(1:2) .EQ. 'Ax' ) THEN

*    Look for top-level axis array
        CALL BDI1_CFIND1( LOC, 'AXIS', CREATE, .FALSE., 'AXIS', 1, NDIM, 
     :                    THERE, CLOC, STATUS )

*    User is interested in a particular axis 
        IF ( THERE .AND. (ITEM(1:4) .EQ. 'Axis') ) THEN

*      Get the axis number
          CALL CHR_CTOI( ITEM(6:6), IAX, STATUS )

*      Locate our particular axis
          CALL DAT_CELL( CLOC, 1, IAX, ALOC, STATUS )

*      Free the axis structure array
          CALL DAT_ANNUL( CLOC, STATUS )

*      Define default dimensions for scalars
          CALL BDI1_CFIND0( 0, 0, CNDIM, CDIMS )

*      Switch depending on the axis item required
          IF ( ITEM(8:) .EQ. 'Units' ) THEN

            CALL BDI1_CFIND1( ALOC, 'UNITS', CREATE, DELETE, '_CHAR*40',
     :                        0, 0, THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'Label' ) THEN

            CALL BDI1_CFIND1( ALOC, 'LABEL', CREATE, DELETE, '_CHAR*80', 
     :                        0, 0, THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'Normalised' ) THEN

            CALL BDI1_CFIND1( ALOC, 'NORMALISED', CREATE, DELETE, 
     :                        '_LOGICAL', 0, 0, THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'Data' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( 1, DIMS(IAX), CNDIM, CDIMS )

            CALL BDI1_CFIND1( ALOC, 'DATA_ARRAY', CREATE, DELETE, RTYPE,
     :                        1, DIMS(IAX), THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'Width' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( 1, DIMS(IAX), CNDIM, CDIMS )

            CALL BDI1_CFIND1( ALOC, 'WIDTH', CREATE, DELETE, RTYPE,
     :                        1, DIMS(IAX), THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'LoWidth' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( 1, DIMS(IAX), CNDIM, CDIMS )

            CALL BDI1_CFIND1( ALOC, 'LWIDTH', CREATE, DELETE, RTYPE,
     :                        1, DIMS(IAX), THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .EQ. 'HiWidth' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( 1, DIMS(IAX), CNDIM, CDIMS )

            CALL BDI1_CFIND1( ALOC, 'HWIDTH', CREATE, DELETE, RTYPE,
     :                        1, DIMS(IAX), THERE, CLOC, STATUS )

          ELSE IF ( ITEM(8:) .LE. ' ' ) THEN
            CALL DAT_CLONE( ALOC, CLOC, STATUS )

          END IF

*      Release the AXIS cell
          CALL DAT_ANNUL( ALOC, STATUS )
          
        END IF

*  Grouping array
      ELSE IF ( ITEM .EQ. 'Grouping' ) THEN

*    Locate the ASTERIX box, creating if allowed 
        CALL ADI1_LOCAST( HID, CREATE, ALOC, STATUS )
        IF ( STATUS .EQ. SAI__OK ) THEN

*      Define dimensions
          CALL BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )

*      Does grouping array exist?
          CALL BDI1_CFIND1( ALOC, 'GROUPING', CREATE, DELETE, 
     :                  '_INTEGER', NDIM, DIMS, THERE, CLOC, STATUS )

        END IF
 
*  Any of the quality associated items
      ELSE IF ( (ITEM.EQ.'Quality') .OR. (ITEM.EQ.'QualityMask') ) THEN

*    Does top level QUALITY structure exist?
        CALL BDI1_CFIND1( LOC, 'QUALITY', CREATE, DELETE, 'QUALITY', 
     :                    0, 0, THERE, QLOC, STATUS )
        IF ( THERE ) THEN

*      Switch on item
*      The quality mask
          IF ( ITEM .EQ. 'QualityMask' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( 0, 0, CNDIM, CDIMS )

*        Does bad bits mask exist?
            CALL BDI1_CFIND1( QLOC, 'BADBITS', CREATE, DELETE, '_UBYTE', 
     :                        0, 0, THERE, CLOC, STATUS )

*      The quality array
          ELSE IF ( ITEM .EQ. 'Quality' ) THEN

*        Define dimensions
            CALL BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )

*        Does quality array exist?
            CALL BDI1_CFIND1( QLOC, 'QUALITY', CREATE, DELETE, '_UBYTE', 
     :                        NDIM, DIMS, THERE, CLOC, STATUS )

          END IF

*      Release QUALITY structure
          CALL ERR_BEGIN( STATUS )
          CALL DAT_ANNUL( QLOC, STATUS )
          CALL ERR_END( STATUS )

        END IF
        
*  Otherwise an error 
      ELSE
        STATUS = SAI__ERROR
        CALL MSG_SETC( 'IT', ITEM )
        CALL ERR_REP( 'BDI1_CFIND_1', 'Unrecognised BDI data item '/
     :                /'^IT', STATUS )
 
      END IF

*  If bad status nullify locator
      IF ( STATUS .NE. SAI__OK ) CLOC = DAT__NOLOC

*  Report any errors
      IF ( STATUS .NE. SAI__OK ) CALL AST_REXIT( 'BDI1_CFIND', STATUS )

      END


      SUBROUTINE BDI1_CFIND0( NDIM, DIMS, CNDIM, CDIMS )
*+
*  Name:
*     BDI1_CFIND0

*  Purpose:
*     Copy dimensions array and dimensionality

*  Language:
*     Starlink Fortran

*  Invocation:
*     CALL BDI1_CFIND0(  NDIM, DIMS, CNDIM, CDIMS )

*  Description:
*     Locate HDS component for a given item, creating if required. If the
*     object does not exist and creation is not allowed then CLOC is set
*     to a flag value.

*  Arguments:
*     NDIM = INTEGER (given)
*        The dimensionality
*     DIMS[] = INTEGER (given)
*        The dimensions 
*     CNDIM = INTEGER (returned)
*        Exported copy of NDIM
*     CDIMS[] = INTEGER (returned)
*        Exported copy of DIMS

*  References:
*     BDI Subroutine Guide : http://www.sr.bham.ac.uk/asterix-docs/Programmer/Guides/bdi.html

*  Keywords:
*     package:bdi, usage:private

*  Copyright:
*     Copyright (C) University of Birmingham, 1995

*  Authors:
*     DJA: David J. Allan (Jet-X, University of Birmingham)
*     {enter_new_authors_here}

*  History:
*     10 Aug 1995 (DJA):
*        Original version.
*     {enter_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
      
*  Type Definitions:
      IMPLICIT NONE              ! No implicit typing

*  Arguments Given:
      INTEGER			NDIM, DIMS(*)

*  Arguments Returned:
      INTEGER			CNDIM, CDIMS(*)

*  Local Variables:
      INTEGER			I			! Loop over dimensions
*.

*  Export dimensions
      CNDIM = NDIM
      DO I = 1, NDIM
        CDIMS(I) = DIMS(I)
      END DO

      END



      SUBROUTINE BDI1_CFIND1( LOC, NAME, CREATE, DELETE, TYPE, NDIM, 
     :                        DIMS, THERE, CLOC, STATUS )
*+
*  Name:
*     BDI1_CFIND1

*  Purpose:
*     Locate HDS component for a given item, creating if required

*  Language:
*     Starlink Fortran

*  Invocation:
*     CALL BDI1_CFIND1( LOC, NAME, CREATE, DELETE, TYPE, NDIM, DIMS, THERE,
*                       CLOC, STATUS ) 

*  Description:
*     Locate HDS component for a given item, creating if required. If the
*     object does not exist and creation is not allowed then CLOC is set
*     to a flag value.

*  Arguments:
*     CLOC = CHARACTER*(DAT__SZLOC) (given)
*        The HDS object containing the component we're interested in
*     NAME = CHARACTER*(*) (given)
*        The name of the component
*     CREATE = LOGICAL (given)
*        Create component if it doesn't exist?
*     DELETE = LOGICAL (given)
*        Delete component if it exists?
*     TYPE = CHARACTER*(*) (given)
*        The type of the component if we have to create it
*     NDIM = INTEGER (given)
*        The dimensionality of the component if we have to create it
*     DIMS[] = INTEGER (given)
*        The dimensions of the component if we have to create it
*     THERE = LOGICAL (returned)
*        Object exists?
*     CLOC = CHARACTER*(DAT__SZLOC) (returned)
*        Locator to component, if THERE is true
*     STATUS = INTEGER (given and returned)
*        The global status.

*  Examples:
*     {routine_example_text}
*        {routine_example_description}

*  Pitfalls:
*     {pitfall_description}...

*  Notes:
*     {routine_notes}...

*  Prior Requirements:
*     {routine_prior_requirements}...

*  Side Effects:
*     {routine_side_effects}...

*  Algorithm:
*     {algorithm_description}...

*  Accuracy:
*     {routine_accuracy}

*  Timing:
*     {routine_timing}

*  External Routines Used:
*     {name_of_facility_or_package}:
*        {routine_used}...

*  Implementation Deficiencies:
*     {routine_deficiencies}...

*  References:
*     BDI Subroutine Guide : http://www.sr.bham.ac.uk/asterix-docs/Programmer/Guides/bdi.html

*  Keywords:
*     package:bdi, usage:private

*  Copyright:
*     Copyright (C) University of Birmingham, 1995

*  Authors:
*     DJA: David J. Allan (Jet-X, University of Birmingham)
*     {enter_new_authors_here}

*  History:
*     10 Aug 1995 (DJA):
*        Original version.
*     {enter_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
      
*  Type Definitions:
      IMPLICIT NONE              ! No implicit typing

*  Global Constants:
      INCLUDE 'SAE_PAR'          ! Standard SAE constants
      INCLUDE 'DAT_PAR'

*  Arguments Given:
      CHARACTER*(DAT__SZLOC)	LOC
      CHARACTER*(*)		NAME, TYPE
      INTEGER			NDIM, DIMS(*)
      LOGICAL			CREATE, DELETE

*  Arguments Returned:
      LOGICAL			THERE
      CHARACTER*(DAT__SZLOC)	CLOC

*  Status:
      INTEGER 			STATUS             	! Global status

*  Local Variables:
      INTEGER			ENDIM, EDIMS(DAT__MXDIM)! Existing dimensions
      INTEGER			I			! Loop over dimensions

      LOGICAL			DOCRE			! Create object?
      LOGICAL			SAME			! Dims the same?
*.

*  Check inherited global status.
      IF ( STATUS .NE. SAI__OK ) RETURN

*  Initialise
      DOCRE = .FALSE.

*  Does the named object exist?
      CALL DAT_THERE( LOC, NAME, THERE, STATUS )
      IF ( DELETE ) THEN

*    Delete it
        IF ( THERE ) THEN
          CALL DAT_ERASE( LOC, NAME, STATUS )
        END IF
        CLOC = DAT__NOLOC

      ELSE IF ( CREATE .AND. .NOT. THERE ) THEN

*    Make sure type and dimensionality are specified
        IF ( (TYPE.EQ.'*unknown*') .OR. (NDIM.EQ.-1) ) THEN
          CALL MSG_SETC( 'N', NAME )
          CALL ERR_REP( ' ', 'Insufficient type or dimensions '/
     :              /'information to create object ^N', STATUS )
        ELSE
          DOCRE = .TRUE.
        END IF

*  If object exists, make sure dimensions are ok. If they are not, coerce
*  them to the correct ones
      ELSE IF ( CREATE .AND. THERE ) THEN

*    Get existing dimensions      
        CALL CMP_SHAPE( LOC, NAME, DAT__MXDIM, EDIMS, ENDIM, STATUS )

*    Are they the same as those required?
        SAME = (NDIM.EQ.ENDIM)
        I = 1
        DO WHILE ( (I.LE.NDIM) .AND. SAME )
          SAME = (DIMS(I).EQ.EDIMS(I))
          I = I + 1
        END DO

*    If not the same, recreate the object
        IF ( .NOT. SAME ) THEN
          CALL DAT_ERASE( LOC, NAME, STATUS )
          DOCRE = .TRUE.
        END IF

      END IF

*  Create it?
      IF ( DOCRE ) THEN
        CALL DAT_NEW( LOC, NAME, TYPE, NDIM, DIMS, STATUS )
        THERE = (STATUS.EQ.SAI__OK)
      END IF

*  Locate if object exists
      IF ( THERE .AND. .NOT. DELETE ) THEN
        CALL DAT_FIND( LOC, NAME, CLOC, STATUS )
      END IF

      END