;+ 
;NAME:
;  thm_ui_part_getspec_options
; 
;PURPOSE:
;  A interface to thm_part_products.pro for creating and loading THEMIS energy/
;  angular particle spectra into the GUI.  Intended to be called from
;  SPD_UI_INIT_LOAD_WINDOW.PRO using the SPEDAS load API.
; 
;CALLING SEQUENCE:
;  thm_ui_part_getspec_options, tab_id, loadedData, historyWin, statusBar, $
;                               treecopy, trObj, callSequence, $
;                               loadTree=loadTree, $
;                               timeWidget=timeid
;
;INPUT:
;  tab_id:  The widget id of the tab.
;  loadedData:  The loadedData object.
;  historyWin:  The history window object.
;  statusText:  The status bar object for the main Load window.
;  treeCopyPtr:  Pointer variable to a copy of the load widget tree.
;  trObj:  The GUI timerange object.
;  callSequence:  Reference to GUI call sequence object
;
;OUTPUT:
;  loadTree = The Load widget tree.
;  timeWidget = The time widget object.
;
;NOTES:
;
; 
;HISTORY:
; 5-jan-2009, jmm, jimm@ssl.berkeley.edu
; 14-jan-2009, jmm, added statusbar object
; 15-jan-2009, jmm, added external_state, so that the active data
;                   widget on the dproc panel can be updated.
; 23-jan-2009, jmm, deletes tplot variables created during processing,
;                   correctly updates active data
; 13-may-2009, bck, moved code from Data Processing window to Load window
; 16-jul-2012, aaf, rewrote error checking on input values to match the 
;                   behavior of other GUI windows
; ??-sept-2013, aaf, modified to use new spectrogram code
; 01-jul-2014, aaf, now conforms to standard SPEDAS load API
; 19-mar-2015, aaf, moments, combined data, eclipse corrections, use plugin replay API 
; 
; 
;$LastChangedBy: nikos $
;$LastChangedDate: 2019-01-31 09:51:05 -0800 (Thu, 31 Jan 2019) $
;$LastChangedRevision: 26523 $
;$URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/trunk/projects/themis/spedas_plugin/thm_ui_part_getspec_options.pro $
;-

; Helper function
; Checks if input from text widget is a valid number, returns NaN if not 
; (imitates check done within spinner) 
function thm_ui_part_getspec_check_num, num

    compile_opt idl2, hidden

  if is_numeric(num,/sci) then begin
    on_ioerror, fail
    ; if we have a valid number, use calc to evaluate the expression
    calc, 'num='+num
    
    return, double(num)
    fail: return, !values.D_NAN
  endif else begin
    return,!values.D_NAN
  endelse

end



; Helper function to abstract sensitization of FAC related widgets
;
pro thm_ui_part_getspec_sens_fac, top_id

    compile_opt idl2, hidden

  
  ;check status of gyro and pa buttons
  id = widget_info(top_id, find_by_uname='pa')
  pa_set = widget_info(id,/button_set)
  
  id = widget_info(top_id, find_by_uname='gyro')
  gyro_set = widget_info(id,/button_set)
  
  ;sensitize widgets accordingly
  id = widget_info(top_id, find_by_uname='fac_base')
  widget_control, id, sensitive = pa_set or gyro_set
  
  id = widget_info(top_id, find_by_uname='gyro_base')
  widget_control, id, sensitive = pa_set or gyro_set
  
  id = widget_info(top_id, find_by_uname='pa_base')
  widget_control, id, sensitive = pa_set or gyro_set
  
end



; Helper function to abstract sensitization of energy range widgets
;
pro thm_ui_part_getspec_sens_erange, top_id

    compile_opt idl2, hidden

  
  ;check selection box
  id = widget_info(top_id, find_by_uname='energy_button')
  limit_energy = widget_info(id, /button_set)

  ;sensitze widgets accordingly
  id = widget_info(top_id, find_by_uname='energy_min')
  widget_control, id, sensitive = limit_energy
  
  id = widget_info(top_id, find_by_uname='energy_max')
  widget_control, id, sensitive = limit_energy

end



; Helper function to abstract sensitization of ESA background widgets
;
pro thm_ui_part_getspec_sens_esabgnd, top_id

    compile_opt idl2, hidden

  
  ;check selection box
  id = widget_info(top_id, find_by_uname='esa_bgnd_remove')
  esa_bgnd_remove = widget_info(id, /button_set)

  ;sensitze widgets accordingly
  id = widget_info(top_id, find_by_uname='esa_bgnd_base')
  widget_control, id, sensitive = esa_bgnd_remove
  
end


; Helper function to edit data type list with regards to 
; combed data and sst calibration options. 
;
pro thm_ui_part_getspec_datatype, state

    compile_opt idl2, hidden


  ;get combined data option
  combined_id = widget_info(state.tab_id, find_by_uname='combined')
  combined = widget_info(combined_id, /button_set)

  ;get sst calibration option
  ;sst calibrations should always be used for combined data
  sst_cal_id = widget_info(state.tab_id, find_by_uname='sst_cal')
  sst_cal = combined ? 1b: widget_info(sst_cal_id, /button_set)
  
  ;get appropriate datatype list
  if combined then begin
    valid_datatypes = state.validCombinedTypes
  endif else if sst_cal then begin
    valid_datatypes = state.validCalTypes
  endif else begin
    valid_datatypes = state.validDataTypes
  endelse

  ;set datatype list
  id = widget_info(state.tab_id, find_by_uname='data_type_list')
  widget_control, id, set_value=valid_datatypes

  ;sensitize/select sst calibration button based on combined data option
  widget_control, sst_cal_id, set_button=sst_cal, sens=~combined

end



