;
;Copyright 1996-2013 United States Government as represented by the
;Administrator of the National Aeronautics and Space Administration.
;All Rights Reserved.
; This software may be used, copied, or redistributed as long as it is not
; sold and this copyright notice is reproduced on each copy made.  This
; routine is provided as is without any express or implied warranties
; whatsoever.
;

function cmb_bin_data,d,dt_sec= dt_sec,dates=dates, sigmul=sigmul,vars=vars,multiple_modes=multiple_modes $
                     ,cdfepochtype=cdfepochtype,diagnostic=diagnostic,OVERRIDE_DEFAULT_BINNING=override_default_binning $
                     ,nointerpolation=nointerpolation

;+
; EXAMPLE USAGE:
;   status= cmb_bin_data( d,dates=[start,stop],dt_sec = dt_sec)
;
; NAME:
;   cmb_bin_data
;            
; PURPOSE:   
; This procedure creates uniformly spaced binned data for the specified variables and data set.
; Bins containing no data are interpolated using nearest neighbor bins that contain data.
; It then creates these variables and their metadata on the calling level of your current IDL session.
;
; CATEGORY:
; Data binning.
;
; CALLING SEQUENCE:                                   
;  status= cmb_bin_data( d, d,dt_sec= dt_sec,dates=dates,sigmul=sigmul,vars=vars,multiple_modes=multiple_modes,cdfepochtype=cdfepochtype)
;                                                     
; INPUTS:                                             
; d = data structure returned by read_mycdf.pro
;
; Keyword Inputs:
;   dates: string of start/stop times; e.g. start='2014/01/20 00:00:00.000',  stop='2014/01/20 03:00:00.000'
;   dt_sec: time interval in seconds of the time bin width, in double precision.
;   sigmul: if set and sigmul >= 1, sigmul is the multiplicative factor of standard deviation for rejection of data: 
;           5 (default),  4 (less aggressive), 6 (more aggressive). Note is sigmul < 1 or not defined filtering is skipped.
;   multiple_modes =1 (default) then set the non-dominant modes to fill
;                  =0 interpolate all modes to the dominant mode 
;           this keyword was introduced because some multi dependency variables are composed of multiple modes, 
;           an example of possible mixture of modes is ion flux as function of time and energy where energy is time dependent.
;   vars: list of cdf variable names,  i.e. vars = ['Magnitude', 'BRTN']
;       Note: cdf variable names are case sensitive.
;       The default output variable name is the cdf variable name.
;       To rename output variables and/or break them into components use following syntax:
;       vars =['cdfvariablename1=var','cdfvariablename2=var1,var2,var3']
;       the later breaks 'cdfvariablename2' into components named 'var1','var2','var3',
;       Note: the number of specified output components must equal the number of components for that cdf variable.
;       For example vars = ['Magnitude=B0', 'BRTN=Bvec'],
;       would create output variables named 'B0' and 'BVEC'.
;       For example vars = ['Magnitude=B0', 'BRTN=Bx,By,Bz'],
;       would create output variables named 'B0' and 'Bx','By','Bz' (components) instead of 'Magnitude' and 'BRTN' (vector).
;   nointerpolation =1 (leave empty bins empty), 0 (DEFAULT,linear interpolate using non-empty neighboring bins)
;
; OUTPUTS:
;   1 is successful
;   0 if not, original unbinned values returned.
;
; COMMON BLOCKS:
;   None.
;
; SIDE EFFECTS:
;   Unknown.
;
; RESTRICTIONS:
;   Unknown.
;
; FUNCTION:
;
; MODIFICATION HISTORY:
;   Code developed by Aaron Roberts and Scott Boardsen at GSFC.
; NOTES:
; Only bins data if attributes var_type ='data' and if allow_bin is defined, it must not equal allow_bin='FALSE'. 
;-
; Catch statement to help with error handling.
; All errors result in returning a status of 0.
; Ron Yurow (Jan 20, 2021)
CATCH, err_status

IF  err_status NE 0 THEN BEGIN
    PRINT, 'Error index: ', err_status
    PRINT, 'Error message: ', !ERROR_STATE.MSG
    RETURN, 0
ENDIF


