	FUNCTION UPD_PLAN, DEF, ERRMSG=ERRMSG
;+
; Project     :	SOHO - CDS
;
; Name        :	UPD_PLAN()
;
; Purpose     :	Adds a SOHO science plan record to the database
;
; Explanation :	This procedure takes a SOHO science plan entry and adds it to
;		the database "sci_plan".  This database contains a series of
;		such entries, making a historical list.
;
;		This routine differs from ADD_PLAN in two important ways.
;		First, it works for all the SOHO instruments, not just CDS.
;		Second, it overwrites whatever is already currently in the
;		database.
;
; Use         :	Result = UPD_PLAN( DEF )
;
;		IF NOT UPD_PLAN( DEF ) THEN ...
;
; Inputs      :	DEF = This is an anonymous structure containing the following
;		      tags:
;
;			INSTRUME     = Single letter code specifying the
;				       instrument.
;			SCI_OBJ      = Science objective from the daily science
;				       meeting
;			SCI_SPEC     = Specific science objective from meeting
;			NOTES	     = Further notes about the observation
;			START_TIME   = Date/time of beginning of observation,
;				       in TAI format
;			END_TIME     = Date/time of end of observation, in TAI
;				       format
;			OBJECT	     = Code for object planned to be observed
;			OBJ_ID	     = Object identification
;			PROG_ID	     = Program ID, linking one or more studies
;				       together
;			CMP_NO	     = Campaign number
;			XCEN	     = Center(s) of instrument FOV along X
;				       axis, given as a character string.
;			YCEN	     = Center(s) of instrument FOV along Y
;				       axis, given as a character string.
;			DISTURBANCES = Description of any disturbances
;
;		      It can also be an array of such structures.
;
; Opt. Inputs :	None.
;
; Outputs     :	The result of the function is a logical value representing
;		whether or not the operation was successful, where 1 is
;		successful and 0 is unsuccessful.
;
; Opt. Outputs:	None.
;
; Keywords    :
;       ERRMSG    = If defined and passed, then any error messages will be
;                   returned to the user in this parameter rather than
;                   depending on the MESSAGE routine in IDL.  If no errors are
;                   encountered, then a null string is returned.  In order to
;                   use this feature, ERRMSG must be defined first, e.g.
;
;                       ERRMSG = ''
;                       Result = UPD_PLAN( ERRMSG=ERRMSG, ... )
;                       IF ERRMSG NE '' THEN ...
;
;
; Calls       :	DATATYPE, DBOPEN, DBBUILD, DBCLOSE, TRIM, GET_INSTRUMENT,
;		GET_OBJECT, GET_CAMPAIGN, DBFIND
;
; Common      :	None.
;
; Restrictions:	Only this routine or DEL_PLAN can be used to add or delete
;		science plan descriptions to or from the database.
;		Modifying the database by hand could corrupt its integrity.
;
;		The data types and sizes of the structure elements must match
;		the definitions in the database.
;
;		!PRIV must be 2 or greater to use this routine.
;
; Side effects:	Marks records as deleted.  PRG_PLAN must be called to really
;		delete the records.
;
; Category    :	Planning, Databases.
;
; Prev. Hist. :	None.
;
; Written     :	William Thompson, GSFC, 7 November 1994
;
; Modified    :	Version 1, William Thompson, GSFC, 7 November 1994
;		Version 2, William Thompson, GSFC, 3 April 1995
;			Fixed bug with conversion to instrument code
;		Version 3, William Thompson, GSFC, 8 May 1995
;			Modified to pay attention to DELETED field in database
;		Version 4, William Thompson, GSFC, 19 May 1995
;			Fixed bug when new entry would overlap more than one
;			old ones.
;			Changed way DBFIND is called, to speed up.
;		Version 5, William Thompson, GSFC, 22 May 1995
;			Modified to allow the input parameter to be an array.
;		Version 6, William Thompson, GSFC, 3 August 1995
;			Fixed bug when only one instrument referred to in list
;			of entries.
;		Version 7, William Thompson, GSFC, 15 August 1995
;			Modified to make more efficient.
;			Ignore overlapping entries rather than exit
;		Version 8, William Thompson, GSFC, 18 August 1995
;			Added tags XCEN, YCEN
;               Version 9, Zarro, ARC, 30 August 1996
;                       Ignore ill-defined CAMPAIGN and OBJECT ID's rather
;                       than exit.         
;
; Version     :	Version 9
;-
;
	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 = UPD_PLAN(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.INSTRUME,1) NE 'String' THEN BEGIN
	   MESSAGE = 'Tag INSTRUME must be a character string'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.INSTRUME) NE 1 THEN BEGIN
	   MESSAGE = 'Tag INSTRUME must be a scalar'
	   GOTO, HANDLE_ERROR
	ENDIF
	MAXLEN = MAX(STRLEN(DEF.INSTRUME), MIN=MINLEN)
	IF (MAXLEN NE 1) OR (MINLEN NE 1) THEN BEGIN
	   MESSAGE = 'Tag INSTRUME must be a single character'
	   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.NOTES,1) NE 'String' THEN BEGIN
	   MESSAGE = 'Tag NOTES must be a character string'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.NOTES) NE 1 THEN BEGIN
	   MESSAGE = 'Tag NOTES must be a scalar'
	   GOTO, HANDLE_ERROR
	END ELSE IF MAX(STRLEN(DEF.NOTES)) GT 50 THEN BEGIN
	   MESSAGE = 'Tag NOTES must be 50 characters or less'
	   GOTO, HANDLE_ERROR
	ENDIF
