;+
; NAME:
;     check_cals
; PURPOSE:
;     Given an optional date (system date used if not specified), the function
;     will return 5 calibrators for use in the build_schedule procedure
; CATEGORY:
;     OVRO APC DATA CALIBRATION
; CALLING SEQUENCE
;     x = check_cals(date)
; INPUT PARAMETERS:
;     date       the date in the format : YYYY-MON-DD where MON is the first
;                3 letters of the month
; ROUTINES CALLED:
;     External: uptime, anytim, msec2str, hms2sec
; OUTPUTS:
;     The output a structure with the calibrators that are found to meet the specifications
; COMMENTS:
;       The variable minlimit is the minimum time between two calbibrators.
; SIDE EFFECTS:
; RESTRICTIONS:
; MODIFICATION HISTORY:
;     Written 13-Mar-2002 by Dale E. Gary and Garrett Smith
;     22-Jun-2007  DG
;       The date was not being passed to UPTIME.  Added conversion to JD and passing to UPTIME.
;       This only effects
;-


function check_cals,date

   ; Convert date string to JD
   jd = (anytim(date,/mjd)).mjd + 2400000.5D
   ; Read the calibrators that are used for OVSA
   cals = uptime(7,JD=jd)

   ;special variable
      minlimit = 60.            ;minimum time between two calbibrators (the end of calibration
                                ;from one to the start time of the other)

   hsi_obs = read_hsi_obs(date=date)
   ovsa = where(hsi_obs.sset gt anytim(cals.info[n_elements(cals.info)-1].rise +':00') - 1800.0 ,nobs)  ;hms2sec(164500)
   if (nobs eq 0) then return,0
   hsi_obs = hsi_obs[[0,ovsa,1]]


   ;dummy time before sunrise for setup of the pre-sunrise calibrator
   hsi_obs[0].SSET = anytim(cals.info[n_elements(cals.info)-1].rise +':00') - 1800.0
   hsi_obs[1].SRISE = anytim(cals.info[n_elements(cals.info)-1].rise +':00')
   hsi_obs[0].DUR = 30.0

   ;dummy time after sunset for setup of the post-sunset calibrator
   hsi_obs[nobs].SSET = anytim(cals.info[n_elements(cals.info)-1].set +':00')
   hsi_obs[nobs+1].SRISE = anytim(cals.info[n_elements(cals.info)-1].set +':00') + 1800.0

   ;add 2 to the nobs for the dummy dates
   nobs = nobs + 2

   ; Get the list of setting times [s] that are of interest
   h = (hsi_obs[0:nobs-2].sset)/60.
   ; Get the list of rising times [s] that are of interest
   hp = (hsi_obs[1:*].srise)/60.
   ; Keep only non-obs periods that are greater than 20 min long
   good = where(hp-h gt 20.,ngood)
   h = h[good]
   hp = hp[good]
   nobs = ngood
   dur = hp-h
   d = fltarr(nobs)+20.
   d[0] = 30
   d[nobs-1] = 30
   dorig = d

   ; Ignore Cas A, Cyg A, and the Sun (which are appended to the cal list)
   ncals = n_elements(cals.info)-3
   ; Specify best order (in case of a tie), as 3C84, 3C279, 3C273, 3C454.3, 3C345
   calorder = [0,2,1,4,3]

   ; Create array to hold the best calibrator index for each observation period
   upcal = intarr(nobs)
   upstat  = strarr(nobs)

   for i = 0, nobs-1 do begin