if cmb_var_type(d) ne 'STRUCT' then return,0
if n_elements(dates) ne 2 then return,0
if keyword_set(nointerpolation) then  fill_empty_bins=0 else fill_empty_bins=1
if n_elements(dt_sec) eq 0 then dt_sec = 0d0 ; seconds, if not set do not bin
if n_elements(sigmul) eq 0 then sigmul=0
; Force override_default_binning keyword to 0 if not passed.  Ron Yurow (2/3/20)
if n_elements(override_default_binning) eq 0 then override_default_binning = 0;
if sigmul ge 1. then autobad = 1

if dt_sec le 0 and sigmul ge 1. then begin
   cmb_spike_editor0,d,sigmul
end
if dt_sec le 0 then return,0
;if n_elements(cdfepochtype) eq 0 then cdfepochtype='CDF_EPOCH'
depend0 = cmb_cdf_get_depend0(d) 
if n_elements(cdfepochtype) eq 0 then cdfepochtype=depend0[0].cdftype
if cdfepochtype eq 'CDF_EPOCH16' then cdfepochtype = 'CDF_TIME_TT2000'
dtbin = cmb_dtbin( dt_sec, cdfepochtype=cdfepochtype)

time_name = 'EPOCH_BIN'
av = cmb_cdf2user_var(vars,d)
; Check to make sure that a least one variable was found that can be processed
; If there were no binnable data variables available, then exit now. Ron Yurow
if  size (av, /type) ne 8 then return, 0 
tnames = tag_names(d)
nvars = n_elements(av.cdfvar)
epr = cmb_string2epr(dates,cdfepochtypeout=cdfepochtype) ;start/stop times in epoch
tbeg = epr[0]
tend = epr[1]
diagnostic={dtbin:dtbin,tbeg:tbeg, tend:tend}

; Set up some variables 
; Ron Yurow (Nov 3, 021)
ii = 0                  ; index of variable being processed in the data structure.
prepass = 1             ; true during the pre-processing phase.
submode_rcrds = LIST () ; LIST minor mode index vectors sorted by order or depend0
                        ; variable names in the depend_0_name array.
depend_0_name = []      ; array of names of variables that are the epoch (depend0) 
                        ; for a variable with a time variant depend1/depend2 
                        ; (IE one that has multiple modes)
finish = 0              ; true at processing completion