;+ 
; Purpose:
; Helper function to check if strings from user input 
; contain valid numbers.
;  -returns 1 if widget contains a valid number, 0 otherwise
;  -if the input is invalid or out of range then a pop-up
;   message will be generated to notify the user
; 
; Arguments:
;   state: state data structure
;   uname: uname of the widget to check
;   namestring: string used to indentify quantity in the 
;               error message, e.g "phi min", "maximum energy"
; 
; Keywords (optional):
;   min: the minimum valid value for the quantity in question
;   max: the maximum valid value for the quantity in question
;   value: set to names keyword to return the numeric value
;          of the widget
;
;-
function thm_ui_part_getspec_check_input, state, uname, namestring, $
                                          min=min, max=max, value=value

    compile_opt idl2, hidden

  tlb = state.tab_id
  
  ;get value
  id = widget_info(tlb, find_by_uname=uname)

  widget_control, id, get_value=value
  
  ;convert to numerical if string is passed in
  if size(/type,value) eq 7 then begin
    value = thm_ui_part_getspec_check_num(value)
  endif
  
  ;if number is valid check against min & max values
  if finite(value,/nan) then begin
    msg = 'Invalid '+strlowcase(namestring)+'.'
  endif else if ~undefined(min) && value lt min then begin
    msg = namestring+' must be greater than or equal to ' + $
                      strtrim(string(min),1) + '.'
  endif else if ~undefined(max) && value gt max then begin
    msg = namestring+' must be less than or equal to ' + $
                      strtrim(string(max),1) + '.'
  endif else begin
    ;if we made it here then we're good to go
    return, 1
  endelse

  ;report an issues with input
  x=dialog_message(msg,/center)
  
  ;return previous (valid) value if requested
  ;this will ensure a valid value is always passed back
  if arg_present(value) then begin
    str_element, state, uname, value
  endif
  
  return, 0

end



; Helper function to reset one or more widgets' value(s)
;
pro thm_ui_part_getspec_reset_widget, state, uname

    compile_opt idl2, hidden


  for i=0, n_elements(uname)-1 do begin

    ;get widget id and type
    id = widget_info(state.tab_id, find_by_uname=uname[i])
    type = widget_info(id, /type)
    
    ;get stored value
    str_element, state, uname[i], value
    
    ;update widget
    case type of 
         1: widget_control, id, set_button=value ;button
        12: begin
              widget_control, id, get_value=list ;combobx
              widget_control, id, set_combobox_select=where(list eq value)
            end
      else: widget_control, id, set_value=value ;spinner/text
    endcase

  endfor

end



; Helper function to copy a widget's value into the 
; appropriate variable
;
pro thm_ui_part_getspec_set_value, state, uname

    compile_opt idl2, hidden


  for i=0, n_elements(uname)-1 do begin
    
    ;get widget id and type
    id = widget_info(state.tab_id, find_by_uname=uname[i])
    type = widget_info(id, /type)
    
    ;get appropriate value
    case type of 
         1: value = widget_info(id, /button_set) ;button
        12: value = widget_info(id, /combobox_gettext) ;combobox
      else: widget_control, id, get_value=value ;spinner/text
    endcase
    
    ;strings from widget_control are often single-element arrays
    if n_elements(value) eq 1 then value=value[0]

    ;set value in state structure
    str_element, state, uname[i], value, /add

  endfor
  
end



; Helper procedure to be called when ready to load spectra.
;  -This procedure will check the applicable widgets for 
;    valid input and stores valid entries.
;  -If the input is invalid the widgets will be reset
;   
pro thm_ui_part_getspec_set_values, state, error=error

    compile_opt idl2, hidden

  error= 1b

  tlb = state.tab_id

  ;Check phi input
  ; first check in inputs are valid, min > max allowed
  a0 = thm_ui_part_getspec_check_input(state,'phi_min','Phi min',min=0,max=360,value=pmin)
  a1 = thm_ui_part_getspec_check_input(state,'phi_max','Phi max',min=0,max=360,value=pmax)
  if ~a0 || ~a1 then begin
    return
  endif


  ;check start angle
  a3 = thm_ui_part_getspec_check_input(state,'start_angle','Start angle',value=sa)
  if ~a3 then begin
    return
  endif


  ;check theta input
  a0 = thm_ui_part_getspec_check_input(state,'theta_min','Theta min',min=-90,max=90,value=tmin)
  a1 = thm_ui_part_getspec_check_input(state,'theta_max','Theta max',min=-90,max=90,value=tmax)
  if ~a0 || ~a1 || tmin ge tmax then begin
    if a0 && a1 && tmin ge tmax then x=dialog_message('Theta minimum must be less than maximum.',/center)
    return
  endif


  ;check pitch angle input
  a0 = thm_ui_part_getspec_check_input(state,'pa_min','Pitch Angle min',min=0,max=180,value=pamin)
  a1 = thm_ui_part_getspec_check_input(state,'pa_max','Pitch Angle max',min=0,max=180,value=pamax)
  if ~a0 || ~a1 || pamin ge pamax then begin
    if a0 && a1 && pamin ge pamax then x=dialog_message('Pitch angle minimum must be less than maximum.',/center)
    return
  endif


  ;check gyrophase input
  a0 = thm_ui_part_getspec_check_input(state,'gyro_min','Gyrophase min',min=0,max=360,value=gvmin)
  a1 = thm_ui_part_getspec_check_input(state,'gyro_max','Gyrophase max',min=0,max=360,value=gvmax)
  if ~a0 || ~a1 || gvmin ge gvmax then begin
    if a0 && a1 && gvmin ge gvmax then x=dialog_message('Gyrophase minimum must be less than maximum.',/center)
    return
  endif


  ;check energy limit input if option is selected
  id = widget_info(state.tab_id, find_by_uname='energy_button')
  state.energy_button = widget_info(id, /button_set) ; Fix the reading of the energy state button  
  if state.energy_button then begin
    e0 = thm_ui_part_getspec_check_input(state,'energy_min','Energy min',min=0,value=emin)
    e1 = thm_ui_part_getspec_check_input(state,'energy_max','Energy max',min=0,value=emax)
    if ~e0 || ~e1 || emin ge emax then begin
      if e0 && e1 && emin ge emax then x=dialog_message('Energy minimun must be less than maximum.',/center)
      return
    endif
  endif


  ;check regrid input
  r0 = thm_ui_part_getspec_check_input(state,'regrid_phi','Regrid dimension (long)',min=4)
  r1 = thm_ui_part_getspec_check_input(state,'regrid_theta','Regrid dimension (lat)',min=2)
  if ~r0 then begin
    return
  endif
  if ~r1 then begin
    return
  endif  


  ;check background removal input
  bnp = thm_ui_part_getspec_check_input(state,'bgnd_npoints','Background points',min=1)
  bs = thm_ui_part_getspec_check_input(state,'bgnd_scale','Background scale',min=0)
  if ~bnp or ~bs then begin
    return
  endif


  ;if no errors then set all options and clear error flag
  error = 0b
  thm_ui_part_getspec_set_value, state, state.widget_list


