pro fits_add_checksum, hdr, im, no_timestamp = no_timestamp, $
              FROM_IEEE=from_IEEE
;+
; NAME:
;    FITS_ADD_CHECKSUM
; PURPOSE:
;    Add or update the CHECKSUM and DATASUM keywords in a FITS header
; EXPLANATION: 
;     Follows the May 2002 version of the FITS checksum proposal at 
;     http://fits.gsfc.nasa.gov/registry/checksum.html 
; CALLING SEQUENCE:
;     FITS_ADD_CHECKSUM, Hdr, [ Data, /No_TIMESTAMP, /FROM_IEEE ]
; INPUT-OUTPUT:
;     Hdr - FITS header (string array), it will be updated with new 
;           (or modified) CHECKSUM and DATASUM keywords 
; OPTIONAL INPUT:
;     Data - data array associated with the FITS header.   If not supplied, or
;           set to a scalar, then the program checks whether there is a 
;           DATASUM keyword already in the FITS header containing the 32bit
;           checksum for the data.  If there is no such keyword then there 
;           assumed to be no data array associated with the FITS header.
; OPTIONAL INPUT KEYWORDS:
;    /FROM_IEEE - If this keyword is set, then the input is assumed to be in 
;             big endian format (e.g. an untranslated FITS array).    This 
;             keyword only has an effect on little endian machines (e.g. 
;             a Linux box).
;    /No_TIMESTAMP - If set, then a time stamp is not included in the comment
;             field of the CHECKSUM and DATASUM keywords.   Unless the 
;             /No_TIMESTAMP keyword is set, repeated calls to FITS_ADD_CHECKSUM
;             with the same header and data will yield different values of 
;             CHECKSUM (as the date stamp always changes).   However, use of the
;             date stamp is recommended in the checksum proposal. 
; PROCEDURES USED:
;     CHECKSUM32, FITS_ASCII_ENCODE(), GET_DATE, SXADDPAR, SXPAR()
; REVISION HISTORY:
;     W. Landsman    SSAI    December 2002
;     Fix problem with images with a multiple of 2880 bytes.  W.L. May 2008
;     Avoid conversion error when DATASUM is an empty string  W.L.  June 2008
;     Don't update DATASUM if not already present and no data array supplied 
;                       W.L. July 2008 
;     Make sure input header array has 80 chars/line  W.L. Aug 2009
;     Make sure CHECKSUM is placed right after an already present DATASUM
;     keyword. Mats Löfdahl October 2019
;-
 On_error,2
 compile_opt idl2

 if N_params() EQ 0 then begin 
     print,'Syntax - FITS_ADD_CHECKSUM, Hdr, Data, /No_TIMESTAMP, /FROM_IEEE'
     return
 endif

 datasum = sxpar(hdr,'DATASUM', Count = N_DATASUM)
 Nim = N_elements(im)
 datasum_update = 1b
 if Nim GT 1 then begin
     checksum32,im, dsum,FROM_IEEE = from_IEEE
     remain = Nim mod 2880
     if remain GT 0 then begin
         exten = sxpar( hdr, 'XTENSION', Count = N_exten)
         if N_exten GT 0 then if exten EQ 'TABLE   ' then $
                 checksum32,[dsum,replicate(32b,2880-remain)],dsum
    endif
    sdsum = strtrim(dsum,2)
    dsum_exist= 1b
 endif else begin 
        if N_datasum EQ 0 then begin      ;Don't update DATASUM keyword 
	      datasum_update = 0b     
 	      sdsum = '         0' 
	 endif else begin
	   if strtrim(datasum,2) EQ '' then dsum=0 else dsum = ulong(datasum)
           sdsum = strtrim(dsum,2)
       endelse   
 endelse 
 
 if keyword_set(no_timestamp) then tm = '' else Get_date,tm,/timetag

; Do the Checksum keywords already exist?

  if N_DATASUM GT 0 then verb = 'updated ' else verb = 'created '
  if datasum_update then sxaddpar,hdr,'DATASUM', sdsum,  $
    ' data unit checksum ' + verb + tm

 test = sxpar(hdr,'CHECKSUM', Count = N_CHECKSUM)
 if N_CHECKSUM GT 0 then verb = 'updated ' else verb = 'created '
 sxaddpar,hdr,'CHECKSUM','0000000000000000', after = 'DATASUM', $
          ' HDU checksum ' + verb + tm ;Initialize CHECKSUM keyword
;Make sure each line in header is 80 characters
 if ~array_equal(strlen(hdr),80) then begin
     n = N_elements(hdr)
     bhdr = replicate(32b,80,n )
     for i=0, n-1 do bhdr[0,i] = byte(hdr[i])
 endif else bhdr = byte(hdr)

 remain = N_elements(bhdr) mod 2880 
 if remain  NE 0 then $
       bhdr = [reform(bhdr,N_elements(bhdr)), replicate(32b, 2880 - remain) ]
 checksum32,bhdr, hsum, /NoSAVE
 if N_elements(dsum) GT 0 then checksum32, [dsum,hsum], hdusum $
                        else hdusum = hsum
 
 ch = FITS_ASCII_ENCODE(not hdusum) ;ASCII encode the complement of the checksum 
 sxaddpar,hdr,'CHECKSUM',ch, after = 'DATASUM'

 return
 end
