FUNCTION ADD_DETAIL, DEF, ERRMSG=ERRMSG,NOCHECK_DB=NOCHECK_DB

    ON_ERROR, 2
;
;  Initialize RESULT to represent non-success.  If the routine is successful,
;  this value will be updated below.
;
    RESULT = 0
;
;  Check the input parameters
;
        IF N_PARAMS() NE 1 THEN BEGIN
           MESSAGE = 'Syntax:  Result = ADD_DETAIL(DEF)'
       GOTO, HANDLE_ERROR
        ENDIF
;
;  Make sure that the user has privilege to write into the database.
;
    IF !PRIV LT 2 THEN BEGIN
           MESSAGE = '!PRIV must be 2 or greater to write into the database'
           GOTO, HANDLE_ERROR
    ENDIF
;
;  Check each of the structure components to verify that it is of the correct
;  type and size.
;
    DEF0 = DEF(0)
;
    IF DATATYPE(DEF0.PROG_ID,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag PROG_ID must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.PROG_ID) NE 1 THEN BEGIN
           MESSAGE = 'Tag PROG_ID must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.STUDY_ID,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag STUDY_ID must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.STUDY_ID) NE 1 THEN BEGIN
           MESSAGE = 'Tag STUDY_ID must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.STUDYVAR,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag STUDYVAR must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.STUDYVAR) NE 1 THEN BEGIN
           MESSAGE = 'Tag STUDYVAR must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.SCI_OBJ,1) NE 'String' THEN BEGIN
           MESSAGE = 'Tag SCI_OBJ must be a character string'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.SCI_OBJ) NE 1 THEN BEGIN
           MESSAGE = 'Tag SCI_OBJ must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(STRLEN(DEF.SCI_OBJ)) GT 50 THEN BEGIN $
           MESSAGE = 'Tag SCI_OBJ must be 50 characters or less'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.SCI_SPEC,1) NE 'String' THEN BEGIN
           MESSAGE = 'Tag SCI_SPEC must be a character string'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.SCI_SPEC) NE 1 THEN BEGIN
           MESSAGE = 'Tag SCI_SPEC must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(STRLEN(DEF.SCI_SPEC)) GT 50 THEN BEGIN
           MESSAGE = 'Tag SCI_SPEC must be 50 characters or less'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.CMP_NO,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag CMP_NO must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.CMP_NO) NE 1 THEN BEGIN
           MESSAGE = 'Tag CMP_NO must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.OBJECT,1) NE 'String' THEN BEGIN
           MESSAGE = 'Tag OBJECT must be a character string'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.OBJECT) NE 1 THEN BEGIN
           MESSAGE = 'Tag OBJECT must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(STRLEN(DEF.OBJECT)) GT 3 THEN BEGIN
           MESSAGE = 'Tag OBJECT must be 3 characters or less'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.OBJ_ID,1) NE 'String' THEN BEGIN
           MESSAGE = 'Tag OBJ_ID must be a character string'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.OBJ_ID) NE 1 THEN BEGIN
           MESSAGE = 'Tag OBJ_ID must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(STRLEN(DEF.OBJ_ID)) GT 6 THEN BEGIN
           MESSAGE = 'Tag OBJ_ID must be 6 characters or less'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.DATE_OBS,1) NE 'Double' THEN BEGIN
           MESSAGE = 'Tag DATE_OBS must be a double precision number'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.DATE_OBS) NE 1 THEN BEGIN
           MESSAGE = 'Tag DATE_OBS must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.DATE_END,1) NE 'Double' THEN BEGIN
           MESSAGE = 'Tag DATE_END must be a double precision number'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.DATE_END) NE 1 THEN BEGIN
           MESSAGE = 'Tag DATE_END must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.ORIG_DUR,1) NE 'Double' THEN BEGIN
           MESSAGE = 'Tag ORIG_DUR must be a double precision number'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.ORIG_DUR) NE 1 THEN BEGIN
           MESSAGE = 'Tag ORIG_DUR must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.N_RASTERS1,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag N_RASTERS1 must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.N_RASTERS1) NE 1 THEN BEGIN
           MESSAGE = 'Tag N_RASTERS1 must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.TIME_TAGGED,1) NE 'Byte' THEN BEGIN
           MESSAGE = 'Tag TIME_TAGGED must be a byte value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.TIME_TAGGED) NE 1 THEN BEGIN
           MESSAGE = 'Tag TIME_TAGGED must be a scalar'
           GOTO, HANDLE_ERROR
        END ELSE IF MAX(DEF.TIME_TAGGED) GT 1 THEN BEGIN 
           MESSAGE = 'Tag TIME_TAGGED must be either 0 or 1'
           GOTO, HANDLE_ERROR
        ENDIF
