SUBROUTINE BDI2_SETLNK( NARG, ARGS, OARG, STATUS )
*+
*  Name:
*     BDI2_SETLNK

*  Purpose:
*     Service SetLink method for various class to FITSfile links

*  Language:
*     Starlink Fortran

*  Invocation:
*     CALL BDI2_SETLNK( NARG, ARGS, OARG, STATUS )

*  Description:
*     Establishes ADI file link between high level objects Scalar, Array
*     and BinDS and the FITSfile.

*  Arguments:
*     NARG = INTEGER (given)
*        Number of method arguments
*     ARGS(*) = INTEGER (given)
*        ADI identifier of method arguments
*     OARG = INTEGER (returned)
*        Output data
*     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:
*     9 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 'ADI_PAR'

*  Arguments Given:
      INTEGER                   NARG, ARGS(*)

*  Arguments Returned:
      INTEGER                   OARG

*  Status:
      INTEGER 			STATUS             	! Global status

*  External References:
      EXTERNAL			CHR_SIMLR
        LOGICAL			CHR_SIMLR
 
*  Local Variables:
      CHARACTER*4		BCS			! String of BCOL
      CHARACTER*72		CMNT			! Key comment
      CHARACTER*20		CONTNT			! CONTENT keyword value
      CHARACTER*80		FPATH			! Sub-HDU path info
      CHARACTER*10		TYP			! Primary data type

      INTEGER			BCOL			! Binary table column #
      INTEGER			BITPIX			! Bits per pixel
      INTEGER			BLEN			! Length of BCS
      INTEGER			DHDU			! Data HDU
      INTEGER			DIMS(ADI__MXDIM)	! Dimensions
      INTEGER			HDUTYP			! FITSIO HDU type code
      INTEGER			MID			! Output model object
      INTEGER			NDIM			! Dimensionality
      INTEGER			NWRD			! # words in FPATH
      INTEGER			PHDU			! Main HDU
      INTEGER			SPOS(3), EPOS(3)	! FPATH bits
      INTEGER			UIHDU			! User specified HDU #
 
      LOGICAL			FPOK			! Fpath data present?
      LOGICAL			ISARY			! Array?
      LOGICAL			ISIMAG			! Image?
      LOGICAL			ISSCAL			! Scalar?
      LOGICAL			ISSPEC			! Spectrum?
      LOGICAL			ISTIME			! Time series?
*.

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

*  Default return value
      OARG = ADI__NULLID

*  Initialise
      ISARY = .FALSE.
      ISSPEC = .FALSE.
      ISIMAG = .FALSE.
      ISTIME = .FALSE.
      ISSCAL = .FALSE.

*  Did user supply an HDU number?
      CALL ADI_CGET0I( ARGS(2), 'UserHDU', UIHDU, STATUS )
      
*  Locate the main HDU for this input. This is the primary HDU for file
*  level input, or the user specified HDU otherwise.
      CALL ADI2_FNDHDU( ARGS(2), 'PRIMARY', .FALSE., PHDU, STATUS )

*  If the user has specified the HDU we can't move outside it
      IF ( UIHDU .GT. 0 ) THEN

*    Did user supply sub-HDU path info?
        CALL ADI_THERE( ARGS(2), 'Fpath', FPOK, STATUS )
        IF ( FPOK ) THEN
          CALL ADI_CGET0C( ARGS(2), 'Fpath', FPATH, STATUS )
          CALL ADI2_FPSPL( FPATH, 3, SPOS, EPOS, NWRD, STATUS )
        ELSE
          NWRD = 0
        END IF

*    The only possible abstract types (for BDI) with an image extension are 
*    BinDS (the whole HDU), or Scalar (a keyword). If the extension is a
*    a BINTABLE then there must be additional path details. If the first
*    path name string is a column name, and there are no additional details
*    then the returned type is Array. If the name is not a column name then
*    the returned type is Scalar. If the the path is a column, then the
*    following modifiers are allowed to specify particular keywords (.UNIT,
*    .MIN, .MAX).
        CALL ADI_CGET0I( PHDU, 'HduType', HDUTYP, STATUS )
        IF ( (HDUTYP .EQ. 1) .OR. (HDUTYP.EQ.2) ) THEN