;  ;set suffix
;  ;anything can be a suffix, as long as there are no spaces
;  id = widget_info(state.tab_id, find_by_uname='suffix')
;  widget_control, id, get_value = suffix
;  state.suffix = strcompress(/remove_all, suffix)
  
  
;  ;set SST contamination removal option
;  id = widget_info(state.tab_id, find_by_uname='sst_method_clean')
;  state.sst_method_clean = widget_info(id, /button_set)
;  
;  ;set SST calibration removal option
;  id = widget_info(state.tab_id, find_by_uname='sst_cal')
;  state.sst_cal = widget_info(id, /button_set)
;
;  ;set ESA background removal option
;  id = widget_info(state.tab_id, find_by_uname='esa_bgnd_remove')
;  state.esa_bgnd_remove = widget_info(id, /button_set)

  
  ;set output types
  types = state.validoutputs
  for i=0, n_elements(types)-1 do begin
    id = widget_info(state.tab_id, find_by_uname=types[i])
    if widget_info(id, /button_set) then outputs = array_concat(types[i],outputs)
  endfor
  ;leave blank string to denote no selection
  ;must use str_element to change the number of elements in the state variable
  str_element, state, 'outputs', keyword_set(outputs) ? outputs:'', /add_replace


end


; Helper function
;-------------------------------------------------------------
;  -Sets all widget to their default states/values
;  -Called on startup and upon user requested options reset
;
pro thm_ui_part_getspec_set_defaults, state

  compile_opt idl2, hidden


  ;-------------------------------------------------------
  ; Set defaults
  ;-------------------------------------------------------

  ;Set values in state structure
  ;  -most widgets will be updated by checking the
  ;   associated value stored in the state structure
  state.suffix = ''
  str_element, state, 'outputs', ['phi','theta','energy'], /add_replace
  state.phi_min = 0.
  state.phi_max = 360.
  state.theta_min = -90.
  state.theta_max =  90.
  state.pa_min =   0.
  state.pa_max = 180.
  state.gyro_min =   0.
  state.gyro_max = 360.
  state.start_angle = 0.
  state.energy_button = 0b
  state.energy_min = 0.
  state.energy_max = 1e7
  state.fac_type = 'MPHIGEO'
  state.regrid_phi =  16
  state.regrid_theta = 8
  state.sst_cal = 1b
  state.sst_method_clean = 0b
  state.eclipse = 0b
  state.sc_pot = 1b
  state.esa_bgnd_remove = 1b
  state.bgnd_type = 'Anode'
  state.bgnd_npoints = 3.0
  state.bgnd_scale = 1.0
  state.combined = 0b

  
  ;clear probe and data type selections
  ;  -only works once the widget it realized
  if widget_info(state.probelist, /realized) then begin
    widget_control, state.probeList , set_list_select=-1
    widget_control, state.dataTypeList, set_list_select=-1
  endif


  ;-------------------------------------------------------
  ; Update Widgets
  ;   -no defaults set in this section, only widget updates
  ;-------------------------------------------------------
  
  ;update applicable widgets from state structures values
  thm_ui_part_getspec_reset_widget, state, state.widget_list
  
  ;update datatype list from other settings
  thm_ui_part_getspec_datatype, state
  
  ;update output selection buttons
  for i=0, n_elements(state.validOutputs)-1 do begin
    id=widget_info(state.tab_id, find_by_uname=state.validoutputs[i])
    widget_control, id, set_button = in_set(state.validoutputs[i],state.outputs)
  endfor
  
  ;sensitize/desensitize FAC options
  thm_ui_part_getspec_sens_fac, state.tab_id

  ;sensitize/desensitize energy range options
  thm_ui_part_getspec_sens_erange, state.tab_id

  ;sensitize/desensitize ESA background options
  thm_ui_part_getspec_sens_esabgnd, state.tab_id
  
  spd_ui_message, 'Using default settings.', sb=state.statusbar, hw=state.historywin

end


