;+
; PROJECT:
;       General Purpose
;       SOHO - CDS/SUMER
;       THEMIS
;
; NAME:
;       DPRINT
;
; PURPOSE:
;       Diagnostic PRINT (activated only when DEBUG reaches DLEVEL)
;
; EXPLANATION:
;       This routine acts similarly to the PRINT command, except that
;       it is activated only when the common block variable DEBUG is
;       set to be equal to or greater than the debugging level set by
;       DLEVEL (default to 0).  It is useful for debugging.
;       If DLEVEL is not provided it uses a persistent (common block) value set with the
;       keyword SETDEBUG.
;
; CALLING SEQUENCE (typically written into code):
;       DPRINT, v1 [,v2 [,v3...]]] [,format=format] [,dlevel=dlevel] [,verbose=verbose]
;             The values of v1,v2,v3 will only be printed if verbose >= dlevel
;
; CALLING SEQUENCE to change options (typically typed from IDL command line - Don't put these lines in code!!!)
;       DPRINT, setdebug=2   ; define persistent debug level (2 is typical level)
;       DPRINT, SETVERBOSE=2 ; Same as above
;       DPRINT, print_trace=[0,1,2,3, or 4]  ; Display program trace info in subsequent calls to DPRINT
;       DPRINT, /print_dlevel      ; Display current dlevel and verbose settings.
;       DPRINT, /print_dtime       ; Display delta time between DPRINT statements.
;
; INPUTS:
;       V1, V2, ... - List of variables to be printed out (20 max).
;
; OPTIONAL INPUTS:
;       None.
;
; OUTPUTS:
;       All input variables are printed out on the screen (or the
;       given unit)
;
; OPTIONAL Keywords:
;       FORMAT - Output format to be used
;       UNIT   - Output unit through which the variables are printed. If
;                missing, the standard output (i.e., your terminal) is used.
;
; KEYWORD PARAMETERS:
;       DLEVEL = DLEVEL - An integer indicating the debugging level; defaults to 0
;       VERBOSE = VERBOSE - An integer indicating current verbosity level, If verbose is set
;       it will override the current value of SETVERBOSE, for the specific call of dprint in which
;       it is set.
;       SETVERBOSE=value            - Set debug level to value
;       SETDEBUG=value            - Same as SETVERBOSE
;       GETDEBUG=named variable   - Get current debug level
;       DWAIT = NSECONDS  ; provides an additional constraint on printing.
;              It will only print if more than NSECONDS has elapsed since last dprint.
;       CHECK_EVENTS= [0,1]    -    If set then WIDGET events are captured and processed within DPRINT
;       BREAK_DETECTED= named variable   - Used to break out of user routines (see DPRINTTOOL)
;
;
; COMMON BLOCKS:
;       DPRINT_COM.
;
; RESTRICTIONS:
;     - Changed see SETDEBUG above
;       Can print out a maximum of 20 variables (depending on how many
;          is listed in the code)
;
; SIDE EFFECTS:
;       Generally None.
;
; CATEGORY:
;       Utility, miscellaneous
;
; PREVIOUS HISTORY:
;       Written March 18, 1995, Liyun Wang, GSFC/ARC
;
; MODIFICATION HISTORY:
;       Version 1, Liyun Wang, GSFC/ARC, March 18, 1995
;       Version 2, Zarro, SM&A, 30 November 1998 - added error checking
;       Version 3, Zarro, (EIT/GSFC), 23 Aug 2000 - removed DATATYPE calls
;       Version 4, Larson  (2007) stripped out calls to "execute" so that it can be called from IDL VM
;                          Fixed bug that allows format keyword to be used.
;                          Added SETDEBUG keyword and GETDEBUG keyword
;                          Added DWAIT keyword
;                          Added PRINT_TRACE,PRINT_DTIME,PRINT_DLEVEL
;                          Added Widget options
; $LastChangedBy: ali $
; $LastChangedDate: 2020-03-05 13:24:53 -0800 (Thu, 05 Mar 2020) $
; $LastChangedRevision: 28381 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/trunk/general/misc/SSW/dprint.pro $
;
;-



;function dprint_header,sublevel=sublevel  ,delta_time=delta_time
;  common dprint_com, dprint_struct
;
;  delta_time=2.1345
;  dlevel = 3   &dbg=2
;  prefix = ''
;  if dprint_struct.print_dlevel then  prefix=[prefix, string(dlevel,dbg,format='(i0.0,"/",i0.0)') ]
;  if dprint_struct.print_time   then  prefix=[prefix, time_string(tformat=dprint_struct.tformat,newtime,/local)]
;  if dprint_struct.print_dtime  then  prefix=[prefix, string(format='(f6.3)',delta_time) ]
;  if dprint_struct.print_trace  then  begin
;    stack = scope_traceback(/structure,system=1)
;    level = n_elements(stack) ; -1
;;    if level gt 200 then begin
;;       Message,"Stack is too large! Runaway recursion?"
;;    endif
;    if keyword_set(sublevel) then level -= sublevel
;    level = level > 1
;    stack = stack[0:level-1]
;    stacknames=stack.routine + string(stack.line,format='("(",i0,")")')
;    prefix = [prefix,stacknames]
;  endif
;  return,prefix
;end


PRO DPRINT,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,  $
           v11,v12,v13,v14,v15,v16,v17,v18,v19,v20, $
           format=format, $            ;  Like the format string in print
           dlevel=dlevel,  $           ;  Overides the debug level
           verbose=verbose,$
           setdebug=setdebug, $
           setverbose=setverbose, $
           getdebug=getdebug,$
           filename=filename, $
           print_dlevel=print_dlevel,  $
           check_events=check_events,  $
           no_check_events=no_check_events,  $
           get_check_events=get_check_events,  $
           print_time = print_time   , $
           print_dtime =print_dtime,   $
           print_trace= print_trace,  $
;           display_routine= display_routine,  $           ; user specified routine to display messages
;           set_display_routine = set_display_routine, $
           display_object = display_object,  $
           set_display_object = set_display_object,  $
           status=status, $
           break_requested = break_requested,  $
           dwait=dwait,  $
           reset=reset,  $
           sublevel=sublevel, $
           get_dprint_struct = get_dprint_struct, $
           help = help, $
           phelp=phelp,  $
           unit=unit
   compile_opt idl2
   on_error,2

   common dprint_com, dprint_struct
   newtime = systime(1)

   if not keyword_set(dprint_struct) or keyword_set(reset) then dprint_struct={  $
       debug:getenv('IDL_DEBUG') ? FIX(getenv('IDL_DEBUG')) : 2, $
       lasttime:newtime, $
       lastflushtime:0d, $
       print_dlevel:0, $
       print_time:0,  $
;       display_routine:'', $
       display_object: obj_new(),  $
       tformat:'',   $
       print_dtime:0,  $
       print_trace:0,  $
       file_unit:-1,    $
       file_name:'',   $
       max_lines: uint(-1),   $       ; maximum # of lines to be displayed
       check_events:0, $
       widget_id:0l,   $
       widget_lasttime:0d, $
       widget_dwait:0.1d,  $
       break_flag:0,   $
       ireturn:0  }

  ; if dprint_struct.ireturn then return        ; do nothing (used to avoid unlimited recursion)

;   if not keyword_set(dprint_struct.lasttime) then dprint_struct.lasttime = newtime
;   if not keyword_set(dprint_struct.lastflushtime) then dprint_struct.lastflushtime = newtime
   getdebug = dprint_struct.debug
;   if n_elements(file_unit_c) eq 0 then file_unit_c = -1  ; standard output
   np = N_PARAMS()
;   if np eq 0 then begin
      if n_elements(print_dlevel) ne 0 then dprint_struct.print_dlevel=print_dlevel
      if n_elements(print_dtime)  ne 0 then begin
          dprint_struct.print_dtime =print_dtime
          dprint_struct.lasttime = newtime
      endif
      if n_elements(print_time)   ne 0 then begin
         if size(/type,print_time) eq 7 then  dprint_struct.tformat = print_time
         dprint_struct.print_time = keyword_set(print_time)
      endif
      if n_elements(print_trace)  ne 0 then dprint_struct.print_trace = print_trace
      if ((n_elements(set_display_object) ne 0) && (scope_level() eq 2 )) then dprint_struct.display_object = set_display_object
;      if n_elements(set_display_routine) ne 0 then dprint_struct.display_routine = set_display_routine
      if n_elements(filename) ne 0 then begin
          if dprint_struct.file_unit gt 0  then free_lun,dprint_struct.file_unit
          dprint_struct.file_unit = -1
          if keyword_set(filename)  then begin
              openw,unit,filename,/get_lun
              dprint_struct.file_unit = unit
              fs = fstat(unit)
              dprint_struct.file_name_c = fs.name
          endif
      endif
      get_dprint_struct = dprint_struct
      get_check_events = dprint_struct.check_events
      if n_elements(check_events) ne 0 then dprint_struct.check_events = check_events
      if n_elements(setdebug) ne 0 then dprint_struct.debug = setdebug
      if n_elements(setverbose) ne 0 then dprint_struct.debug = setverbose
      if keyword_set(status) then printdat,dprint_struct
;      return
;   endif

   if keyword_set(help) then printdat,dprint_struct

   IF N_ELEMENTS(dlevel) EQ 0 THEN dlevel = 0
   delta_time = newtime-dprint_struct.lasttime

   if keyword_set(dprint_struct.check_events && ~keyword_set(no_check_events)) then begin
      event= widget_event(/nowait)
    ; if event.top ne 0 then $
    ;   printdat,event,time_string(systime(1),prec=3)
   endif

   if keyword_set(dwait) and not keyword_set(dprint_struct.break_flag) then begin
      if dwait ge delta_time  then return
   endif

   if newtime-dprint_struct.lastflushtime gt 10. then begin
      dprint_struct.lastflushtime = newtime
      wait,.01    ; This wait statement is the only way I know to flush the print buffer. This is a low overhead.
   endif

   dbg = (n_elements(verbose) ne 0 && verbose lt 99) ? verbose : dprint_struct.debug
   IF dlevel GT dbg and not keyword_set(dprint_struct.break_flag) THEN RETURN

   dprint_struct.ireturn = 1

   prefix = ''

   if dprint_struct.print_dlevel then  prefix=[prefix, string(dlevel,dbg,format='(i0.0,"/",i0.0)') ]
   if dprint_struct.print_time   then  prefix=[prefix, time_string(tformat=dprint_struct.tformat,newtime,/local)]
   if dprint_struct.print_dtime  then  prefix=[prefix, string(format='(f10.3)',delta_time) ]
   if dprint_struct.print_trace ne 0  then  begin
     stack = scope_traceback(/structure,system=0)
     level = n_elements(stack) -1
;     if level gt 200 then begin
;        Message,"Stack is too large! Runaway recursion?"
;     endif
     if keyword_set(sublevel) then level -= sublevel
     level = level > 1
     stack = stack[0:level-1]
;     levels = indgen(level)
;     stacknames=strtrim(levels,2)+'  '+stack.routine + string(stack.line,format='(" (",i0,")")')
     stacknames=stack.routine + string(stack.line,format='("(",i0,")")')
     case dprint_struct.print_trace of
        1: if level ge 2 then stacknames = stacknames[level-1]
        2: if level ge 2 then stacknames[0:level-2] = '  '
        4: stacknames = string(/print,format='(i2," ",a-30)',level,stacknames[level-1])
        else:   ; do nothing
     endcase
     prefix = [prefix,stacknames]
;     if level eq 1 and stack[0].line le 1 then prefix=''     ; Calls from command line  (Kludge)
  endif
  if keyword_set(prefix) then prefix = prefix[1:*]

    if 1 then begin

    endif

   dprint_struct.lasttime = newtime

   if dprint_struct.file_unit gt 0 then begin   ; perform safety check
       fs = fstat(dprint_struct.file_unit)
       if fs.open eq 0 or fs.name ne dprint_struct.file_name then begin
           dprint_struct.file_unit = -1
           dprint_struct.file_name = ''
       endif
   endif
   u = n_elements(unit) ? unit : dprint_struct.file_unit

   prefix_str = keyword_set(prefix) ? strjoin(prefix+': ') : ''
;   if keyword_set(prefix) then print,prefix_str,format='(a,$)'

   if keyword_set(phelp) then begin    ; experimental option - this may change
        vnames0=scope_varname(v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12)
        vnames1=scope_varname(v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,level=-1)
        for i=0,np-1 do begin
           printdat,unit=u,scope_varfetch(vnames0[i]),varname=vnames1[i],output=txt,recursemax=phelp
           text = (i eq 0) ? txt : [text,txt]
        endfor
   endif else begin

   case np of
   0:  text = ''  ;string(/print,format=format)
   1:  text = string(/print,format=format,v1)
   2:  text = string(/print,format=format,v1,v2)
   3:  text = string(/print,format=format,v1,v2,v3)
   4:  text = string(/print,format=format,v1,v2,v3,v4)
   5:  text = string(/print,format=format,v1,v2,v3,v4,v5)
   6:  text = string(/print,format=format,v1,v2,v3,v4,v5,v6)
   7:  text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7)
   8:  text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8)
   9:  text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9)
   10: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10)
   11: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11)
   12: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12)
   13: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13)
   14: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14)
   15: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15)
   16: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16)
   17: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17)
   18: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18)
   19: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19)
   20: text = string(/print,format=format,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20)
   else: text = 'Get real! 20 variables is enough!'
   endcase

   endelse

   ;msg = prefix_str+text
   prefix_len = strlen(prefix_str)
   spaces = prefix_len gt 0 ? string(replicate(32b,prefix_len)) : ''
