;+
;Procedure: plotxylib
;
;Purpose:  A library of helper functions for plotxy, plotxyz, and plotxyvec.  
;          To make the library available for a routine just call: plotxylib
;          That will force all the routines to be compiled.
;          
;          pxy_set_state sets the state of !TPLOTXY which keeps track
;          of windowing information for the plotxy* routines.  pxy_push_state
;          pushes arguments to these routines onto a data structure that allows
;          them to be replot without retyping the arguments.  pxy_replot will
;          replot a series of calls from memory.  pxy_get_pos will calculate
;          the position and shape of a window from the windowing information
;          in !TPLOTXY and the requested data ranges and margins for a window.
;          pxy_set_window is a routine that houses some redundant
;          initialization code. 
;          
;          SEE ALSO: plotxy,plotxyz,plotxyvec
;
; $LastChangedBy: pcruce $
; $LastChangedDate: 2009-12-15 14:49:37 -0800 (Tue, 15 Dec 2009) $
; $LastChangedRevision: 7011 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/ssl_general/trunk/tplot/plotxylib.pro $
;
;-


;HELPER FUNCTION
;set window up, store windowing information
pro pxy_set_window,overplot,addpanel,replot,window,xsize,ysize,wtitle,multi,noisotropic,isotropic=isotropic

  compile_opt hidden,idl2

;if the window has not yet been generated or no more
;windows are available

  if ~keyword_set(overplot) && ~keyword_set(addpanel) && strlowcase(!D.name) ne 'ps' && strlowcase(!D.name) ne 'z' then begin
   
     if ~undefined(window) or keyword_set(xsize) or keyword_set(ysize) or keyword_set(wtitle) or !D.window eq -1 then begin

        device,window_state=wlist
        
        if ~undefined(window) then begin

          ;if the window doesn't really exist create it
           if window ge n_elements(wlist) || window lt 0 then $
              message,'You passed an out of range window value' $
           else if wlist[window] eq 0 then $
              window,window,xsize=640,ysize=512 $
           else $
              wset,window 
    
        endif else if !D.window eq -1 then begin
           window = 0
           xsize = 640
           ysize = 512
        endif else $
           window = !D.window

        if not keyword_set(xsize) then begin
           xs = !D.X_SIZE
        endif else begin
           xs = xsize
        endelse

        if not keyword_set(ysize) then begin
           ys = !D.Y_SIZE
        endif else begin
           ys = ysize
        endelse

        if not keyword_set(wtitle) then begin 
           wt = strcompress('IDL ' + string(window))
        endif else begin
           wt = wtitle
        endelse

        window,window,xsize=xs,ysize=ys,title=wt,retain=2
  
     endif 

   ;if this isn't a recursive call making use of the state information
   ;then we can safely reset the state information
     if ~keyword_set(replot) then begin
      
        pxy_set_state,multi

     endif

  endif

  if keyword_set(addpanel) then begin

     !tplotxy.current++
  
  endif

  if ~keyword_set(noisotropic) then begin
     isotropic = 1
  endif else begin
     isotropic = 0
  endelse

end

;helper function, intializes the tplotxy system variable
pro pxy_set_state,multi

  compile_opt idl2,hidden

  plotlist = 0

  if keyword_set(multi) then begin

     args = strsplit(multi,' *[ ,/:;\.\\] *',/extract,/fold_case,/regex,count=c)

     if c ne 2 then begin
        message,'illegal multi string "' + multi + '"'
     endif

     if stregex(args[0],'r',/boolean) then begin
        revcols = 1
     endif else begin
        revcols = 0
     endelse

     if stregex(args[1],'r',/boolean) then begin
        revrows = 1
     endif else begin
        revrows = 0
     endelse

     cols = long(stregex(args[0],'[0-9]*',/extract))

     if cols eq 0 then begin
        message,'error parsing multi cols: "' + args[0] + '"'
     endif 

     rows = long(stregex(args[1],'[0-9]*',/extract))
     
     if cols eq 0 then begin
        message,'error parsing multi rows: "' + args[1] + '"'
     endif 

  endif else begin
    
     revcols = 0
     revrows = 0
     cols = 1
     rows = 1
 
  endelse

  DEFSYSV,'!tplotxy',exists=bool

  ;free any old memory
  if bool && is_struct(*(!tplotxy.plotvec)) then begin
     
     plotvec = !tplotxy.plotvec

     t=csvector(*plotvec,/free)
     ptr_free,plotvec

  endif

  tplotxyval = { rows:rows,$
                 revrows:revrows,$
                 cols:cols,$
                 revcols:revcols,$
                 current:0,$
                 pos:dblarr(4),$ ;these values needed to properly overplot arrows
                 xrange:dblarr(2),$
                 yrange:dblarr(2),$
                 plotvec:ptr_new(csvector('start')) }


  DEFSYSV,'!tplotxy',tplotxyval