; Helper function
;----------------------------------------------------------
;  -Takes the current settings and calls the lower level
;   spectrogram generation routine.
;
pro thm_ui_part_getspec_apply, state, error=error

    compile_opt idl2, hidden

  error = 1
  
  widget_control, /hourglass
  
  sb = state.statusbar
  hw = state.historywin
  

  ; Get requested options from widgets
  ;-------------------------------------

  ;check/get probe
  probe_idx = widget_info(state.probeList,/list_select)
  if probe_idx[0] eq -1 then begin
    spd_ui_message, 'Please select a probe.', sb=sb
    return
  endif
  
  if in_set(probe_idx,0) then begin
    probe = state.validProbes[1:n_elements(state.validProbes)-1]
  endif else begin
    probe = state.validProbes[probe_idx]
  endelse


  ;check/get data type
  data_idx = widget_info(state.dataTypeList,/list_select)
  if data_idx[0] eq -1 then begin
    spd_ui_message, 'Please select a datatype.', sb=sb
    return
  endif
  
  
  ;notify if no outputs have been selected
  for i=0, n_elements(state.validOutputs)-1 do begin
    id = widget_info(state.tab_id, find_by_uname=state.validOutputs[i])
    if widget_info(id, /button_set) then outputs_set = 1b
  endfor
  if ~keyword_set(outputs_set) then begin
    spd_ui_message, 'Please select one or more output types.', sb=sb
    return
  endif

  
  ;check/get time range
  state.tr->getproperty, starttime=start_obj
  state.tr->getproperty, endtime=stop_obj
  start_obj->getProperty,tstring=startText
  stop_obj->getProperty,tstring=stopText
  trange = time_double([startText, stopText])
  
  ;check that start and end times are not out of order/equal
  if trange[0] ge trange[1] then begin
    spd_ui_message, 'Error: End time less than/equal to Start time.', sb=sb
    return
  endif
  
  ;check that start and end times are not in the future
  if (trange[0] gt systime(/sec)) AND (trange[1] gt systime(/sec)) then begin
    spd_ui_message, "Error: Start and end times are later than today's date. ", sb=sb
    return
  endif


  ;Check editable widget values and copy into appropriate variables
  ;in the state structure if valid.
  thm_ui_part_getspec_set_values, state, error=error
  if keyword_set(error) then return


  ;get current tplot names so that extra varaibles can be cleaned later
  tnames_before = tnames('*',create_time=ct)


  ;get status of energy limit selection;
  ; -energy limits will only be applied if the button is set;
  id = widget_info(state.tab_id, find_by_uname='energy_button');
  limit_energy = widget_info(id, /button_set)



  ; Copy options out of state structure
  ;  -numerical values have already been checked
  ;-------------------------------------

  trange = time_double([startText, stopText])
  outputs = state.outputs
  combined = state.combined
  suffix = strcompress(/remove_all, state.suffix)

  phi = [state.phi_min, state.phi_max]
  theta = [state.theta_min, state.theta_max]
  pitch = [state.pa_min, state.pa_max]
  gyro = [state.gyro_min, state.gyro_max]
  energy = state.energy_button ? [state.energy_min, state.energy_max]:0

  start_angle = state.start_angle

  regrid = [state.regrid_phi,state.regrid_theta]
  fac_type = strlowcase(state.fac_type)

  use_eclipse_corrections = state.eclipse ? 2:0
  use_sc_pot = state.sc_pot

  esa_bgnd_remove = state.esa_bgnd_remove
  bgnd_type = state.bgnd_type
  bgnd_npoints = state.bgnd_npoints
  bgnd_scale = state.bgnd_scale

  sst_cal = state.sst_cal
  sst_method_clean = state.sst_method_clean


  ;get appropriate list of valid datatypes
  ;combined data will also use sst calibrations
  if combined then begin
    valid_datatypes = state.validCombinedTypes
  endif else if sst_cal then begin
    valid_datatypes = state.validCalTypes
  endif else begin
    valid_datatypes = state.validDataTypes
  endelse

  ;get datatype selection
  if ~combined && in_set(data_idx,0) then begin
    datatype = valid_datatypes[1:*]
  endif else begin
    datatype = valid_datatypes[data_idx]
  endelse



  ; Check for overwrite
  ;----------------------------------
  clobber = ''  ;must be defined later

  ;initialize flags to control overwrite
  ;array must be created and reformed to maintain dimensionality (IDL bug?)
  flag_dimensions = [n_elements(probe),n_elements(datatype),n_elements(outputs)]
  load_flags = replicate(1b,flag_dimensions)
  load_flags = reform(load_flags,flag_dimensions)

  ;list out valid moment variables
  moment_outputs = [ 'avgtemp', $
                     'density', $
                     'eflux', $
                     'flux', $
                     'mftens', $
                     'ptens', $
                     'sc_current', $
                     'velocity', $
                     'vthermal', $
                     'magt3', $
                     't3', $
                     'symm', $
                     'symm_theta', $
                     'symm_phi', $
                     'symm_ang', $
                     'delta_time' ]

  for k=0, n_elements(probe)-1 do begin
    for j=0, n_elements(datatype)-1 do begin
      for i=0, n_elements(outputs)-1 do begin
        
        ;names of existing variables in gui
        existing_names = state.loadedData->getGroupNames()
        if ~is_string(existing_names) then existing_names = ''
        
        ;check for presense of variable(s)
        ;requesting moments loads multiple products it is checked separately
        if outputs[i] eq 'moments' then begin
      
          ;moments load in an "all or nothing" fassion so the overwrite option
          ;should reflect that for simplicity and maintainability
          name = ssl_set_intersection('th'+probe[k]+'_'+datatype[j]+'_'+moment_outputs+suffix, existing_names)
          ;null set intersection will return -1L
          query = ~is_num(name)
          prompttext = 'One or more moments for datatype "th'+probe[k]+'_'+datatype[j]+ $
                       '" already exist. Do you want to overwrite them? '+ $
                       ' Click "No" continue with the existing variables or "Cancel" to stop.'+ $
                       ssl_newline()+' '+ssl_newline()+'The following will be overwritten: '+ $
                       ssl_newline()+strjoin(name,',  ')
      
        endif else begin
          
          name = 'th'+probe[k]+'_'+datatype[j]+'_eflux'+'_'+outputs[i]+suffix
          query = in_set(name, existing_names) 
          prompttext = 'The variable "' + name + $
              '" already exists. Do you want to overwrite it?'+ $
              ' Click "No" continue with the existing variable or "Cancel" to stop.'
      
        endelse
      
        ;query the user if the variable already exists
        if query then begin
        
          ;only query if neither "yes to all" nor "no to all" have been selected
          if clobber ne 'yestoall' AND clobber ne 'notoall' then begin
      
            clobber = spd_ui_prompt_widget(state.tab_id, sb, hw, promptText=promptText,$
                title='Variable already exists.', defaultvalue='cancel', $
                /no, /yes, /allno, /allyes, /cancel, frame_attr=8)
            
            ;re-initialize hourglass after prompt widget has been called
            widget_control, /hourglass
            
          endif
            
          ;if answer was "yes" or "yes to all" then nothing need be done
          if clobber eq 'no' then load_flags[k,j,i] = 0b
          if clobber eq 'notoall' then load_flags[k,j,i] = 0b
          if clobber eq 'cancel' then begin
            spd_ui_message, 'Load canceled by user.', sb=sb, hw=hw
            return
          endif
          
        endif
        
      endfor
    endfor
  endfor


  ; Generate products and load into gui
  ;----------------------------------
  thm_ui_part_products, loaded_data=state.loadeddata, $
                        history_window=hw, $
                        status_bar=sb, $
                        load_flags=load_flags, $

                        probe=probe, $
                        datatype=datatype, $
                        trange=trange, $
                        outputs=outputs, $
                        suffix=suffix, $

                        energy=energy, $
                        phi=phi, $
                        theta=theta, $
                        gyro=gyro, $
                        pitch=pitch, $
                        start_angle=start_angle, $

                        regrid=regrid, $
                        fac_type=fac_type, $

                        use_eclipse_corrections=use_eclipse_corrections, $
                        use_sc_pot=use_sc_pot, $

                        esa_bgnd_remove = esa_bgnd_remove, $
                        bgnd_type = bgnd_type, $
                        bgnd_npoints = bgnd_npoints, $
                        bgnd_scale = bgnd_scale, $

                        sst_cal=sst_cal, $
                        sst_method_clean=sst_method_clean


  ; Add operation to call sequence
  ;----------------------------------
  state.callsequence->addplugincall, 'thm_ui_part_products', $
                        load_flags=load_flags, $

                        probe=probe, $
                        datatype=datatype, $
                        trange=trange, $
                        outputs=outputs, $
                        suffix=suffix, $

                        energy=energy, $
                        phi=phi, $
                        theta=theta, $
                        gyro=gyro, $
                        pitch=pitch, $
                        start_angle=start_angle, $

                        regrid=regrid, $
                        fac_type=fac_type, $

                        use_eclipse_corrections=use_eclipse_corrections, $
                        use_sc_pot=use_sc_pot, $

                        esa_bgnd_remove = esa_bgnd_remove, $
                        bgnd_type = bgnd_type, $
                        bgnd_npoints = bgnd_npoints, $
                        bgnd_scale = bgnd_scale, $

                        sst_cal=sst_cal, $
                        sst_method_clean=sst_method_clean


  error = 0
  
  return
  
