;+
; PROJECT:
;       SOHO - CDS
;
; NAME:
;       MOD_CAMPAIGN()
;
; PURPOSE:
;       Modify a CDS campaign record in the database
;
; CATEGORY:
;       Planning, Databases
;
; EXPLANATION:
;       This procedure takes a CDS campaign entry and modify the
;       corresponding entry in database table CAMPAIGN.  This database
;       contains a series of such entries, making a historical list.
;
; SYNTAX:
;       Result = mod_campaign(DEF)
;
; INPUTS:
;       DEF = This is an anonymous structure containing the following tags:
;             CMP_NO   = Unique identifier number.  If no matches
;                        are found, then a simpler structure is
;                        returned with this set to -1.
;             CMP_NAME = Name of the campaign.
;	      CMP_TYPE = Campaign type, e.g. JOP, Intercal, etc.
;             CMP_DESC = Up to five lines of text (400 characters)
;                        describing the campaign.
;             DATE_OBS = Starting date for the observing campaign
;             DATE_END = Ending data for the campaign
;             OBSERVER = Observer in overall charge of the campaign
;	      COMMENT  = A comment associated with the campaign
;	      INSTITUTES = Structure containing information about the
;			 institutes involved, with the following tags:
;
;			INSTITUT = The name of the institute.
;			OBSERVER = The name of the observer at that institute
;
; OPTIONAL 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.
;
; OPTIONAL 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 = mod_campaign(errmsg=errmsg, ... )
;                       IF errmsg NE '' THEN ...
;
; COMMON:
;	None.
;
; RESTRICTIONS:
; 	Only this routine and ADD_CAMPAIGN can be used to update or add
;	SOHO campaigns 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.
;
; SIDE EFFECTS:
;	The dates in the structure are rounded off to millisecond
;	accuracy.
;
; HISTORY:
;       Version 1, 10-Sep-1996, William Thompson, GSFC
;		Converted from SUMER version by Liyun Wang
;	Version 2, 23-Sep-1996, William Thompson, GSFC
;		Added campaign type, changed string lengths.
;	Version 3, 21-Nov-1996, William Thompson, GSFC
;		Fixed bug when modification would add a comment that didn't
;		exist before.
;	Version 4, 22-Nov-1996, Zarro, GSFC
;		Added check for modified CMP_TYPE and call to TRIM_CAMPAIGN
;	Version 5, 22-Nov-1996, William Thompson, GSFC
;		Fixed bug when modification would add an institute when there
;		weren't any before.
;	Version 6, 19-Dec-1996, William Thompson, GSFC
;		Allow COMMENTS tag to be optional.
;
; CONTACT:
;       WTHOMPSON
;-

FUNCTION mod_campaign, def, errmsg=errmsg

   ON_ERROR, 1

;---------------------------------------------------------------------------
;  Check the input parameters
;---------------------------------------------------------------------------
   IF N_PARAMS() NE 1 THEN BEGIN
      msg = 'Syntax:  Result = mod_campaign(def)'
      GOTO, handle_error
   ENDIF
   
;---------------------------------------------------------------------------
;  Initialize RESULT to represent non-success.  If the routine is successful,
;  this value will be updated below.
;---------------------------------------------------------------------------
   result = 0
;
;  Make sure that the user has privilege to write into the database.
;
	IF !PRIV LT 2 THEN BEGIN $
           MSG = '!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.
;---------------------------------------------------------------------------
   type = datatype(def.cmp_no, 2)
   IF (type LT 2) OR (type GT 3) THEN BEGIN
      msg = 'Tag CMP_NO must be an integer'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.cmp_no) NE 1 THEN BEGIN
      msg = 'Tag CMP_NO must be a scalar'
      GOTO, handle_error
   ENDIF

