
;+
;Purpose:
;  Helps simplify transformation logic code using a recursive formulation.
;  Rather than specifying the set of transformations for each combination of
;  in_coord & out_coord, this routine will perform only the nearest transformation
;  then make a recursive call to itself, with each call performing one additional
;  step in the chain.  This makes it so only neighboring coordinate transforms need be
;  specified.
;
;The set of transformations forms the following graph:
;  GSE<->GEI<->GEO<->MAG
;  GSE<->GSM<->SM
;
;-
pro spd_cotrans_transform_helper,in_name,out_name,in_coord,out_coord, $
                      ignore_dlimits=ignore_dlimits
                      
  compile_opt hidden

  ;case select below modified to increase simplicity and maintainability.
  ;#1 Identity transform separated.
  ;#2 Recursive calls to spd_cotrans prevents duplicated code. 
  if in_coord eq out_coord then begin
    if in_name ne out_name then copy_data,in_name,out_name 
  endif else begin
    case in_coord of

      'gse': switch out_coord of
        'sm':
        'gsm': begin
          cotrans, in_name, out_name, /gse2gsm,ignore_dlimits=ignore_dlimits
          recursive_in_coord='gsm'
          break
        end
        'agsm': begin
          ; using a rotation angle of 4 degrees when transforming to aGSM coordinates in the GUI
          gse2agsm, in_name, out_name, rotation_angle = 4.0
          recursive_in_coord='agsm'
          break
        end
        else: begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans, in_name,out_name,/gse2gei, ignore_dlimits=ignore_dlimits
          recursive_in_coord='gei'
        end
      endswitch
      'agsm': begin
        agsm2gse, in_name, out_name, rotation_angle = 4.0
        recursive_in_coord='gse'
        break
      end
      'sm': begin
         cotrans, in_name,out_name,/sm2gsm, ignore_dlimits=ignore_dlimits
         recursive_in_coord='gsm'
      end
      'gsm': switch out_coord of
        'sm': begin
          cotrans, in_name,out_name,/gsm2sm, ignore_dlimits=ignore_dlimits
          recursive_in_coord='sm'
          break
        end
        else: begin
          cotrans, in_name,out_name,/gsm2gse, ignore_dlimits=ignore_dlimits
          recursive_in_coord='gse'
        end
      endswitch
      'gei': switch out_coord of
        'geo': begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans,in_name,out_name,/gei2geo,ignore_dlimits=ignore_dlimits
          recursive_in_coord='geo'
          break
        end
        'mag': begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans,in_name,out_name,/gei2geo,ignore_dlimits=ignore_dlimits
          recursive_in_coord='geo'
          break
        end
        'j2000': begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans,in_name,out_name,/gei2j2000,ignore_dlimits=ignore_dlimits
          recursive_in_coord='j2000'
          break
        end
        else: begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans,in_name,out_name,/gei2gse,ignore_dlimits=ignore_dlimits
          recursive_in_coord='gse'
        end
      endswitch
      'geo': switch out_coord of
        'mag': begin
          ;geo2mag,in_name,out_name
          cotrans,in_name,out_name,/geo2mag,ignore_dlimits=ignore_dlimits
          recursive_in_coord='mag'            
          break
        end
        else: begin
          ; if the data is of type 'vel' this is an invalid coordinate transform, warn user
          spd_cotrans_validate_transform, in_name, in_coord, out_coord
          cotrans,in_name,out_name,/geo2gei,ignore_dlimits=ignore_dlimits
          recursive_in_coord='gei'
          break
        end
      endswitch 
      'mag': begin
          cotrans,in_name,out_name,/mag2geo,ignore_dlimits=ignore_dlimits
          ;mag2geo,in_name,out_name
          recursive_in_coord='geo'
      end
      'j2000': begin
          cotrans,in_name,out_name,/j20002gei,ignore_dlimits=ignore_dlimits
          recursive_in_coord='gei'
      end
      else: begin
        dprint,"spd_cotrans: does not know how to transform "+in_coord+" to " + out_coord
        recursive_in_coord=out_coord
      end
    endcase
    spd_cotrans_transform_helper,out_name,out_name,recursive_in_coord,out_coord, $
                      ignore_dlimits=ignore_dlimits 
  endelse          
                      
end