end




;event handler
Pro thm_ui_part_getspec_options_event, event

  widget_control, /hourglass
  
  err_xxx = 0
  Catch, err_xxx
  IF (err_xxx NE 0) THEN BEGIN
    Catch, /Cancel
    Help, /Last_Message, Output = err_msg

    print, err_msg

    dummy = dialog_message('An unknown error occured and the window must be restarted. See console for details.', /center, _extra=_extra)
    
    if is_struct(state) then begin
      for i=0, n_elements(err_msg)-1 do begin
        spd_ui_message, err_msg[i], sb=state.statusBar, hw=state.historyWin
      endfor
    endif
    
    if widget_valid(base) then begin
      Widget_Control, base, Set_UValue=state, /No_Copy
    endif
    
    Widget_Control, event.TOP, Set_UValue=state, /No_Copy
    widget_control, event.top,/destroy
    RETURN
  ENDIF


  ;ignore events from widgets without a uval
  widget_control, event.id, get_uvalue = uval
  if undefined(uval) then return
  
  ;get state structure
  base = event.handler
  widget_control, base, Get_UValue=state, /no_copy
  
  
  ;identify widget by uval and handle event
  ;-----------------------------------------------------------------------
  case uval of
  
    'energy_button': begin
      ;sensitize energy limit spinners
      thm_ui_part_getspec_sens_erange, state.tab_id
    end
    
    'fac_set': begin
      ;sensitize FAC options as needed
      thm_ui_part_getspec_sens_fac, state.tab_id
    end
    
    'esa_bgnd_remove':begin
      ;sensitize/desensitize ESA background options
      thm_ui_part_getspec_sens_esabgnd, state.tab_id
    end

    'CLEARDATA': begin
      widget_control, state.dataTypeList, set_list_select=-1
    end
    
    'CLEARPROBE': begin
      widget_control, state.probeList , set_list_select=-1
    end
    
    'SST_CAL': begin
      ;update datatype list from other settings
      thm_ui_part_getspec_datatype, state
    end

    'COMBINED': begin
      ;update datatype list from other settings
      thm_ui_part_getspec_datatype, state
    end
    
    'HELP':begin
      ;assume help file is in this dir
      info = routine_info('thm_ui_part_getspec_options',/source)
      xdisplayfile, file_dirname(info.path,/mark)+'thm_ui_part_getspec_options.txt', $
                    group=state.tab_id, /modal, done_button='Done', $
                    title='HELP: THEMIS Derived Particle Products'
    end
    
    'RESET':begin
      thm_ui_part_getspec_set_defaults, state
    end
    
    'APPLY':begin  
      widget_control, /hourglass
      thm_ui_part_getspec_apply, state, error=error
    end
  
    else:
  
  endcase
  ;-----------------------------------------------------------------------
  
  widget_control, base, set_uvalue=state, /NO_COPY
  
  return

end

Pro thm_ui_part_getspec_options, tab_id, loadedData, historyWin, statusBar, $
                                 treecopy, trObj, callSequence, $
                                 loadTree=loadTree, $
                                 timeWidget=timeid

compile_opt idl2, hidden                       

widget_control, /hourglass

;------------------------------------------------------------------------------
;Set up initial widget bases for easier formatting
;------------------------------------------------------------------------------
mainBase = widget_base(tab_id, /col, tab_mode=1, event_pro='thm_ui_part_getspec_options_event')
  topBase = widget_base(mainBase, /row, ypad=8)
    topCol1Base =  widget_base(topBase, /col, space=4)
      instrLabelBase = widget_base(topcol1base, /row)
      instrumentBase = widget_base(topCol1Base, /col, /frame, xpad=5)
        timeBase = widget_base(instrumentBase)
        dataButtonsBase = widget_base(instrumentBase, /col, /nonexclusive, xpad=5)
        dataBase = widget_base(instrumentBase, /row)
    topCol2Base = widget_base(topBase, /col, space=2)
      col2row1 = widget_base(topcol2base, ypad=2, /col) ;zero padding to keep tops aligned
        output_label_base = widget_base(col2row1, /row, /align_left)
        output_base = widget_base(col2row1, /row, /frame, ypad=6, xpad=5)
      col2row2 = widget_base(topcol2base, ypad=2, /col)
        angle_range_label_base = widget_base(col2row2, /row, /align_left)
        angle_range_base = widget_base(col2row2, /col, /frame, ypad=8, xpad=5)
      col2row3 = widget_base(topcol2base, ypad=2, /col)
        energy_range_label_base = widget_base(col2row3, /row, /align_left)
        energy_range_base = widget_base(col2row3, /col, /frame, ypad=4, xpad=5)
      col2row4 = widget_base(topcol2base, ypad=2, /col)
        start_angle_label_base = widget_base(col2row4, /row, /align_left)
        start_angle_base = widget_base(col2row4, /col, /frame, ypad=4, xpad=5)
    topCol3Base =  widget_base(topBase, /col, space=6)
      col4row1 = widget_base(topcol3base, ypad=2, /col)
        suffixLabelBase = widget_base(col4row1,/col)
        suffixBase = widget_base(col4row1, /row, /frame, ypad=8, xpad=10)
      col3row2 = widget_base(topcol3base, ypad=2, /col)
        fac_label_base = widget_base(col3row2, /row, /align_left)
        fac_base = widget_base(col3row2, /col, /frame, ypad=4, xpad=5, uname='fac_base')
      col3row3 = widget_base(topcol3base, ypad=2, /col)
        esa_label_base = widget_base(col3row3, /row, /align_left)
        esa_base = widget_base(col3row3, /col, /frame, ypad=4, xpad=5)
      col3row4 = widget_base(topcol3base, ypad=2, /col)
        sst_label_base = widget_base(col3row4, /row, /align_left)
        sst_base = widget_base(col3row4, /col, /frame, ypad=4, xpad=5)



  buttonBase = widget_base(mainBase, /row, /align_center)
  
  ;data input options
  dataLabel = widget_label(instrLabelBase, value='Data Selection: ', /align_left)


