;+
; Project     : SOHO - CDS     
;                   
; Name        : COPY_STRUCT
;               
; Purpose     : Copies one structure to another (with options).
;               
; Explanation : Copy all Fields with matching Tags names (except for 
;               "except_Tags" from one structure array to another structure 
;               array (may be different).  Can also recursively copy from/to 
;               structures nested within structures.
;               
; Use         : IDL> copy_struct, struct_From, struct_To
;                    copy_struct, struct_From, struct_To, EXCEPT=["imag","tag"]
;                    copy_struct, struct_From, struct_To, /RECUR_FROM
;    
; Inputs      : struct_From -  structure array to copy from.
;               struct_To   -  structure array to copy values to.
;               
; Opt. Inputs : None
;               
; Outputs     : None
;               
; Opt. Outputs: NF_copied = incremented by total # of Fields copied.
;               
; Keywords    : EXCEPT_TAGS = string array of Tag names to ignore (NOT to copy).
;
;               SELECT_TAGS = Tag names to copy (takes priority over EXCEPT).
;
;               /RECUR_FROM = option to search for Tag names
;                             in sub-structures of struct_From,
;                             and then call copy_struct recursively
;                             for those nested structures.
;               /RECUR_TO = search for sub-structures of struct_To,
;                           and then call copy_struct recursively
;                           for those nested structures.
;               /RECUR_TANDEM = call copy_struct recursively
;                               for the sub-structures with matching
;                               Tag names in struct_From and struct_To
;                (for use when Tag names match but structure types differ).
;
; Calls       : For option SELECT_TAGS calls pro match.
;
; Common      : None
;               
; Restrictions: None
;               
; Side effects: None
;               
; Category    : Util, structures
;               
; Prev. Hist. : Frank Varosi STX @ NASA/GSFC
;
; Written     : CDS version, CDP, RAL, 23-Aug-94
;               
; Modified    : 
;
; Version     : Version 1, 23-Aug-94
;-            

pro copy_struct, struct_From, struct_To, NF_copied, Recur_Level,$
                                    EXCEPT_TAGS  = except_Tags, $
                                    SELECT_TAGS  = select_Tags, $
                                    RECUR_From   = recur_From,  $
                                    RECUR_TO     = recur_To,    $
                                    RECUR_TANDEM = recur_tandem

      if N_elements( Recur_Level ) NE 1 then Recur_Level = 0

      Ntag_from = N_tags( struct_From )
      Ntag_to = N_tags( struct_To )

      if (Recur_Level EQ 0) then begin      ;check only at first user call.

            NF_copied = 0

            if (Ntag_from LE 0) then begin
                  message," 1st argument must be a structure",/CON,/INF
                  return
               endif

            if (Ntag_to LE 0) then begin
                  message," 2nd argument must be a structure",/CON,/INF
                  return
               endif

            N_from = N_elements( struct_From )
            N_to = N_elements( struct_To )

            if (N_from GT N_to) then begin

                  message," # elements (" + strtrim( N_to, 2 ) + $
                              ") in output TO structure",/CON,/INF
                  message," increased to (" + strtrim( N_from, 2 ) + $
                              ") as in FROM structure",/CON,/INF
                  struct_To = [ struct_To, $
                              replicate( struct_To(0), N_from-N_to ) ]

              endif      else if (N_from LT N_to) then begin

                  message," # elements (" + strtrim( N_to, 2 ) + $
                              ") in output TO structure",/CON,/INF
                  message," decreased to (" + strtrim( N_from, 2 ) + $
                              ") as in FROM structure",/CON,/INF
                  struct_To = struct_To(0:N_from-1)
               endif
         endif

      Recur_Level = Recur_Level + 1            ;go for it...

      Tags_from = Tag_names( struct_From )
      Tags_to = Tag_names( struct_To )
      wto = indgen( Ntag_to )

;Determine which Tags are selected or excluded from copying:

      Nseltag = N_elements( select_Tags )
      Nextag = N_elements( except_Tags )

      if (Nseltag GT 0) then begin

            match, Tags_to, [strupcase( select_Tags )], mt, ms
            w = where( mt, Ntag_to )

            if (Ntag_to LE 0) then begin
                  message," selected tags not found",/INFO,/CONTIN
                  return
               endif

            Tags_to = Tags_to(mt)
            wto = wto(mt)

        endif else if (Nextag GT 0) then begin

            except_Tags = [strupcase( except_Tags )]

            for t=0,Nextag-1 do begin

                  w = where( Tags_to NE except_Tags(t), Ntag_to )
                  Tags_to = Tags_to(w)
                  wto = wto(w)
              endfor
         endif

;Now find the matching Tags and copy them...

      for t = 0, Ntag_to-1 do begin

            wf = where( Tags_from EQ Tags_to(t) , nf )

            if (nf GT 0) then begin

                  from = wf(0)
                  to = wto(t)

                  if keyword_set( recur_tandem ) AND            $
                     ( N_tags( struct_To.(to) ) GT 0 ) AND      $
                     ( N_tags( struct_From.(from) ) GT 0 ) then begin

                        struct_tmp = struct_To.(to)

                        copy_struct, struct_From.(from), struct_tmp,  $
                                    NF_copied, Recur_Level,       $
                                    EXCEPT=except_Tags,           $
                                    SELECT=select_Tags,           $
                                    /RECUR_TANDEM,                $
                                    RECUR_FROM = recur_From,      $
                                    RECUR_TO   = recur_To

                        struct_To.(to) = struct_tmp

                    endif else begin

                        struct_To.(to) = struct_From.(from)
                        NF_copied = NF_copied + 1
                     endelse
              endif
        endfor

;Handle request for recursion on FROM structure:

      if keyword_set( recur_From ) then begin

            wfrom = indgen( Ntag_from )

            if (Nextag GT 0) then begin

                  for t=0,Nextag-1 do begin

                      w = where( Tags_from NE except_Tags(t), Ntag_from )
                      Tags_from = Tags_from(w)
                      wfrom = wfrom(w)
                    endfor
               endif

            for t = 0, Ntag_from-1 do begin

                 from = wfrom(t)

                 if N_tags( struct_From.(from) ) GT 0 then begin

                  copy_struct, struct_From.(from), struct_To,        $
                                    NF_copied, Recur_Level,    $
                                    EXCEPT=except_Tags,        $
                                    SELECT=select_Tags,        $
                                    /RECUR_FROM,               $
                                    RECUR_TO     = recur_To,   $
                                    RECUR_TANDEM = recur_tandem
                  endif
              endfor
        endif

;Handle request for recursion on TO structure:

      if keyword_set( recur_To ) then begin

            for t = 0, Ntag_to-1 do begin

               to = wto(t)

               if N_tags( struct_To.(to) ) GT 0 then begin

                  struct_tmp = struct_To.(to)

                  copy_struct, struct_From, struct_tmp,              $
                                    NF_copied, Recur_Level,    $
                                    EXCEPT=except_Tags,        $
                                    SELECT=select_Tags,        $
                                    /RECUR_TO,                 $
                                    RECUR_FROM = recur_From,   $
                                    RECUR_TANDEM = recur_tandem
                  struct_To.(to) = struct_tmp
                 endif
              endfor
        endif

      Recur_Level = Recur_Level - 1
return
end