*      If the user did not specify anything beyond the BINTABLE we have
*      an error
          IF ( NWRD .EQ. 1 ) THEN
            STATUS = SAI__ERROR
            CALL ERR_REP( 'BDI2_SETLNK_BT', 'BDI cannot access ascii'/
     :                    /'or binary table as input', STATUS )
          ELSE

*        Does the 2nd path word specify a table column?
            CALL ADI2_FNDBTC( PHDU, FPATH(SPOS(2):EPOS(2)), BCOL,
     :                        STATUS )
            IF ( BCOL .GT. 0 ) THEN

*          Convert column number to string for keyword testing
              CALL CHR_ITOC( BCOL, BCS, BLEN )

*          If no other stuff supplied we're dealing with the whole column
              IF ( NWRD .EQ. 2 ) THEN

*            In which case our link type is Array
                ISARY = .TRUE.

*            The sub-item is the column name
                CALL ADI_CPUT0C( ARGS(2), 'SubItem', 
     :                           FPATH(SPOS(2):EPOS(2)), STATUS )

*            Convert type info. Only make array 2-D if column has many
*            values
                CALL ADI2_BTCTYP( PHDU, BCOL, DIMS(1), TYP, STATUS )
                IF ( DIMS(1) .EQ. 1 ) THEN
                  NDIM = 1
                ELSE
                  NDIM = 2
                END IF

*            NDIM'th dimension is the length of the table
                CALL ADI2_HGKYI( PHDU, 'NAXIS2', DIMS(NDIM), CMNT, 
     :                           STATUS )

*          The TLMIN keyword for the column?
              ELSE IF ( CHR_SIMLR( FPATH(SPOS(3):EPOS(3)), 
     :                             'MIN' ) ) THEN

*            Link with the keyword
                CALL BDI2_LNKKEY( ARGS(2), PHDU, 'TLMIN'//BCS(:BLEN),
     :                            .FALSE., ' ', TYP, ISSCAL, STATUS )

*          The TLMAX keyword for the column?
              ELSE IF ( CHR_SIMLR( FPATH(SPOS(3):EPOS(3)), 
     :                             'MAX' ) ) THEN

*            Link with the keyword
                CALL BDI2_LNKKEY( ARGS(2), PHDU, 'TLMAX'//BCS(:BLEN),
     :                            .FALSE., ' ', TYP, ISSCAL, STATUS )

*          The TUNIT keyword for the column?
              ELSE IF ( CHR_SIMLR( FPATH(SPOS(3):EPOS(3)), 
     :                             'UNIT' ) ) THEN

*            Link with the keyword
                CALL BDI2_LNKKEY( ARGS(2), PHDU, 'TUNIT'//BCS(:BLEN),
     :                            .FALSE., ' ', TYP, ISSCAL, STATUS )

              END IF

*        Otherwise must be a keyword
            ELSE

*          Link with the keyword
              CALL BDI2_LNKKEY( ARGS(2), PHDU, FPATH(SPOS(2):EPOS(2)),
     :                          (SPOS(3).GT.0), FPATH(EPOS(2)+2:),
     :                          TYP, ISSCAL, STATUS )

            END IF

          END IF

        ELSE

*      Second path item should be keyword name (1st is HDU itself)
          IF ( NWRD .GT. 1 ) THEN

*        Link with the keyword
            CALL BDI2_LNKKEY( ARGS(2), PHDU, FPATH(SPOS(2):EPOS(2)),
     :                        (SPOS(3).GT.0), FPATH(EPOS(2)+2:),
     :                        TYP, ISSCAL, STATUS )

          ELSE