; Change the loop structure to allow for a pre-pass phase where minor mode record
; indices are calculated from variables that have a time variant depend1/depend2
; and a second pass where all remaining processing is completed.   
; Ron Yurow (Nov 3, 2021) 
; for ii = 0, nvars-1 do begin
WHILE (~finish) DO BEGIN 
    print,'----------------------------------------------------' 
    ip = where( tnames eq strupcase(av.cdfvar[ii]) ) & if n_elements(ip) ne 1 then stop & ip = ip[0] ;bug fixed SAB 6/5/2014
    
    if ip eq -1 then goto, skipthisvar
    ; Added check to the keyword override_default_binning before responding to the ALLOW_BIN attribute Ron Yurow (02/03/20)
    if cmb_tag_name_exists('ALLOW_BIN',d.(ip)) and (keyword_set(override_default_binning) eq 0) then begin ;SAB 4/6/2016
       if d.(ip).allow_bin eq 'FALSE' then BEGIN
          print, 'ALLOW_BIN = FALSE, variable:', d.(ip).varname, ' not binned'
          if cmb_tag_name_exists('units',d.(ip)) then  $
                      d.(ip).units = d.(ip).units + ' (not binned)'
          goto, skipthisvar 
       ENDIF
    endif 
    iep=where( strlowcase(d.(ip).depend_0) eq strlowcase(tnames)) & iep=iep[0]
    VALIDMIN = 0
    VALIDMAX = 0
    if cmb_tag_name_exists('VALIDMIN',d.(ip)) and cmb_tag_name_exists('VALIDMAX',d.(ip)) then begin
       VALIDMIN = d.(ip).VALIDMIN
       VALIDMAX = d.(ip).VALIDMAX
    endif
    ;print,'name,valid min/max:', d.(0).varname, validmin, validmax ; for diagnostic
    if iep eq -1 then begin
       print,'CDF variable ' + av.cdfvar[ii] + ' is not dependent on time, skipped.'
       goto,skipthisvar
    endif
    ; set mode to the either the empty array (for prepass/normal processing) or the array of indexes 
    ; of submodes (if available)
    ; Ron Yurow (Nov 3, 2921)
    mode = []

    ; If we are in main processing phase then check if this variable has an epoch that we
    ; minor mode record indices for.  If it does, then set mode to correct array of indices
    ; for its epoch.
    ; Ron Yurow  (Nov 3, 2021)
    if (~prepass) then begin
        if  cmb_tag_name_exists ('depend_0',d.(ip)) && strlen (d.(ip).depend_0) gt 0 then begin
            dpnd_0_ind = where (d.(ip).depend_0 eq depend_0_name, found)
            if  (found gt 0) then begin
                mode = submode_rcrds [dpnd_0_ind [0], *]
            endif
        endif
    endif

    ; Added extra keywords to cmb_cdf_check_if_dependencies_are_time_varying to allow preprocessing
    ; and reformating.
    ; Ron Yurow (Nov 3, 2201)
    ; cmb_cdf_check_if_dependencies_are_time_varying,d, ip, multiple_modes=multiple_modes
    cmb_cdf_check_if_dependencies_are_time_varying,d, ip, multiple_modes=multiple_modes,mode=mode,prepass=prepass
    
    ; Check if we in the pre-pass.  If we are and if cmb_cdf_check_if_dependencies_are_time_varying
    ; returned an array minor mode record indices, then store those indices along with the accompanying 
    ; epoch.  In all cases, we are done with pre-processing and move on to the next variable.
    ; Ron Yurow (Nov 3, 2021)
    if  (prepass) then begin
        if  n_elements (mode) gt 0 then begin
            if  cmb_tag_name_exists ('depend_0',d.(ip)) && strlen (d.(ip).depend_0) gt 0 then begin
                dpnd0 = d.(ip).depend_0
                sink = where (dpnd0 eq depend_0_name, found)
                found = (n_elements (depend_0_name) gt 0)? found : 0
                if  (found eq 0) then begin
                    depend_0_name = [depend_0_name, dpnd0]
                    submode_rcrds.Add, mode
                endif
            endif
         endif
        goto, skipthisvar
    endif
 ;add by SAB 4/17/2014
    other_depend = cmb_cdf_get_dependencies(d, ip, /not_zero, level=level) & if other_depend[0] ne '' then other=', another dependency is ' + other_depend else other=''
    print,'CDF VARIABLE ', av.cdfvar[ii], ', now called ',av.uservar[ii],' whose independent variable is ', time_name, other
    print,'CREATED VARIABLE: ', cmb_var_label(av.uservar[ii], other_depend, time_name)
    note =  cmb_var_label(av.uservar[ii], other_depend, time_name)
    t_d = cmb_epoch_modify( cmb_dat(d.(iep)),cdfepochtypeout=cdfepochtype)
;    help,cdfepochtype, t_d,cmb_dat(d.(iep)) &stop   
    print,'Depend_0', tnames[iep]