;------------------------------------------------------------------------------
; NOTE: Default widget values and states will be handled in the helper funtion
;       thm_ui_part_getspec_set_defaults.
;       Changes to any defaults shoudl be made there.
;------------------------------------------------------------------------------

  loadtree = obj_new() ;calling function needs object passed back

;------------------------------------------------------------------------------
; Time selection
;------------------------------------------------------------------------------
  tr = trObj
  timeid = spd_ui_time_widget(timeBase,$
                              statusBar,$
                              historyWin,$
                              timeRangeObj=tr,$
                              uvalue='TIME_WIDGET',$
                              uname='time_widget')
                              

;------------------------------------------------------------------------------
; Probe selection
;------------------------------------------------------------------------------
  validProbesVisible = [' * (All)', ' A (P5)', ' B (P1)', ' C (P2)', ' D (P3)', ' E (P4)']
  validProbes = ['*', 'a', 'b', 'c', 'd', 'e']
  probeBase = Widget_Base(dataBase, /col)
  plBase = Widget_base(probeBase, /row)
  probeListBase = Widget_Base(plBase, /col)
  probeLabel = Widget_Label(probeListBase, Value='Probe: ', /align_left)
  probeList = Widget_List(probeListBase, Value=validProbesVisible, /multiple, uval='PROBE', XSize=16, YSize=11)
  probeButtonBase = Widget_Base(probeListBase, /row, /align_center)
  probeClearButton = Widget_Button(probeButtonBase, value=' Clear Probe ', uvalue='CLEARPROBE', /align_center)


;------------------------------------------------------------------------------
; Data type selection
;------------------------------------------------------------------------------

  suffix = ''
  validDataTypes = ['*', $
                    'peif', 'peir', 'peib', 'peef', 'peer', 'peeb', $
                    'psif', 'psir', 'psef', 'pser',         'pseb'] 
  validCalTypes = ['*', $
                    'peif', 'peir', 'peib', 'peef', 'peer', 'peeb', $
                    'psif',         'psef',                 'pseb'] 
  validCombinedTypes= [ 'ptiff', 'ptirf', 'ptibb', 'ptifb', 'ptibf', $
                        'pteff', 'pterf', 'ptebb', 'ptefb', 'ptebf' ]


  eclipse_button = widget_button(dataButtonsBase, value='Apply Eclipse Corrections', $
                    uname='eclipse', tooltip='Apply spacecraft spin corrections during eclipses')
  pot_button = widget_button(dataButtonsBase, value='Use Spacecraft Potential', $
                 uname='sc_pot', tooltip='Apply spacecraft potential corrections when producing moments')
  sstButton = widget_button(dataButtonsBase, value='Use SST Calibrations', $
                            uname='sst_cal', uval='SST_CAL', tooltip= $
                            'Use newest SST calibrations (not available for reduced data).')
  
  combinedButton = widget_button(dataButtonsBase, value='Combined Data', $
                            uname='combined', uval='COMBINED', tooltip= $
                            'Generate data from interpolated ESA+SST distributions. '+ $
                            'The last two characters of the data type represent '+ $
                            'ESA and SST data types respectively. '+ $
                            'Processing time will significantly increase.')

  dataTypeBase = Widget_Base(DataBase, /col)
  dataL1Base = Widget_Base(dataTypeBase, /col)
  dataButtonBase = Widget_Base(dataTypeBase, /col, /align_center)
  dataTypeLabel = Widget_Label(dataL1Base, Value='Data Type:', /align_left)
  dataTypeList = Widget_List(dataL1Base, Value=validDataTypes, uval='DATATYPE', $
                             uname='data_type_list', /Multiple, XSize=16, YSize=11)
  dataClearButton = Widget_Button(dataButtonBase, value=' Clear Data Type ', uvalue='CLEARDATA', /align_center)

  ;suffix
  suffixlabel = widget_label(suffixLabelBase, value = 'Variable Name: ', /align_left)
  suffixlabel = widget_label(suffixBase, value = 'Suffix: ', /align_left)
  suffixid = widget_text(suffixBase, value=suffix, xsize=22, ysize=1, uname='suffix', /editable)