;+
;Procedure:
;  spd_cotrans
;
;Purpose:
;  Transform between various THEMIS and geophysical coordinate systems
;
;Calling Sequence:
;  spd_cotrans, input_name [,output_name] 
;
;Arguments:
; input_name: String or string array of input tplot variable(s).  Standard tplot
;             wildcards may be used to specify multiple variables.
; output_name (optional) String or string array of output tplot variable names.
;             Number of output names must match number of input names once 
;             wildcards are considered.
;
;Keywords:
;  in_coord:  String specifying the coordinate system of the input(s).
;             This keyword is optional if the dlimits.data_att.coord_sys attribute
;             is present for the tplot variable, and if present, it must match
;             the value of that attribute (see cotrans_set_coord, cotrans_get_coord).
;               e.g. 'gse', 'gsm', 'sm', 'gei','geo', 'mag'
;  out_coord:  String specifying the desitnation coordinate system.
;                e.g. 'gse', 'gsm', 'sm', 'gei','geo', 'mag' 
;  in_suffix:  Suffix of input variable name.  This specifies the portion of
;              the input variable's name that will be replace with the output
;              suffix.  If specified, the name effective input name will be
;              input_name + in_suffix
;  out_suffix:  Suffix appended to the output name.  If in_suffix is present or
;               the input coordinates are part of the input variable's name then
;               they will be replaced with out_suffix.
;           
;  out_vars: return a list of the names of any transformed variables
;
;  valid_names:  return valid coordinate system names in named varibles supplied to
;                in_coord and/or out_coord keywords.
;  ignore_dlimits: set this keyword to true so that an error will not
;                  be produced if the internal label of the coordinate system clashed
;                  with the user provided coordinate system.
;  no_update_labels: Set this keyword if you want the routine to not update the labels automatically
;
;Notes:
;  This procedure was forked from thm_cotrans.
;
;
;$LastChangedBy: aaflores $
;$LastChangedDate: 2016-02-24 18:53:52 -0800 (Wed, 24 Feb 2016) $
;$LastChangedRevision: 20171 $
;$URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/tags/spedas_3_2/spedas_gui/utilities/cotrans/spd_cotrans.pro $
;
;-
pro spd_cotrans, input_name, output_name, $

                 in_coord=in_coord, $
                 out_coord=out_coord, $

                 in_suffix=in_suf, $
                 out_suffix=out_suf, $

                 out_vars=out_vars, $

                 valid_names=valid_names, $
                 ignore_dlimits=ignore_dlimits,$
                 verbose=verbose, $
                 no_update_labels=no_update_labels


compile_opt idl2, hidden


;get verbosity from !spedas is not defined
defsysv, 'spedas', exists=spedas_defined
if spedas_defined eq 0 then spedas_init
vb = undefined(verbose) ? !spedas.verbose : verbose


;get valid coordinates
coordSysObj = obj_new('spd_ui_coordinate_systems')
vcoord = coordSysObj->makeCoordSysList()
obj_destroy, coordSysObj

;print usage if requested (vestigial)
if keyword_set(valid_names) then begin
  in_coord = vcoord
  out_coord = vcoord
  if keyword_set(vb) then begin
    dprint, dlevel=0, string(strjoin(vcoord, ','), format = '( "Valid coords:",X,A,".")')
  endif
  return
endif


;Validate in_coord and out_coord
;--------------------------------
if not keyword_set(out_coord) and keyword_set(out_suf) then begin
  out_coord=strmid(out_suf,2,3,/reverse)
  if stregex(out_coord,'sm',/boolean) && ~stregex(out_coord,'gsm',/boolean) then begin
    out_coord = 'sm'
  endif
endif
 
if not keyword_set(out_coord) then begin
  dprint, dlevel=1, 'Must specify out_coord or out_suffix'
  return
endif else begin
  out_coord = ssl_check_valid_name(strlowcase(out_coord), vcoord)
endelse

if not keyword_set(out_coord) then return

if n_elements(out_coord) gt 1 then begin
  dprint, dlevel=1, 'Can only specify one out_coord'
  return
endif

if ~keyword_set(in_coord) && keyword_set(in_suf) then begin 
  in_coord=strmid(in_suf,2,3,/reverse)
  if stregex(in_coord,'sm',/boolean) && ~stregex(in_coord,'gsm',/boolean) then begin
    in_coord = 'sm'
  endif