;
	IF DATATYPE(DEF0.START_TIME,1) NE 'Double' THEN BEGIN
	   MESSAGE = 'Tag START_TIME must be a double precision number'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.START_TIME) NE 1 THEN BEGIN
	   MESSAGE = 'Tag START_TIME must be a scalar'
	   GOTO, HANDLE_ERROR
	ENDIF
;
	IF DATATYPE(DEF0.END_TIME,1) NE 'Double' THEN BEGIN
	   MESSAGE = 'Tag END_TIME must be a double precision number'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.END_TIME) NE 1 THEN BEGIN
	   MESSAGE = 'Tag END_TIME 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.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.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.XCEN,1) NE 'String' THEN BEGIN
	   MESSAGE = 'Tag XCEN must be a character string'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.XCEN) NE 1 THEN BEGIN
	   MESSAGE = 'Tag XCEN must be a scalar'
	   GOTO, HANDLE_ERROR
	END ELSE IF MAX(STRLEN(DEF.XCEN)) GT 50 THEN BEGIN
	   MESSAGE = 'Tag XCEN must be 50 characters or less'
	   GOTO, HANDLE_ERROR
	ENDIF
;
	IF DATATYPE(DEF0.YCEN,1) NE 'String' THEN BEGIN
	   MESSAGE = 'Tag YCEN must be a character string'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.YCEN) NE 1 THEN BEGIN
	   MESSAGE = 'Tag YCEN must be a scalar'
	   GOTO, HANDLE_ERROR
	END ELSE IF MAX(STRLEN(DEF.YCEN)) GT 50 THEN BEGIN
	   MESSAGE = 'Tag YCEN must be 50 characters or less'
	   GOTO, HANDLE_ERROR
	ENDIF
;
	IF DATATYPE(DEF0.DISTURBANCES,1) NE 'String' THEN BEGIN
	   MESSAGE = 'Tag DISTURBANCES must be a character string'
	   GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(DEF0.DISTURBANCES) NE 1 THEN BEGIN
	   MESSAGE = 'Tag DISTURBANCES must be a scalar'
	   GOTO, HANDLE_ERROR
	END ELSE IF MAX(STRLEN(DEF.DISTURBANCES)) GT 50 THEN BEGIN
	   MESSAGE = 'Tag DISTURBANCES must be 50 characters or less'
	   GOTO, HANDLE_ERROR
	ENDIF
;
;  Make sure that the instrument code is valid.
;
	FOR I = 0, N_ELEMENTS(DEF)-1 DO BEGIN
            ERR=''
	    GET_INSTRUMENT, DEF(I).INSTRUME, TEMP,ERR=ERR
	    IF TEMP.CODE EQ '' THEN BEGIN
	        MESSAGE = 'Instrument ' + DEF(I).INSTRUME +	$
			' not found in database'
                MESSAGE,MESSAGE,/CONT
	    ENDIF
	    IF I EQ 0 THEN INS = REPLICATE(TEMP,N_ELEMENTS(DEF)) ELSE	$
		INS(I) = TEMP
;
;  Make sure that the campaign number matches an entry in the SOHO campaign
;  database.
;
	    IF DEF(I).CMP_NO NE 0 THEN BEGIN
                ERR='' & ICMP=DEF(I).CMP_NO
		GET_CAMPAIGN, ICMP, TEMP,ERR=ERR
		IF (TEMP.CMP_NO LE 0) and (ICMP NE 1) THEN BEGIN
		    MESSAGE = 'Campaign number ' + STRTRIM(DEF(I).CMP_NO,2) + $
			    ' not found in database'
		    MESSAGE,MESSAGE,/CONT
		ENDIF
	    ENDIF
;
;  Make sure that the object code matches an entry in the database.
;
            ERR=''
	    GET_OBJECT, DEF(I).OBJECT, TEMP,ERR=ERR
	    IF TEMP.OBJECT EQ '' THEN BEGIN
		MESSAGE = 'Object code ' + DEF(I).OBJECT +	$
			' not found in database'
                MESSAGE,MESSAGE,/CONT
	    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).END_TIME LE DEF(I).START_TIME THEN BEGIN
	        MESSAGE = 'The stop date must be after the start date'
	        GOTO, HANDLE_ERROR
	    ENDIF
	ENDFOR