;---------------------------------------------------------------------------
;  Check existence of campaign number in DB
;---------------------------------------------------------------------------
   msg = ''
   get_campaign, def.cmp_no, def_tmp, err=msg
   IF msg NE '' THEN GOTO, handle_error
   
   IF datatype(def.cmp_name, 1) NE 'String' THEN BEGIN
      msg = 'Tag CMP_NAME must be a character string'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.cmp_name) NE 1 THEN BEGIN
      msg = 'Tag CMP_NAME must be a scalar'
      GOTO, handle_error
   END ELSE IF STRLEN(def.cmp_name) GT 120 THEN BEGIN
      def.cmp_name = STRMID(def.cmp_name, 0, 120)
      MESSAGE, 'Tag CMP_NAME trimmed to 120 characters long.', /cont
   ENDIF

   IF datatype(def.cmp_type, 1) NE 'String' THEN BEGIN
      msg = 'Tag CMP_TYPE must be a character string'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.cmp_type) NE 1 THEN BEGIN
      msg = 'Tag CMP_TYPE must be a scalar'
      GOTO, handle_error
   END ELSE IF STRLEN(def.cmp_type) GT 20 THEN BEGIN
      def.cmp_type = STRMID(def.cmp_type, 0, 20)
      MESSAGE, 'Tag CMP_TYPE trimmed to 20 characters long.', /cont
   ENDIF

   IF datatype(def.observer, 1) NE 'String' THEN BEGIN
      msg = 'Tag OBSERVER must be a character string'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.observer) NE 1 THEN BEGIN
      msg = 'Tag OBSERVER must be a scalar'
      GOTO, handle_error
   END ELSE IF STRLEN(def.observer) GT 120 THEN BEGIN
      def.observer = STRMID(def.observer, 0, 120)
      MESSAGE, 'Tag OBSERVER trimmed to 120 characters long.', /cont
   ENDIF

   IF datatype(def.cmp_desc, 1) NE 'String' THEN BEGIN
      msg = 'Tag CMP_DESC must be a character string'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.cmp_desc) GT 5 THEN BEGIN
      msg = 'Tag CMP_DESC must have no more than 5 elements'
      GOTO, handle_error
   END ELSE IF MAX(STRLEN(def.cmp_desc)) GT 80 THEN BEGIN
      MSG = 'Each element of tag CMP_DESC must be 80 characters or less'
      GOTO, HANDLE_ERROR
   ENDIF

   IF datatype(def.date_obs, 1) NE 'Double' THEN BEGIN
      msg = 'Tag DATE_OBS must be a double precision number'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.date_obs) NE 1 THEN BEGIN
      msg = 'Tag DATE_OBS must be a scalar'
      GOTO, handle_error
   ENDIF

   IF datatype(def.date_end, 1) NE 'Double' THEN BEGIN
      msg = 'Tag DATE_END must be a double precision number'
      GOTO, handle_error
   END ELSE IF N_ELEMENTS(def.date_end) NE 1 THEN BEGIN
      msg = 'Tag DATE_END must be a scalar'
      GOTO, handle_error
   ENDIF

   IF TAG_EXIST(DEF,'COMMENT') THEN BEGIN
      IF datatype(def.comment, 1) NE 'String' THEN BEGIN
         msg = 'Tag COMMENT must be a character string'
         GOTO, handle_error
      END ELSE IF N_ELEMENTS(def.comment) NE 1 THEN BEGIN
         msg = 'Tag COMMENT must be a scalar'
         GOTO, handle_error
      END ELSE IF STRLEN(def.comment) GT 80 THEN BEGIN
         def.comment = STRMID(def.comment, 0, 80)
         MESSAGE, 'Tag COMMENT trimmed to 80 characters long.', /cont
      ENDIF
   ENDIF

   FOR I=0,N_ELEMENTS(DEF.INSTITUTES)-1 DO BEGIN
	INST = DEF.INSTITUTES(I)

	IF datatype(inst.institut, 1) NE 'String' THEN BEGIN
	      msg = 'Subtag INSTITUT must be a character string'
	      GOTO, handle_error
	END ELSE IF N_ELEMENTS(inst.institut) NE 1 THEN BEGIN
	      msg = 'Subtag INSTITUT must be a scalar'
	      GOTO, handle_error
	END ELSE IF STRLEN(inst.institut) GT 80 THEN BEGIN
	      inst.institut = STRMID(inst.institut, 0, 80)
	      MESSAGE, 'Subtag INSTITUT trimmed to 80 characters long.', /cont
	ENDIF

	IF datatype(inst.observer, 1) NE 'String' THEN BEGIN
	      msg = 'Subtag OBSERVER must be a character string'
	      GOTO, handle_error
	END ELSE IF N_ELEMENTS(inst.observer) NE 1 THEN BEGIN
	      msg = 'Subtag OBSERVER must be a scalar'
	      GOTO, handle_error
	END ELSE IF STRLEN(inst.observer) GT 120 THEN BEGIN
	      inst.observer = STRMID(inst.observer, 0, 120)
	      MESSAGE, 'Subtag OBSERVER trimmed to 120 characters long.', /cont
	ENDIF

	DEF.INSTITUTES(I) = INST
   ENDFOR