;------------------------------------------------------------------------------
;Output selection
;------------------------------------------------------------------------------
  outputs = ['phi', 'theta', 'energy']
  validOutputs = ['energy', 'phi', 'theta', 'pa', 'gyro', 'moments'] 

  spec_label = widget_label(output_label_base, value='Output Selection')
  
  output_col_1 = widget_base(output_base, /nonexclusive)
    energy_button = widget_button(output_col_1, value='Energy', uname='energy', $ 
                     tooltip='Energy spectrogram.  Averages over all look directions.')
    moments_button = widget_button(output_col_1, value='Moments', uname='moments', $
                     tooltip='Various products derived from moments of the particle distribution.')
  
  output_col_2 = widget_base(output_base, /nonexclusive)
    phi_button = widget_button(output_col_2, value='Phi', uname='phi', $ 
                     tooltip='Phi (longitudinal) spectrogram.  Averages over energy and theta (latitude).')
    theta_button = widget_button(output_col_2, value='Theta', uname='theta', $ 
                     tooltip='Theta (latitudinal) spectrogram. Averages over energy and phi (longitude).')
  
  output_col_3 = widget_base(output_base, /nonexclusive)
    gyro_button = widget_button(output_col_3, value='Gyrophase', uname='gyro', uval='fac_set', $ 
                     tooltip='Field aligned longitudinal spectrogram.  Averages over energy and latitude in field alligned coordinates.')
    pa_button = widget_button(output_col_3, value='Pitch Angle', uname='pa', uval='fac_set', $ 
                     tooltip='Field aligned colatitude spectrogram.  Averages over energy and longitude in field alligned coordinates.') 
  

;------------------------------------------------------------------------------
; Angle Range Limits
;------------------------------------------------------------------------------
  phi = [0,360.]
  theta = [-90,90.]
  gyro = [0,360]
  pa = [0,180]

  angle_range_label = widget_label(angle_range_label_base, value='Angular Ranges (min/max):')
  
  phi_range_base = widget_base(angle_range_base, /row)
    phi_range_label = widget_label(phi_range_base, value='Phi:  ')
    phi_min = spd_ui_spinner(phi_range_base, label='', value=phi[0], uname='phi_min', $
                     tooltip = 'Minimum phi used to calculate all outputs.')
    phi_max = spd_ui_spinner(phi_range_base, label='', value=phi[1], uname='phi_max', $
                     tooltip = 'Maximum phi used to calculate all outputs.')

  theta_range_base = widget_base(angle_range_base, /row)
    theta_range_label = widget_label(theta_range_base, value='Theta:  ')
    theta_min = spd_ui_spinner(theta_range_base, label='', value=theta[0], uname='theta_min', $
                     tooltip = 'Minimum theta used to calculate all outputs.')
    theta_max = spd_ui_spinner(theta_range_base, label='', value=theta[1], uname='theta_max', $
                     tooltip = 'Maximum theta used to calculate all outputs.')

  gyro_range_base = widget_base(angle_range_base, /row, uname='gyro_base')
    gyro_range_label = widget_label(gyro_range_base, value='Gyro:  ')
    gyro_min = spd_ui_spinner(gyro_range_base, label='', value=gyro[0], uname='gyro_min', $
                     tooltip = 'Minimum gyrophase used to calculate field aligned spectrograms.')
    gyro_max = spd_ui_spinner(gyro_range_base, label='', value=gyro[1], uname='gyro_max', $
                     tooltip = 'Maximum gyrophase used to calculate field aligned spectrograms.')

  pa_range_base = widget_base(angle_range_base, /row, uname='pa_base')
    pa_range_label = widget_label(pa_range_base, value='PA:  ')
    pa_min = spd_ui_spinner(pa_range_base, label='', value=pa[0], uname='pa_min', $
                     tooltip = 'Minimum pitch angle used to calculate field aligned spectrograms.')
    pa_max = spd_ui_spinner(pa_range_base, label='', value=pa[1], uname='pa_max',$
                     tooltip = 'Maximum gyrophase used to calculate field aligned spectrograms.')

  ;dynamically resize angle range labels
  geo = widget_info(theta_range_label,/geo)
    widget_control, phi_range_label, xsize = geo.scr_xsize
    widget_control, gyro_range_label, xsize = geo.scr_xsize
    widget_control, pa_range_label, xsize = geo.scr_xsize


;------------------------------------------------------------------------------
; Energy Range Limits
;------------------------------------------------------------------------------
  energy = [0,1e7]
  
  energy_range_label = widget_label(energy_range_label_base, value='Energy Range (min/max):')

  energy_range_base2 = widget_base(energy_range_base, /row, xpad=0)
    energy_range_bbase = widget_base(energy_range_base2, /nonexclusive, xpad=0, ypad=0)
      energy_range_button = widget_button(energy_range_bbase, value='Energy (eV):', $
                    uname='energy_button', uval='energy_button', $
                    tooltip='Limit the energy range used to calculate all spectrograms.')
    energy_min = spd_ui_spinner(energy_range_base2, label='', value=energy[0], uname='energy_min', $
                    tooltip = 'Minimum energy used to calculate all outputs.', inc=100)
    energy_max = spd_ui_spinner(energy_range_base2, label='', value=energy[1], uname='energy_max', $
                    tooltip = 'Maximum energy used to calculate all outputs.', inc=100)
    
    
;------------------------------------------------------------------------------
; Start Angle
;------------------------------------------------------------------------------
  phi_start = 0.
  
  start_angle_label = widget_label(start_angle_label_base, value='Phi/Gyro Start Angle: ')
  
  start_angle_base2 = widget_base(start_angle_base, /row, xpad=0)
    start_angle = spd_ui_spinner(start_angle_base2, label='Start plot at (degrees): ', $
                      value=phi_start, incr=5,  uname='start_angle', $
                      tooltip='Start phi and gyrophase y axes at this value.')
  
  
;------------------------------------------------------------------------------
; FAC Options
;------------------------------------------------------------------------------
  regrid = [16,8.]
  facs =  ['MPHIGEO', 'PHIGEO', 'XGSE']

  fac_label = widget_label(fac_label_base, value='Field Aligned Coordinates:')
  
  regrid_base = widget_base(fac_base, /row)
    regrid_label = widget_label(regrid_base, value='Bin Num (long,lat):  ')
    regrid_phi = spd_ui_spinner(regrid_base, value=regrid[0], incr=2, label='', text_box_size=4, $
                 uname='regrid_phi', tooltip='Number of bins across rphi (field aligned longitude).')
    regrid_theta = spd_ui_spinner(regrid_base, value=regrid[1], incr=2, label='', text_box_size=4, $
                 uname='regrid_theta', tooltip='Number of bins across rtheta (field aligned longitude).')
  
  coord_base = widget_base(fac_base, /row)
    coord_label = widget_label(coord_base, value='FAC Variant:  ')
    coord_list = widget_combobox(coord_base, value=facs, uname='fac_type')