;
;  Make sure that the proposed entries don't overlap with each other.
;
	I = 0
	WHILE I LT N_ELEMENTS(DEF) DO BEGIN
	    W = WHERE((DEF(I).START_TIME LT DEF.END_TIME) AND	$
		    (DEF(I).END_TIME GT DEF.START_TIME) AND	$
		    (INS(I).CODE EQ INS.CODE), COUNT)
	    IF COUNT GT 1 THEN BEGIN
		MESSAGE, 'Proposed ' + INS(I).NAME + ' entries at time ' + $
			TAI2UTC(DEF(I).START_TIME,/ECS,/TR) + ' to ' +	$
			TAI2UTC(DEF(I).END_TIME,/ECS,/TR) + ' and ' +	$
			TAI2UTC(DEF(W(1)).START_TIME,/ECS,/TR) + ' to '+$
			TAI2UTC(DEF(W(1)).END_TIME,/ECS,/TR) +		$
			' overlap each other.', /CONTINUE
		BELL
		REMOVE, W(1:*), DEF, INS, OBJ
	    ENDIF
	    I = I + 1
	ENDWHILE
;
;  Open the science plan database for write access.
;
	DBOPEN, 'sci_plan', 1
;
;  Step through all the instruments.  First determine a unique set of all the
;  instruments which have been referred to.
;
	ALL_INS = INS.CODE
	IF N_ELEMENTS(ALL_INS) GT 1 THEN BEGIN
		ALL_INS = ALL_INS(SORT(ALL_INS))
		ALL_INS = ALL_INS(UNIQ(ALL_INS))
	ENDIF
	FOR J = 0,N_ELEMENTS(ALL_INS)-1 DO BEGIN
;
;  Find any existing entries which overlap with the proposed entries.
;
	    W = WHERE(INS.CODE EQ ALL_INS(J))
	    MIN_START_TIME = MIN(DEF(W).START_TIME)
	    MAX_END_TIME   = MAX(DEF(W).END_TIME)
	    ENTRIES = DBFIND('END_TIME>' + TRIM(MIN_START_TIME,'(F15.3)') + $
		    ',START_TIME<' + TRIM(MAX_END_TIME,'(F15.3)') +	$
		    ',INSTRUME=' + ALL_INS(J) + ',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, 'start_time,end_time', START_TIME, END_TIME
	    ENDELSE
;
;  It's okay if the entries in the database only touch the time range of the
;  new data.  Otherwise, delete those entries.  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(START_TIME)
			ENTRIES    = ENTRIES(S)
			START_TIME = START_TIME(S)
			END_TIME   = END_TIME(S)
		ENDIF
		IF END_TIME(0) EQ MIN_START_TIME THEN BEGIN
		    N_FOUND = N_FOUND - 1
		    IF N_FOUND GT 0 THEN BEGIN
			ENTRIES = ENTRIES(1:*)
			START_TIME = START_TIME(1:*)
			END_TIME = END_TIME(1:*)
		    ENDIF
		ENDIF
		IF N_FOUND GT 0 THEN BEGIN
		    IF START_TIME(N_FOUND-1) EQ MAX_END_TIME THEN BEGIN
			N_FOUND = N_FOUND - 1
			IF N_FOUND GT 0 THEN BEGIN
			    ENTRIES = ENTRIES(0:N_FOUND-1)
			    START_TIME = START_TIME(0:N_FOUND-1)
			    END_TIME = END_TIME(0:N_FOUND-1)
			ENDIF
		    ENDIF
		ENDIF
		IF N_FOUND GT 0 THEN DBUPDATE, ENTRIES, 'deleted',	$
			REPLICATE('Y', N_ELEMENTS(ENTRIES))
	    ENDIF
	ENDFOR
;
;  Add the entries.
;
	DELETED = REPLICATE('N', N_ELEMENTS(DEF))
	DBBUILD, INS.CODE, DEF.SCI_OBJ, DEF.SCI_SPEC, DEF.NOTES,	$
		DEF.START_TIME, DEF.END_TIME, OBJ.OBJECT, DEF.OBJ_ID,	$
		DEF.PROG_ID, DEF.CMP_NO, DEF.XCEN, DEF.YCEN,		$
		DEF.DISTURBANCES, DELETED, STATUS=STATUS
	IF STATUS EQ 0 THEN BEGIN
	   MESSAGE = 'Write to sci_plan database was not successful'
	   GOTO, HANDLE_ERROR
	ENDIF
;
;  Signal success.
;
	RESULT = 1
	GOTO, FINISH
;
;  Error handling point.
;
HANDLE_ERROR:
	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = 'UPD_PLAN: ' + MESSAGE	$
		ELSE MESSAGE, MESSAGE, /CONTINUE
;
;  Close the database, and return whether the routine was successful or not.
;
FINISH:
	DBCLOSE
;
	RETURN, RESULT
	END