;---------------------------------------------------------------------------
;  Make sure that the stop date is greater than the start date
;---------------------------------------------------------------------------
   if def.date_end lt def.date_obs then begin
      msg = 'The stop date must be greater or equal to the start date'
      GOTO, handle_error
   ENDIF

;---------------------------------------------------------------------------
;  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 entry is already in the database.
;
	GET_CAMPAIGN, DEF.CMP_NO, OLD
	IF OLD.CMP_NO LT 0 THEN BEGIN
	   MESSAGE = 'Campaign number ' + TRIM(DEF.CMP_NO) +	$
		   ' is not in the database'
	   GOTO, HANDLE_ERROR
	ENDIF

;-- trim all string blanks

        trim_campaign,def
;
;  Concatenate the multiline campaign descriptions into single strings.
;
	CMP_DESC = STRING(REPLICATE(32B,400))
	FOR I=0,N_ELEMENTS(DEF.CMP_DESC)-1 DO	$
		STRPUT,CMP_DESC,(DEF.CMP_DESC)(I),80*I
	OLD_DESC = STRING(REPLICATE(32B,400))
	FOR I=0,N_ELEMENTS(OLD.CMP_DESC)-1 DO	$
		STRPUT,OLD_DESC,(OLD.CMP_DESC)(I),80*I
;
;  Open the database for write access, and find the exact record to modify.
;
	DBOPEN, 'campaign', 1
	ENTRY = DBFIND('CMP_NO=' + TRIM(DEF.CMP_NO), /SILENT)
	ENTRY = ENTRY(0)
;
;  For each entry, check to see if it has changed.  If it has, then update the
;  database.
;
	MODS = 0
;
	IF DEF.CMP_NAME NE OLD.CMP_NAME THEN BEGIN
		DBUPDATE, ENTRY, 'cmp_name', DEF.CMP_NAME
		PRINT, 'Modified parameter CMP_NAME'
		MODS = MODS + 1
	ENDIF

	IF DEF.CMP_TYPE NE OLD.CMP_TYPE THEN BEGIN
		DBUPDATE, ENTRY, 'cmp_type', DEF.CMP_TYPE
		PRINT, 'Modified parameter CMP_TYPE'
		MODS = MODS + 1
	ENDIF

	IF CMP_DESC NE OLD_DESC THEN BEGIN
		DBUPDATE, ENTRY, 'cmp_desc', CMP_DESC
		PRINT, 'Modified parameter CMP_DESC'
		MODS = MODS + 1
	ENDIF
;
	IF DEF.DATE_OBS NE OLD.DATE_OBS THEN BEGIN
		DBUPDATE, ENTRY, 'date_obs', DEF.DATE_OBS
		PRINT, 'Modified parameter DATE_OBS'
		MODS = MODS + 1
	ENDIF
;
	IF DEF.DATE_END NE OLD.DATE_END THEN BEGIN
		DBUPDATE, ENTRY, 'date_end', DEF.DATE_END
		PRINT, 'Modified parameter DATE_END'
		MODS = MODS + 1
	ENDIF
;
	IF DEF.OBSERVER NE OLD.OBSERVER THEN BEGIN
		DBUPDATE, ENTRY, 'observer', DEF.OBSERVER
		PRINT, 'Modified parameter OBSERVER'
		MODS = MODS + 1
	ENDIF