;   for i=0,n_elements(msg)-1 do  printf,u,msg[i]

;   str_element,dprint_struct,'display_routine',disprout
;   if size(/type,display_routine) eq 7 then disprout = display_routine
;   for i=0,n_elements(disprout)-1 do if keyword_set(disprout[i]) then begin
;       call_procedure,disprout[i],text,prefix=prefix_str
;   endif

   if n_elements(text) gt dprint_struct.max_lines then begin
        text = text[0:dprint_struct.max_lines-1]
        text[dprint_struct.max_lines-1] = 'Output Terminated.'
   endif

   dispobj = dprint_struct.display_object                             ; grab default object from structure
   if keyword_set(display_object) then dispobj = display_object  ; grab alternative object(s) from keyword
   for i=0,n_elements(dispobj)-1 do begin
        if (obj_valid(dispobj[i]) && obj_hasmethod(dispobj[i],'print')) then begin
            (dispobj[i])->print,prefix=prefix_str,text
        endif else begin                                             ; Default output (typical usage)
            for j=0,n_elements(text)-1 do  printf,u,(j eq 0 ? prefix_str : spaces) + text[j]
        endelse
   endfor


   if keyword_set(dwait) then wait, .01      ; This  line is used to flush the print buffer (update the display)


   if keyword_set(dprint_struct.widget_id) and newtime-dprint_struct.widget_lasttime ge dprint_struct.widget_dwait then begin
        dprint_struct.widget_lasttime = newtime
        dprinttool,/update,text,prefix=prefix ,/sublevel
   endif

   dprint_struct.ireturn=0

   if keyword_set(dprint_struct.break_flag) and arg_present(break_requested) then begin
       dprint_struct.break_flag = 0
       break_requested=1
       print,strjoin(prefix,': ')+' Break Detected'

   endif
   return

END

;---------------------------------------------------------------------------
; End of 'dprint.pro'.
;---------------------------------------------------------------------------



; .run
;pro testrout,str,prefix=pre
;print,(keyword_set(pre) ? '('+pre+') ' : '') + '"'+str+'"'
;end