;
    IF DATATYPE(DEF0.TRACKING,1) NE 'Byte' THEN BEGIN
           MESSAGE = 'Tag TRACKING must be a byte value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.TRACKING) NE 1 THEN BEGIN
           MESSAGE = 'Tag TRACKING must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(DEF.TRACKING) GT 1 THEN BEGIN
           MESSAGE = 'Tag TRACKING must be either 0 or 1'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.N_POINTINGS,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag N_POINTINGS must be a short integer value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.N_POINTINGS) NE 1 THEN BEGIN
           MESSAGE = 'Tag N_POINTINGS must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MIN(DEF.N_POINTINGS) LT 0 THEN BEGIN
           MESSAGE = 'Tag N_POINTINGS must not be negative'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.N_REPEAT_S,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag N_REPEAT_S must be a short integer value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.N_REPEAT_S) NE 1 THEN BEGIN
           MESSAGE = 'Tag N_REPEAT_S must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MIN(DEF.N_REPEAT_S) LE 0 THEN BEGIN
           MESSAGE = 'Tag N_REPEAT_S must be positive'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.FLAG_MASTER,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag FLAG_MASTER must be a short integer value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.FLAG_MASTER) NE 1 THEN BEGIN
           MESSAGE = 'Tag FLAG_MASTER must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.GSET_ID,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag GSET_ID must be a short integer value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.GSET_ID) NE 1 THEN BEGIN
           MESSAGE = 'Tag GSET_ID must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(DEF0.GET_RAW,1) NE 'Byte' THEN BEGIN
           MESSAGE = 'Tag GET_RAW must be a byte value'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(DEF0.GET_RAW) NE 1 THEN BEGIN
           MESSAGE = 'Tag GET_RAW must be a scalar'
           GOTO, HANDLE_ERROR
    END ELSE IF MAX(DEF.GET_RAW) GT 1 THEN BEGIN
           MESSAGE = 'Tag GET_RAW must be either 0 or 1'
           GOTO, HANDLE_ERROR
    ENDIF
;
;  See if the PROG_NUM parameter exists.  If it doesn't then use 0.
;
    IF TAG_EXIST(DEF0, 'PROG_NUM') THEN BEGIN
        IF DATATYPE(DEF0.PROG_NUM,1) NE 'Long' THEN BEGIN
               MESSAGE = 'Tag PROG_NUM must be a long integer'
               GOTO, HANDLE_ERROR
        END ELSE IF N_ELEMENTS(DEF0.PROG_NUM) NE 1 THEN BEGIN
               MESSAGE = 'Tag PROG_NUM must be a scalar'
               GOTO, HANDLE_ERROR
        ENDIF
        PROG_NUM = DEF.PROG_NUM
    END ELSE PROG_NUM = REPLICATE(0L, N_ELEMENTS(DEF))
;
;  Extract the first instance of the structure in the POINTINGS tag, and check
;  it's tags.
;
    TEST = (DEF0.POINTINGS)(0)
;
    IF DATATYPE(TEST.INS_X,1) NE 'Float' THEN BEGIN
           MESSAGE = 'Tag INS_X must be a floating point number'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(TEST.INS_X) NE 1 THEN BEGIN
           MESSAGE = 'Tag INS_X must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(TEST.INS_Y,1) NE 'Float' THEN BEGIN
           MESSAGE = 'Tag INS_Y must be a floating point number'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(TEST.INS_Y) NE 1 THEN BEGIN
           MESSAGE = 'Tag INS_Y must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
    IF DATATYPE(TEST.ZONE_ID,1) NE 'Integer' THEN BEGIN
           MESSAGE = 'Tag ZONE_ID must be a short integer'
           GOTO, HANDLE_ERROR
    END ELSE IF N_ELEMENTS(TEST.ZONE_ID) NE 1 THEN BEGIN
           MESSAGE = 'Tag ZONE_ID must be a scalar'
           GOTO, HANDLE_ERROR
    ENDIF
;
;  The number of pointings supplied must match the number specified.
;
    IF MAX(DEF.N_POINTINGS) GT N_ELEMENTS(DEF0.POINTINGS) THEN BEGIN
        MESSAGE = 'Number of pointings does not match array size'
        GOTO, HANDLE_ERROR
    ENDIF