endif

if keyword_set(in_coord) then begin
  in_coord = ssl_check_valid_name(strlowcase(in_coord), vcoord)
  if not keyword_set(in_coord) then return
  if n_elements(in_coord) gt 1 then begin
    dprint, dlevel=1, 'Can only specify one in_coord'
    return
  endif
endif


;Validate names
;--------------------------------
if not keyword_set(in_suf) then in_suf = ''
if not keyword_set(out_suf) then out_suf = ''

; allow for globbing on the input parameters
in_names = tnames(input_name+in_suf, n)
if n eq 0 then begin
  dprint, dlevel=1, 'No match: '+input_name+in_suf
  return
endif

;generate output names if not provided
if n_params() eq 1 || n_elements(in_names) ne n_elements(output_name) then begin
  if n_params() eq 2 then dprint, dlevel=1, 'WARNING: Ignoring out_names'
  base_len = in_suf ne '' ? strpos(in_names,in_suf,/reverse_search):strlen(in_names) 
  out_names = in_names
  for j = 0, n-1 do begin
    out_names[j] = strmid(in_names[j],0,base_len[j])+out_suf
  endfor
endif else begin
  out_names = output_name + out_suf
endelse

;throw error because it's probably the program's fault?
if n_elements(in_names) ne n_elements(out_names) then begin
  message, 'SPD_COTRANS: number of input variables does not match number of output variables'
endif


;Resolve discrepancies between in_coord keyword, and data_att.coord_sys
;--------------------------------
if keyword_set(ignore_dlimits) then begin

  if is_string(in_coord) then begin
    in_coords = replicate(in_coord,n_elements(in_names))
  endif else begin
    dprint, 'Must specify input coordinates if /ignore_dlimits is set'
    return
  endelse

endif else begin
  in_coords = strarr(n_elements(in_names))
  for i = 0,n_elements(in_names)-1 do begin
  
    data_in_coord = cotrans_get_coord(in_names[i])
    
    if ~keyword_set(in_coord) || strmatch(in_coord, 'unknown') then begin
      in_coords[i] = data_in_coord
    endif else if strmatch(data_in_coord,'unknown') then begin
      in_coords[i] = in_coord
    endif else if data_in_coord ne in_coord then begin
      in_coords[i] = 'conflict'
    endif else begin
      in_coords[i] = in_coord
    endelse
  
  endfor
endelse


;Loop over input variables
;--------------------------------
for i = 0, n_elements(in_names)-1 do begin
  
  in_nam = in_names[i]
  out_nam = out_names[i]

  ;check that data is present
  get_data, in_nam, data=in, dl=in_dl
  if size(in, /type) ne 8 then begin
    dprint, dlevel=1, 'Input tplot variable '+in_nam+' has no data'
    continue
  endif

  ;check that data is a 3-vector
  sizein=size(in.y)
  if sizein[0] ne 2 or sizein[2] ne 3 then begin
    dprint, dlevel=1, 'Input tplot variable '+in_nam+' is not a 3-vector. Skipping'
    continue
  endif

  in_c = in_coords[i]

  ;verify input coordinates match variable's metadata
  if in_c eq 'conflict' then begin
    dprint, dlevel=1, 'Argument input coordinate system and data coordinate system of "' + in_nam + '" do not match. Skipping.'
    continue
  endif else if in_c eq 'unknown' then begin
    dprint, dlevel=1, 'Tplot variable "' + in_nam + '" has unknown input coordinate system. Skipping'
    continue
  endif

  dprint, dlevel=2, 'Transforming '+in_nam+' ('+in_c+') to '+out_coord
  
  ;perform the transformation
  ;--------------------------------
  spd_cotrans_transform_helper,in_nam,out_nam,in_c,out_coord, $
                      ignore_dlimits=ignore_dlimits   
  
  ;aggregate transformed variables
  name_list = array_concat(out_nam,name_list)

  ;update labels in limits and dlimits structures  
  if ~keyword_set(no_update_labels) then begin
    spd_cotrans_update_dlimits,out_nam,in_c,out_coord
    spd_cotrans_update_limits,out_nam,in_c,out_coord
  endif

endfor

if arg_present(out_vars) && n_elements(name_list) gt 0 then begin
  out_vars = name_list
endif

end