;------------------------------------------------------------------------------
; ESA Background
;------------------------------------------------------------------------------

  esa_bgnd_remove = 1b
  bgnd_type =  ['Anode','Angle','Omni']
  bgnd_npoints = 3.0
  bgnd_scale = 1.0

;  esa_label = widget_label(esa_label_base, value='ESA Background')

  esa_bgnd_button_base = widget_base(esa_label_base, /nonexclusive,/col, ypad=0)
    esa_bgnd_button = widget_button(esa_bgnd_button_base, $
                      value='Subtract ESA Background', $
                      uvalue='esa_bgnd_remove', uname='esa_bgnd_remove', $
                      tooltip='Calculate ESA background and subtract from '+ $
                      'each time sample. Click "Help" for more information.')

  esa_bgnd_base = widget_base(esa_base, uname='esa_bgnd_base', /col, ypad=0)
    bgnd_type_base = widget_base(esa_bgnd_base, /row)
      bgnd_type_label = widget_label(bgnd_type_base, value='Type:  ')
      bgnd_type_list = widget_combobox(bgnd_type_base, value=bgnd_type, uname='bgnd_type')

    bgnd_npoints_base = widget_base(esa_bgnd_base, /row)
      bgnd_npoints_spinner = spd_ui_spinner(bgnd_npoints_base, label='Number of Points:  ', $
                               uname='bgnd_npoints', incr=1, value=bgnd_npoints, $
                               getXLabelSize=scr_xsize, min_value=1, text_box_size=6, $
                               tooltip='The number of lowest values points to '+ $
                               'average over when determining background.')
      widget_control, bgnd_type_label, xsize = scr_xsize+1

    bgnd_scale_base = widget_base(esa_bgnd_base, /row)
      bgnd_scale_spinner = spd_ui_spinner(bgnd_scale_base, label='Scaling:  ', $ 
                                uname='bgnd_scale', incr=.05, value=bgnd_scale, $
                                xlabelsize=scr_xsize, min_value=0, text_box_size=6, $
                                tooltip='A scaling factor that the background '+ $
                                'will be multiplied by before it is subtracted.')
  

;------------------------------------------------------------------------------
; SST Contamination
;------------------------------------------------------------------------------

  sst_method_clean = 0b

  sst_label = widget_label(sst_label_base, value='SST Contamination:')
  
  sst_cont_button_base = widget_base(sst_base, /nonexclusive,/col)
    sst_cont_button = widget_button(sst_cont_button_base, $
                      value='Mask Default Bins', uname='sst_method_clean', $ 
                      tooltip='Mast the default set of contaminated SST bins.')  


;------------------------------------------------------------------------------
; Main Buttons
;------------------------------------------------------------------------------

  apply_button = Widget_Button(buttonBase, Value = '    Apply    ', XSize = 70, $
                               UValue = 'APPLY', tooltip='Create particle spectra from active data')
  resetButton = Widget_Button(buttonBase, value='Reset All', uvalue='RESET', $
                              tooltip='Reset all settings to their default values.')
  helpButton = Widget_Button(buttonBase, Value='Help', XSize=70, UValue='HELP', $
                              tooltip='Open Help Window.')


;------------------------------------------------------------------------------
; Widget formatting
;------------------------------------------------------------------------------

  ;dynamically resize the second column's framed bases
  geo = widget_info(energy_range_base,/geo)
    widget_control, output_base, xsize=geo.scr_xsize
    widget_control, angle_range_base, xsize=geo.scr_xsize
    widget_control, start_angle_base, xsize=geo.scr_xsize

  ;dynamically resize the third column's framed bases
  geo = widget_info(fac_base,/geo)
    widget_control, sst_base, xsize=geo.scr_xsize
    widget_control, suffixbase, xsize=geo.scr_xsize


;------------------------------------------------------------------------------
; Set state, initialize widgets, and realize
;------------------------------------------------------------------------------


state = {tab_id:tab_id, $
        
        ; GUI objects & variables
         loadedData:loadedData, historyWin:historyWin, callSequence:callSequence, $ 
         statusBar:statusBar, probeList:probeList, tr:tr, $
        
        ; Widgets
         dataTypeList:dataTypeList, $
         probeClearButton:probeClearButton, $
         dataClearButton:dataClearButton, $
        
        ; Widget support variables (lists etc)
         validProbes:validProbes, $
         validDataTypes:validDataTypes, $
         validCalTypes:validCalTypes, $
         validCombinedTypes:validCombinedTypes, $
         validOutputs:validOutputs, $
        
        ; Stored Options (initialized in thm_ui_part_getspec_set_defaults)
        ; The nomenclature here should match the uvalue of the corresponding
        ; widget so that it's value can be set with thm_ui_part_getspec_set_value 
         phi_min:0d,    phi_max:0d, $
         theta_min:0d,  theta_max:0d, $
         pa_min:0d,     pa_max:0d, $
         gyro_min:0d,   gyro_max:0d, $
         energy_button:0b, $
         energy_min:0d, energy_max:0d, $
         outputs:[''], $
         start_angle:0d, $
         regrid_phi:0d, regrid_theta:0d, $
         fac_type:'',  $
         eclipse:0b, $
         sc_pot:1b, $
         esa_bgnd_remove:0b, $
         bgnd_type:'', $
         bgnd_npoints:0d, $
         bgnd_scale:0d, $
         sst_cal:0b, $
         sst_method_clean:0b, $
         combined:0b, $
         suffix:'', $

         widget_list: ['phi_min','phi_max', $
                       'theta_min','theta_max', $
                       'pa_min','pa_max', $
                       'gyro_min','gyro_max', $
                       'energy_min','energy_max', $
                       'start_angle', $
                       'suffix', $
                       'fac_type', $
                       'regrid_phi', 'regrid_theta', $
                       'eclipse', $
                       'esa_bgnd_remove', $
                       'sc_pot', $
                       'bgnd_type', $
                       'bgnd_npoints', $
                       'bgnd_scale', $
                       'sst_cal', $
                       'sst_method_clean', $
                       'combined'] $
         }


;Set widget values and states
thm_ui_part_getspec_set_defaults, state


widget_control, widget_info(tab_id, /child), set_uvalue=state, /no_copy

Return
End