*        Get shape of HDU
            CALL ADI2_IMGTSHP( PHDU, .FALSE., BITPIX, NDIM, DIMS, 
     :                         STATUS )
            CALL ADI2_BP2TYP( BITPIX, TYP, STATUS )

          END IF

        END IF 

*  Look for high level data forms
      ELSE

*    HDU has CONTENT keyword?
        CALL ADI2_HGKYC( PHDU, 'CONTENT', CONTNT, CMNT, STATUS )
        IF ( STATUS .EQ. SAI__OK ) THEN
          IF ( CHR_SIMLR( 'SPECTRUM', CONTNT ) ) THEN
            ISSPEC = .TRUE.
          ELSE IF ( CHR_SIMLR( 'IMAGE', CONTNT ) ) THEN
            ISIMAG = .TRUE.
          ELSE IF ( CHR_SIMLR( 'SERIES', CONTNT ) ) THEN
            ISTIME = .TRUE.
          END IF
        ELSE
          CALL ERR_ANNUL( STATUS )

*      Check dimensionality
          CALL ADI2_HGKYI( PHDU, 'NAXIS', NDIM, CMNT, STATUS )
       
*      Look for RATE extension if no primary data
          IF ( NDIM .EQ. 0 ) THEN
            CALL ADI2_FNDHDU( ARGS(2), 'RATE', .FALSE., DHDU, STATUS )
            IF ( STATUS .EQ. SAI__OK ) THEN
              ISTIME = .TRUE.
              CALL ADI_ERASE( DHDU, STATUS )
            ELSE
              STATUS = SAI__ERROR
              CALL ERR_REP( ' ', 'Unknown FITS dataset type', STATUS )
            END IF
          ELSE

*        Get shape of HDU
            CALL ADI2_IMGTSHP( PHDU, .FALSE., BITPIX, NDIM, DIMS, 
     :                         STATUS )
            CALL ADI2_BP2TYP( BITPIX, TYP, STATUS )

          END IF

        END IF

      END IF

*  Switch on the supported dataset types. First images 
      IF ( ISIMAG ) THEN

*    New model object
        CALL ADI_NEW0( 'XYimage', OARG, STATUS )

*    The number of bins is the number of rows in the table
        NDIM = 2
        CALL ADI2_HGKYI( PHDU, 'NAXIS1', DIMS(1), CMNT, STATUS )
        CALL ADI2_HGKYI( PHDU, 'NAXIS2', DIMS(2), CMNT, STATUS )

*    Convert BITPIX value to type
        CALL ADI2_HGKYI( PHDU, 'BITPIX', BITPIX, CMNT, STATUS )
        CALL ADI2_BP2TYP( BITPIX, TYP, STATUS )

*  OGIP spectra    
      ELSE IF ( ISSPEC ) THEN

*    New model object
        CALL ADI_NEW0( 'Spectrum', OARG, STATUS )
        
*    Locate the extension containing the spectrum
        CALL ADI2_FNDHDU( ARGS(2), 'SPECTRUM', .FALSE., DHDU, STATUS )

*    The number of bins is the number of rows in the table
        NDIM = 1
        CALL ADI2_HGKYI( DHDU, 'NAXIS2', DIMS(1), CMNT, STATUS )
    
*    Type is REAL
        TYP = 'REAL'
    
*    Release the data HDU
        CALL ADI_ERASE( DHDU, STATUS )

*  OGIP time series
      ELSE IF ( ISTIME ) THEN

*    New model object
        CALL ADI_NEW0( 'TimeSeries', OARG, STATUS )

*    Locate the extension containing the time series
        CALL ADI2_FNDHDU( ARGS(2), 'RATE', .FALSE., DHDU, STATUS )

*    The number of bins is the number of rows in the table
        NDIM = 1
        CALL ADI2_HGKYI( DHDU, 'NAXIS2', DIMS(1), CMNT, STATUS )

*    Type is REAL
        TYP = 'DOUBLE'
    
*    Release the data HDU
        CALL ADI_ERASE( DHDU, STATUS )