;
;  Open the comments database for write access, and see if the comment has
;  changed.
;
	IF TAG_EXIST(DEF,'COMMENT') THEN BEGIN
	    IF TAG_EXIST(OLD,'COMMENT') THEN OLD_COMMENT = OLD.COMMENT ELSE $
		    OLD_COMMENT = ''
	    IF TRIM(DEF.COMMENT) NE OLD_COMMENT THEN BEGIN
		DBUPDATE, ENTRY, 'comments', 'Y'
		DBCLOSE
		DBOPEN, 'campaign_c', 1
		ENTRY = DBFIND('CMP_NO=' + TRIM(DEF.CMP_NO) + ', COMM_IND=1', $
			/SILENT)
		ENTRY = ENTRY(0)
		IF ENTRY(0) EQ 0 THEN BEGIN
			DBBUILD, DEF.CMP_NO, 1, DEF.COMMENT
		END ELSE BEGIN
			DBUPDATE, ENTRY, 'comment', DEF.COMMENT
		ENDELSE
	    ENDIF
	ENDIF
;
;  The institutes tag gets a little tricky.  If the total number has changed,
;  then it's best to delete all the old information, and write in the new.
;
	IF N_ELEMENTS(DEF.INSTITUTES) NE N_ELEMENTS(OLD.INSTITUTES) THEN BEGIN
	    DBOPEN, 'institutes', 1
	    ENTRIES = DBFIND('CMP_NO=' + TRIM(DEF.CMP_NO), /SILENT)
	    IF ENTRIES(0) NE 0 THEN DBDELETE, ENTRIES
;
	    N_INST = N_ELEMENTS(DEF.INSTITUTES)
	    DBBUILD, REPLICATE(DEF.CMP_NO, N_INST), INDGEN(N_INST)+1,	$
		    DEF.INSTITUTES.INSTITUT, DEF.INSTITUTES.OBSERVER,	$
		    REPLICATE('N', N_INST), STATUS=STATUS
	    IF STATUS EQ 0 THEN BEGIN
        	MSG = 'Write to institutes database was not successful'
	        GOTO, HANDLE_ERROR
	    ENDIF
;
;  If there weren't any institutes before, then one needs to add in the new
;  records.
;
	END ELSE BEGIN
	    DBOPEN, 'institutes', 1
	    ENTRIES = DBFIND('CMP_NO=' + TRIM(DEF.CMP_NO), /SILENT)
	    IF ENTRIES(0) EQ 0 THEN BEGIN
                N_INST = N_ELEMENTS(DEF.INSTITUTES)
		DBBUILD, REPLICATE(DEF.CMP_NO, N_INST), INDGEN(N_INST)+1, $
			DEF.INSTITUTES.INSTITUT, DEF.INSTITUTES.OBSERVER, $
			REPLICATE('N', N_INST), STATUS=STATUS
		IF STATUS EQ 0 THEN BEGIN
        	    MSG = 'Write to institutes database was not successful'
		    GOTO, HANDLE_ERROR
		ENDIF
;
;  Otherwise, simply replace the old information with the new information.
;
	    END ELSE BEGIN
		ENTRIES = DBSORT(ENTRIES, 'inst_ind')
		FOR I = 0, N_ELEMENTS(DEF.INSTITUTES)-1 DO BEGIN
		    INST = DEF.INSTITUTES(I)
		    OLD_INST = OLD.INSTITUTES(I)
;
		    IF INST.INSTITUT NE OLD_INST.INSTITUT then begin
			DBUPDATE, ENTRIES(I), 'institut', INST.INSTITUT
			PRINT, 'Modified parameter INSTITUT(' + TRIM(I) + ')'
			MODS = MODS + 1
		    ENDIF
;
		    IF INST.OBSERVER NE OLD_INST.OBSERVER then begin
			DBUPDATE, ENTRIES(I), 'observer', INST.OBSERVER
			PRINT, 'Modified parameter OBSERVER(' + TRIM(I) + ')'
			MODS = MODS + 1
		    ENDIF
		ENDFOR
	    ENDELSE
	ENDELSE
;
;  Signal success.
;
	RESULT = 1
	GOTO, FINISH

;---------------------------------------------------------------------------
;  Error handling point
;---------------------------------------------------------------------------
handle_error:
   IF N_ELEMENTS(errmsg) NE 0 THEN $
      errmsg = 'Mod_campaign: ' + msg $
   ELSE $
      MESSAGE, msg, /continue

finish:
   DBCLOSE
   RETURN, result

END