end


;adds the information to repeat the previous tplotxy call to the 
;tplotxy global variable
pro pxy_push_state,func_name,state,_extra=ex

  compile_opt idl2,hidden

  plotvec = *(!tplotxy.plotvec)

  ptr_free,!tplotxy.plotvec

  str_element,state,'ex',ex,/add

  func = {func:func_name,state:state}

  !tplotxy.plotvec = ptr_new(csvector(func,plotvec))

end

;determines the position of the current panel
;using the margin information and the tplotxy information
;assumes data is already logarithm'd, sorted, etc...

function pxy_get_pos,x,y,isotropic,xmargin,ymargin

  compile_opt idl2,hidden

  ;validate inputs
  if keyword_set(xmargin) then begin

     if n_elements(xmargin) ne 2 then begin
        message,'malformed x margin'
     endif

     if ~is_num(xmargin,/real) then begin
        message,'x margin must contain real number'
     endif

     id = where(xmargin gt 1 or xmargin lt 0) 

     if id[0] ne -1 then begin
        message,'x margin must be in the range [0,1]'
     endif

  endif else begin

     xmargin = [.15,.17]

  endelse
              
  if keyword_set(ymargin) then begin

     if n_elements(ymargin) ne 2 then begin
        message,'malformed y margin'
     endif
     
     if ~is_num(ymargin,/real) then begin
        message,'y margin must contain real number'
     endif

     id = where(ymargin gt 1 or ymargin lt 0) 

     if id[0] ne -1 then begin
        message,'y margin must be in the range [0,1]'
     endif

  endif else begin

     ymargin = [.1,.075]

  endelse

  ;verify that the current panel is not too large
  
  if !tplotxy.current ge (!tplotxy.rows * !tplotxy.cols) then begin
     message,'no more panels available in current layout'
  endif

  xpanel_size = 1.0/!tplotxy.cols
  ypanel_size = 1.0/!tplotxy.rows

  if !tplotxy.revcols eq 0 then begin
     xpanel_cur = !tplotxy.current mod !tplotxy.cols
  endif else begin
     xpanel_cur = !tplotxy.cols - 1 - (!tplotxy.current mod !tplotxy.cols)
  endelse

  if !tplotxy.revrows ne 0 then begin
     ypanel_cur = !tplotxy.current / !tplotxy.cols
  endif else begin
     ypanel_cur = !tplotxy.rows - 1 - (!tplotxy.current / !tplotxy.cols)
  endelse

  ;coordinates
  x1 = xpanel_cur * xpanel_size + xpanel_size*xmargin[0]
  x2 = (xpanel_cur+1) * xpanel_size - xpanel_size*xmargin[1]

  y1 = ypanel_cur * ypanel_size + ypanel_size*ymargin[0]
  y2 = (ypanel_cur+1) * ypanel_size - ypanel_size*ymargin[1]

  if keyword_set(isotropic) then begin

     x_data_sz = abs(double(x[1])-double(x[0]))

     y_data_sz = abs(double(y[1])-double(y[0]))

     ;plot size normalized into centimeters for comparisons
     x_plot_sz = !D.x_size*(x2-x1)/!D.x_px_cm
     
     y_plot_sz = !D.y_size*(y2-y1)/!D.y_px_cm

     if x_data_sz/y_data_sz lt x_plot_sz/y_plot_sz then begin

        x_plot_sz = y_plot_sz * x_data_sz/y_data_sz

     endif else begin

        y_plot_sz = x_plot_sz * y_data_sz/x_data_sz

     endelse 
     
     x2 = x1 + !D.x_px_cm * x_plot_sz/!D.x_size

     y2 = y1 + !D.y_px_cm * y_plot_sz/!D.y_size 
       
  endif
  
  !tplotxy.pos = [x1,y1,x2,y2]
  !tplotxy.xrange = x
  !tplotxy.yrange = y

  return, [x1,y1,x2,y2]

end

pro pxy_replot

  compile_opt idl2,hidden

  !tplotxy.current = 0

  plotvec = *(!tplotxy.plotvec)

  len = csvector(plotvec,/length)

  for i = 1,len-1 do begin

     c = csvector(i,plotvec,/read)

     state=c.state

     if c.func eq 'plotxy' then begin

        plotxy, state.vectors,replot=1,_extra=state.ex

     endif else if c.func eq 'plotxyz' then begin

        plotxyz,state.x,state.y,state.z,replot=1,_extra=state.ex

     endif else if c.func eq 'plotxyvec' then begin

        plotxyvec,state.xy,state.dxy,replot=1,_extra=state.ex
        
     endif else begin
        
        message,'unrecognized replot function'

     endelse

  endfor

end

pro plotxylib

;does nothing
;call plotxylib at the beginning of any
;routine that needs the routines in this
;library to guarantee that they are compiled

end