*  Array?
      ELSE IF ( ISARY ) THEN

*    New model object
        CALL ADI_NEW0( 'Array', OARG, STATUS )

*  Scalar?
      ELSE IF ( ISSCAL ) THEN

*    New model object
        CALL ADI_NEW0( 'Scalar', OARG, STATUS )

*    Set dimensions
        NDIM = 0
        DIMS(1) = 0

      END IF

      IF ( OARG .EQ. ADI__NULLID ) THEN
        MID = ARGS(1)
      ELSE
        MID = OARG 
      END IF

*  Store the dimensions
      CALL BDI_SETSHP( MID, NDIM, DIMS, STATUS )
      CALL BDI_SETTYP( MID, TYP, STATUS )

*  Release the main HDU
      CALL ADI_ERASE( PHDU, STATUS )

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

*  Make link
      CALL ADI_SETLNK( MID, ARGS(2), STATUS )

      END



      SUBROUTINE BDI2_LNKKEY( FID, HDU, KEY, GSUB, SUBKEY, TYP, ISSCAL, 
     :                        STATUS )
*+
*  Name:
*     BDI2_LNKKEY

*  Purpose:
*     Try to link BDI with a FITS keyword or its comment

*  Language:
*     Starlink Fortran

*  Invocation:
*     CALL BDI2_SETLNK_KEY( FID, HDU, KEY, GSUB, SUBKEY, TYP, ISSCAL, STATUS )

*  Description:
*     Establishes ADI file link between a Scalar and a FITS keyword 

*  Arguments:
*     NARG = INTEGER (given)
*        Number of method arguments
*     ARGS(*) = INTEGER (given)
*        ADI identifier of method arguments
*     OARG = INTEGER (returned)
*        Output data
*     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:
*     9 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

*  Arguments Given:
      INTEGER                   FID, HDU
      LOGICAL			GSUB
      CHARACTER*(*)		KEY, SUBKEY

*  Arguments Returned:
      LOGICAL			ISSCAL
      CHARACTER*(*)		TYP

*  Status:
      INTEGER 			STATUS             	! Global status

*  External References:
      EXTERNAL			CHR_SIMLR
        LOGICAL			CHR_SIMLR
 
*  Local Variables:
      CHARACTER*10		SUBITM			! The sub file item

      INTEGER			KID			! Keyword object
*.

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

*  Search for named keyword
      CALL ADI2_HGKY( HDU, KEY, KID, STATUS )
      IF ( STATUS .EQ. SAI__OK ) THEN

*    The file sub item
        SUBITM = KEY

*    More path data?
        IF ( GSUB .AND. (SUBKEY .GT. ' ') ) THEN

*      Must be COMMENT
          IF ( CHR_SIMLR( SUBKEY, 'COMMENT' ) ) THEN

*        Keyword comment exists?
            TYP = 'CHAR' 
            ISSCAL = .TRUE.
            SUBITM = '&'//SUBITM

          ELSE
            CALL MSG_SETC( 'KEY', KEY )
            CALL MSG_SETC( 'ITEM', SUBKEY )
            STATUS = SAI__ERROR
            CALL ERR_REP( ' ', 'Spurious FITS path item /'/
     :                 /'^ITEM/ following valid keyword name ^KEY', 
     :                  STATUS ) 

          END IF

*    Keyword only required
        ELSE 
          ISSCAL = .TRUE.
          CALL ADI_TYPE( KID, TYP, STATUS )
              
        END IF

*    The file SubItem is the keyword name with a '&' prepended if
*    we're after the comment
        CALL ADI_CPUT0C( FID, 'SubItem', SUBITM, STATUS )

*    Release the keyword
        CALL ADI_ERASE( KID, STATUS )

*   Keyword not found
      ELSE
        CALL MSG_SETC( 'KEY', KEY )
        CALL ERR_REP( ' ', 'Unable to locate keyword ^KEY in'/
     :                        /' file ^FILE (HDU ^H)', STATUS )

      END IF

      END