;
;  Make sure that the program ID matches an entry in the CDS program database.
;

        CHECK_DB=1-KEYWORD_SET(NOCHECK_DB)
    FOR I = 0, N_ELEMENTS(DEF)-1 DO BEGIN

           IF CHECK_DB THEN BEGIN
        IF DEF(I).PROG_ID NE 0 THEN BEGIN
        GET_PROGRAM, DEF(I).PROG_ID, TEMP
        IF TEMP.PROG_ID LE 0 THEN BEGIN
            MESSAGE = 'Program ID ' + STRTRIM(DEF(I).PROG_ID,2) + $
                ' not found in database' 
            GOTO, HANDLE_ERROR
        ENDIF
        ENDIF
;
;  Make sure that the campaign number matches an entry in the SOHO campaign
;  database.
;


        IF DEF(I).CMP_NO NE 0 THEN BEGIN
        GET_CAMPAIGN, DEF(I).CMP_NO, TEMP
        IF (TEMP.CMP_NO LE 0) AND (DEF(I).CMP_NO NE 1) THEN BEGIN 
                   MESSAGE = 'Warning - Campaign number ' + STRTRIM(DEF(I).CMP_NO,2) + $
                      ' not found in database'                
                   MESSAGE,MESSAGE,/CONT
;                   IF (DEF(I).CMP_NO LT 0) THEN GOTO, HANDLE_ERROR
        ENDIF
        ENDIF


;  Make sure that the study ID and study variation index match an entry in the
;  CDS study database.
;
        GET_STUDY, DEF(I).STUDY_ID, DEF(I).STUDYVAR, TEMP
        IF TEMP.STUDY_ID LE -1 THEN BEGIN
        MESSAGE = 'Study ID/variation ' + TRIM(DEF(I).STUDY_ID) + $
            ',' + TRIM(DEF(I).STUDYVAR) + ' not found in database'
        GOTO, HANDLE_ERROR
        ENDIF
;
;  If any of the rasters use the GIS detector, then make sure that GSET_ID is
;  valid.
;
        IF TEMP.N_RASTER_DEF GT 0 THEN BEGIN
        USE_GIS = 0
        FOR IR = 0, N_ELEMENTS(TEMP.RASTERS)-1 DO BEGIN
            GET_F_RASTER, TEMP.RASTERS(IR).RAS_ID, TEMP_R
            USE_GIS = USE_GIS OR (TEMP_R.DETECTOR EQ 'G')
        ENDFOR
        IF USE_GIS THEN BEGIN
            GET_GSET, DEF(I).GSET_ID, TEMP_G
            IF TEMP_G.GSET_ID LE 0 THEN BEGIN
            MESSAGE = 'GSET ID ' + TRIM(DEF(I).GSET_ID) +   $
                ' not found in database'
            GOTO, HANDLE_ERROR
            ENDIF
        ENDIF
        ENDIF
           ENDIF

;
;  Make sure that the object code matches an entry in the database.
;
       GET_OBJECT, DEF(I).OBJECT, TEMP
       IF TEMP.OBJECT EQ '' THEN BEGIN
        MESSAGE = 'Object code ' + DEF(I).OBJECT + $
            ' not found in database'
        GOTO, HANDLE_ERROR
       ENDIF
       IF I EQ 0 THEN OBJ = REPLICATE(TEMP,N_ELEMENTS(DEF)) ELSE    $
        OBJ(I) = TEMP

;
;  Make sure that the stop date is greater than the start date.
;
       IF DEF(I).DATE_END LE DEF(I).DATE_OBS THEN BEGIN
        MESSAGE = 'The stop date must be after the start date'
        GOTO, HANDLE_ERROR
       ENDIF
    ENDFOR
;
;  Reformat the times to millisecond accuracy.  This is necessary so that
;  the times are written out in a controlled way.
;
    DEF.DATE_OBS = DOUBLE( STRING( DEF.DATE_OBS, FORMAT='(F15.3)' ))
    DEF.DATE_END = DOUBLE( STRING( DEF.DATE_END, FORMAT='(F15.3)' ))
;
;  Make sure that the proposed entries don't overlap with each other.
;
    FOR I = 0, N_ELEMENTS(DEF)-1 DO BEGIN
        W = WHERE((DEF(I).DATE_OBS LT DEF.DATE_END) AND $
            (DEF(I).DATE_END GT DEF.DATE_OBS), COUNT)
        IF COUNT GT 1 THEN BEGIN
        MESSAGE = 'Proposed entries at time ' + $
            TAI2UTC(DEF(I).DATE_OBS,/ECS,/TR) + ' to ' +    $
            TAI2UTC(DEF(I).DATE_END,/ECS,/TR) + ' and ' +   $
            TAI2UTC(DEF(W(1)).DATE_OBS,/ECS,/TR) + ' to ' + $
            TAI2UTC(DEF(W(1)).DATE_END,/ECS,/TR) +      $
            ' overlap each other.'
        GOTO, HANDLE_ERROR
        ENDIF
    ENDFOR