try_again:
      ; Check start time of HESSI night
      time_since_rise = (h[i] - anytim(cals.info[calorder].rise+':00')/60. + 1440) mod 1440
      time_til_set = (anytim(cals.info[calorder].set +':00')/60. - (h[i]+d[i]) + 1440.) mod 1440.
      up1s = time_since_rise ge 0 and time_since_rise le 7.95*60.  ; True if source is up at start
      up2s = time_til_set ge 0 and time_til_set le 7.95*60.        ; True if source is up 20 min after start
      ; Check end time of HESSI night
      time_since_rise = (hp[i]-d[i] - anytim(cals.info[calorder].rise+':00')/60. + 1440) mod 1440
      time_til_set = (anytim(cals.info[calorder].set +':00')/60. - hp[i] + 1440.) mod 1440.
      up1e = time_since_rise ge 0 and time_since_rise le 7.95*60.  ; True if source is up 20 min before end
      up2e = time_til_set ge 0 and time_til_set le 7.95*60.        ; True if source is up at end

      up_always = up1s and up2s and up1e and up2e           ; True if sources are up during entire interval
      up_start = (up1s and up2s) and (byte(1) - (up1e and up2e))  ; True if up only at start
      up_end   = (byte(1) - (up1s and up2s)) and (up1e and up2e)  ; True if up only at end

      ; Find which case gives best calibrator
      best = where(up_always+up_start+up_end,nbest)
      if (nbest eq 0) then begin
         ; No possible calibrators!
         ; See if we can reduce time on this calibrator
         if (d[i] gt 15) then begin
            possible = where(time_til_set le 0,nposs)
            if (nposs eq 1) then begin
               d[i] = d[i] + time_til_set[possible]
               goto, try_again
            endif
         endif
         upcal[i] = ncals+2
         upstat[i] = 'none'
      endif else begin
         best = best[0]  ; This is the index of the best calibrator--now find when it is up
         upcal[i] = calorder[best]
         case 1 of
            up_always[best]: begin   ; The best calibrator is up during the whole time
                    upstat[i] = 'always'
                 end
             up_start[best]: begin    ; The best calibrator is up only at the start
                    upstat[i] = 'at start only'
                 end
               up_end[best]: begin    ; The best calibrator is up only at the end
                    upstat[i] = 'at end only'
                 end
         endcase
      endelse
   endfor
   good_calstimes = strarr(nobs)
   good_calstimesfloat = fltarr(nobs)
   num_good = 0
   for i = 0, nobs-1 do begin
      case upstat[i] of
      'always' : begin
                  good_calstimes[i] = strmid(msec2str( (h[i]+ (dur[i]-d[i]) /2) *60000.),1,5) + ': ' + cals.info[upcal[i]].name
                  good_calstimesfloat[i] = (h[i]+(dur[i]-d[i])/2) *60.
                  num_good = num_good + 1
                 end
      'at start only': begin
                  good_calstimes[i] = strmid(msec2str(h[i]*60000.),1,5) + ': ' + cals.info[upcal[i]].name
                  good_calstimesfloat[i] = h[i]*60.
                  num_good = num_good + 1
                 end
      'at end only': begin
                  good_calstimes[i] = strmid(msec2str((hp[i]-d[i])*60000.),1,5) + ': ' + cals.info[upcal[i]].name
                  good_calstimesfloat[i] = (hp[i]-d[i])*60.
                  num_good = num_good + 1
                 end
      'none': begin
                  good_calstimes[i] = strmid(msec2str(h[i]*60000.),1,5) + ': ' + cals.info[upcal[i]].name
                  good_calstimesfloat[i] = h[i]*60.
                  num_good = num_good + 1
                 end
      endcase
   endfor

   out = strarr(num_good) ;reduced string array of the start times
   cal_ranking = intarr(num_good) ;reduced int array of the calibrator used
   stimes = fltarr(num_good) ; reduced and corrected float array of start times
   pl = 0                   ; previous location in the larger calibrator arrays (so the loop does not overwrite data)
   for k = 0, num_good - 1 do begin ; for loop that deals with the reduced arrays that starts for the beginning of the arrays goes to the end of the arrays
      for i = pl, nobs - 1 do begin ; for loop that deals with the larger arrays that starts at the last position + 1 of the arrays to the end of the arrays
         if (upstat[i] eq 'always' or upstat[i] eq 'at start only' or upstat[i] eq 'at end only') then begin      ; if the upstat[i] contains data then
            out[k] = good_calstimes[i]  ; assign good_calstimes[i] to out[k]
            case upcal[i] of        ;convert the ranking of the calibrators to 1 to 5 scale (1 = best. 5 = worst)
               0: cal_ranking[k] = 1    ; 3C84
               2: cal_ranking[k] = 2    ; 3C279
               1: cal_ranking[k] = 3    ; 3C273
               4: cal_ranking[k] = 4    ; 3C454.3
               3: cal_ranking[k] = 5    ; 3C345
            endcase                 ;end rank convertion
            if (good_calstimesfloat[i] lt anytim(cals.info[n_elements(cals.info)-1].rise +':00') - 1800.0 ) then begin ; if good_calstimesfloat[i] is less than 40 mins before sunrise then
                stimes[k] = good_calstimesfloat[i] + hms2sec(240000)    ; add a day since is is in reality is the next day
            endif else begin                                          ; end if else
                stimes[k] = good_calstimesfloat[i]                      ; assign good_calstimesfloat[i] to stimes[k]
            endelse                                                   ; end else
            pl = i + 1                                                ; set the pl equal to i + 1
            goto,break                                                     ; since data has been found, and assigned for this value of k, end the i loop
         endif                                                      ;end if
      endfor
      break:                                                  ; end for
   endfor                                                   ; end for

   if (num_good gt 5) then begin        ; if good_num is greater than five then try to remove the extras
      final_output = strarr(5)          ; set up the final output array
      removed_count = 0;                ; start the removed_count at 0
      for k = 1, num_good - 2 do begin                                              ;cycle through the list of good calibrators, skipping the fist and the last
        if ( num_good - removed_count ge 6) then begin                              ;if there are more that 6 calibrators contiue the for loop
         if ( cal_ranking[k] gt cal_ranking[k - 1] or k - 1 eq 0) then begin            ;if the the ranking of the current calibrator k is worst than the previous calibrator OR the previous calibrator is k - 1 = 0 then
            if ((stimes[k] - stimes[k-1]) le (minlimit+30)*60) then begin                   ; if the time difference between the two calibrators is less than (minlimit+30)*60 which is the timelimit plus the duration of the calibration in seconds then
               cal_ranking[k] = 6                                                               ;set the ranking to 6, this effectively removies it from the list
               removed_count = removed_count + 1                                                ;add one to the removed_count
            endif                                                                           ;end if
         endif else begin                                                               ;else
           if ( cal_ranking[k] gt cal_ranking[k + 1] or k + 1 eq num_good - 1) then begin   ;if the the ranking of the current calibrator k is worst than the next calibrator OR the next calibrator is k + 1 = num_good-1 then
             if ((stimes[k+1] - stimes[k]) le 60*(minlimit+20)) then begin                      ;if the time difference between the two calibrators is less than (minlimit+20)*60 which is the timelimit plus the duration of the calibration in seconds then
               cal_ranking[k] = 6                                                                   ;set the ranking to 6, this effectively removies it from the list
               removed_count = removed_count + 1                                                    ;add one to the removed_count
             endif                                                                              ;endif
           endif else begin                                                                 ;else
               if ((cal_ranking[k] eq cal_ranking[k - 1]) or (cal_ranking[k] eq cal_ranking[k + 1])) then begin  ;if the ranking of the previous or the next calibrator is equal to the calibrator k then
                  if (((stimes[k+1] - stimes[k]) le 60*(minlimit+20)) or ((stimes[k] - stimes[k-1]) le 60*(minlimit+20))) then begin  ;if the diffence in time between the previous or the next calibrators and cal k is less than (minlimit+20)*60 then
                     cal_ranking[k] = 6                                                              ;set the ranking to 6, this effectively removies it from the list
                     removed_count = removed_count + 1                                               ;add one to the removed_count
                  endif                                                                             ;endif
               endif                                                                            ;endif
           endelse                                                                          ;endelse
         endelse                                                                        ;endelse
        endif                                                                       ;endif
      endfor                                                                    ;endfor
      if (num_good - removed_count ge 6) then begin         ; if there are STILL more than 5 calibrators then
          if (stimes[1] - stimes[0] gt stimes[5] - stimes[4])   then begin
              cal_ranking[1] = 6    ;the 2nd calibrator is removed
          endif else begin
              cal_ranking[4] = 6    ;the 5th calibrator is removed
          endelse                                                   ;endif
      endif
      final_num = where(cal_ranking lt 6, nfinal)  ; find where the calibrators are less than 6
      final_output = out(final_num)                ; set final_output equal to final_num
   endif else begin      ;else
      final_output = out ;just set final_output to out
   endelse               ;end else
   ;Start printing output and final_output
   ;print,'final_output'
   ;for i = 0,nfinal-1 do begin
   ;   print,final_output[i]
   ;endfor
   ;print,'orignal output'
   ;for i = 0, num_good - 1 do begin
   ;      print, out[i]
   ;endfor
   ;stop printing
   cals_struct = {name:strarr(nfinal), time:stimes[final_num], tadj:(d[final_num]-dorig[final_num])}
   for i = 0,nfinal - 1 do begin
     cals_struct.name[i] = strmid(final_output[i],7)
   endfor
return,cals_struct
end