;    help,t_d,cdfepochtype
    if dtbin le 0 then print,'Note data is not binned in time, original time dependency of ', av.cdfvar[ii],' is ',tnames[iep]
    values = cmb_dat(d.(ip))
    fillflag= d.(ip).fillval
    fillflag0= d.(ip).fillval ; SAB 2019/06/17
    ;if finite(fillflag) eq 0 then fillflag = cmb_valid_data_range(d,ip) ; SAB 2019/06/17       
    if finite(fillflag) eq 0 then begin  ; SAB 2019/06/17
       fillflag = -1e31
       jj = cmb_fillval(values,fillflag0, /ieq)
       if jj[0] ne -1 then values[jj] = fillflag
    endif
    ibreak = cmb_break_decision(av,ii,size(cmb_dat(d.(ip)),/structure))
    if ibreak eq -1 then goto,skipthisvar    
    if (ibreak) then begin ;break matrix in components
		   vname0 = cmb_var_name_components(av.uservar[ii])
		   for jj = 0, av.nc[ii]-1 do begin
		vname = vname0[jj]
		value = reform(values[jj,*])
		if n_elements(value) eq  1 then goto,skipthisvar
      ; Modified to pass the fillflag0 specially for use as the fill value for
      ; periods where the sample variance can not be calculated.  Ron Yurow (08/08/2019)
		cmb_timebin_array,t_d, value, tbeg, tend, dtbin, $
			 t_out, valueout, fillflag, valueoutuncertaintyofmean, $
			 serout_flag=valueout_nbin, fill_empty_bins=fill_empty_bins, $
			 autobad=autobad, sigmul=sigmul, fillflag0, $
			 VALIDMIN=VALIDMIN, VALIDMAX=VALIDMAX
		   endfor
    endif else begin ;don't break into componets
		vname = av.uservar[ii]
		if n_elements(values) eq  1 then goto, skipthisvar
      ; Modified to pass the fillflag0 specially for use as the fill value for
      ; periods where the sample variance can not be calculated.  Ron Yurow (08/08/2019)
		cmb_timebin_array,t_d, values, tbeg, tend, dtbin, $
			 t_out, valueout ,fillflag, valueoutuncertaintyofmean, $
			 serout_flag=valueout_nbin, fill_empty_bins=fill_empty_bins, $
			 autobad=autobad, sigmul=sigmul, fillflag0, $
			 VALIDMIN=VALIDMIN, VALIDMAX=VALIDMAX
    endelse
    
    if finite(fillflag0) eq 0 then begin  ; SAB 2019/06/17
       ; reset fillflag to intial value
       jj = cmb_fillval(valueout,fillflag, /ieq)
       if jj[0] ne -1 then valueout[jj] = fillflag0
    endif    
    
    if n_elements(d0) eq 0 then d0 = create_struct(time_name, cmb_epoch_modify(t_out,cdfepochtypeout=depend0[0].cdftype) )
    ;cmb_depend_0_modify,d0,d,ip,t_out
    d0 = create_struct(d0, vname, valueout, vname+'_NBIN', valueout_nbin)
    d0 = create_struct(d0, vname + '_bin_DELTA_MINUS_VAR', $
            valueoutuncertaintyofmean, $
            vname + '_bin_DELTA_PLUS_VAR', $
            valueoutuncertaintyofmean ) 
    if cmb_tag_name_exists('epoch', d0) then stop
    skipthisvar:
      ; Move to the next variable.  If we are in the pre-process phase and we have
      ; looked at all available variables, then move to normal processing phase.
      ; If we have processed all variables then exit the loop.
      ; Ron Yurow (Nov 3, 2021)
      ii++
      if  (prepass) then begin
          if  (ii eq nvars) then begin
              prepass = 0
              ii = 0
              ; mode=[] ; <<<< only for testing
          endif
      endif else begin
          if  (ii eq nvars) then finish = 1
      endelse
; Change loop closure to match loop type
; Ron Yurow (Nov 2021)
; endfor ;ii
ENDWHILE
print,'----------------------------------------------------'

print,'Created time variable ' + time_name
if cmb_var_type(d0) ne 'STRUCT' then return,1
d0 = create_struct(d0,'aux', create_struct('time_bin_width_sec',dt_sec, 'epr', epr))
;set time dependent dependencies to that of the dominant instrument mode
for ii = 0, nvars-1 do begin ;add by SAB 4/17/2014
    ip= where( strpos(tnames, strupcase(av.cdfvar[ii]) ) ne -1 ) & ip=ip[0] ;add by SAB 4/17/2014
    if ip[0] ne -1 then cmb_cdf_check_if_dependencies_are_time_varying,d, ip,/set_depend_to_dominant_mode ;add by SAB 4/17/2014
endfor ;add by SAB 4/17/2014

dummy = CMB_CDF_GET_DEPENDENCIES(d,/write,/not_zero, d0=d0) ;  time dependent dependencies for variables that where binned.
; save,d,d0 ; note comment out after testing cmb_updatestructwith_epoch_bin
;cmb_check_delta, d, d0 ; not need SAB 2018/09/26
cmb_cdf_updatedatastructure,d,d0 ; overwrite input handles with handles pointing to the binned data
cmb_add_dependencies_of_binned_data, d, d0 ; SAB 2018/09/26
;cmb_fix_data_dependencies,d,d0 ; superseeded by cmb_add_dependencies_of_binned_data, SAB 2018/09/26
;d = cmb_updatestructwith_epoch_bin(d,d0) ; replace the depend_0 values to 'EPOCH_BIN' for variables that where binned.
;cmb_meta_validate,d  ;fixes depend_0 metadata
;cmb_cdf_nbin_var_type,d ;change new _bin variables to support data.
if cmb_var_type(d0) eq 'STRUCT' then return, 1 else return,0 ; SAB 2018 1/9
;return,1
end