;
;  Open the science detailed database for write access.
;
    DBOPEN, 'sci_details', 1
;
;  Make sure that the proposed entries do not overlap any of the existing
;  entries.
;
    MIN_DATE_OBS = MIN(DEF(W).DATE_OBS)
    MAX_DATE_END = MAX(DEF(W).DATE_END)
    ENTRIES = DBFIND('DATE_END>' + TRIM(MIN_DATE_OBS,'(F15.3)') + $
        ',DATE_OBS<' + TRIM(MAX_DATE_END,'(F15.3)') +   $
        ',DELETED=N', /SILENT)
    IF !ERR EQ 0 THEN BEGIN
        N_FOUND = 0
    END ELSE BEGIN
        ENTRIES = ENTRIES(UNIQ(ENTRIES))
        N_FOUND = N_ELEMENTS(ENTRIES)
        DBEXT, ENTRIES, 'date_obs,date_end', DATE_OBS, DATE_END
    ENDELSE
;
;  It's okay if the entries in the database only touches the time range of the
;  new data.  Start by sorting the entries by the start time, so that one only
;  has to check the first and last entry.
;
    IF N_FOUND GT 0 THEN BEGIN
        IF N_FOUND GT 1 THEN BEGIN
            S = SORT(DATE_OBS)
            ENTRIES  = ENTRIES(S)
            DATE_OBS = DATE_OBS(S)
            DATE_END = DATE_END(S)
        ENDIF
        IF DATE_OBS(N_FOUND-1) EQ MAX_DATE_END THEN $
            N_FOUND = N_FOUND - 1
        IF DATE_END(0) EQ MIN_DATE_OBS THEN N_FOUND = N_FOUND - 1
        IF N_FOUND GT 0 THEN BEGIN
            MESSAGE = 'Definition would overlap one in the database'
            GOTO, HANDLE_ERROR
        ENDIF
    ENDIF
;
;  Add the entries.
;
    DBBUILD, DEF.PROG_ID, DEF.STUDY_ID, DEF.STUDYVAR, DEF.SCI_OBJ,  $
        DEF.SCI_SPEC, DEF.CMP_NO, OBJ.OBJECT, DEF.OBJ_ID,   $
        DEF.DATE_OBS, DEF.DATE_END, DEF.ORIG_DUR, DEF.N_RASTERS1, $
        DEF.TIME_TAGGED, DEF.TRACKING, DEF.N_POINTINGS,     $
        DEF.N_REPEAT_S, DEF.FLAG_MASTER, DEF.GSET_ID, DEF.GET_RAW, $
        PROG_NUM, REPLICATE('N', N_ELEMENTS(DEF)), STATUS=STATUS
    IF STATUS EQ 0 THEN BEGIN
           MESSAGE = 'Write to sci_details database was not successful'
           GOTO, HANDLE_ERROR
    ENDIF
;
;  Write the associated pointings.  First, collect them into arrays.
;
    N_POINTINGS = 0
    FOR I = 0, N_ELEMENTS(DEF)-1 DO BEGIN
        N = DEF(I).N_POINTINGS
        IF N GT 0 THEN BEGIN
        IF N_POINTINGS EQ 0 THEN BEGIN
            DATE_OBS  = REPLICATE(DEF(I).DATE_OBS, N)
            POINT_IND = INDGEN(N)
            POINTINGS = DEF(I).POINTINGS(0:N-1)
        END ELSE BEGIN
            DATE_OBS  = [DATE_OBS,  REPLICATE(DEF(I).DATE_OBS, N)]
            POINT_IND = [POINT_IND, INDGEN(N)]
            POINTINGS = [POINTINGS, DEF(I).POINTINGS(0:N-1)]
        ENDELSE
        N_POINTINGS = N_POINTINGS + N
        ENDIF
    ENDFOR
;
    IF N_POINTINGS GT 0 THEN BEGIN
        DBOPEN, 'point_details', 1
        DBBUILD, DATE_OBS, POINT_IND, POINTINGS.INS_X, POINTINGS.INS_Y, $
            POINTINGS.ZONE_ID, REPLICATE('N', N_POINTINGS)
    ENDIF
;
;  Signal success.
;
    RESULT = 1
    GOTO, FINISH
;
;  Error handling point.
;
HANDLE_ERROR:
    IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = 'ADD_DETAIL: ' + MESSAGE $
        ELSE MESSAGE, MESSAGE, /CONTINUE
;
;  Close the database, and return whether the routine was successful or not.
;
FINISH:
    DBCLOSE
;
    RETURN, RESULT
    END