;+
;
; Project   : Densitycube, reconstruction, 3D, and s3drs software
;                   
; Name      : render_rot_gui
;               
; Purpose   : used to manipulate 3D densitycubes and do 3D reconstructions
;               
; Explanation: 3D Widget for manipulating and viewing density cubes,
; with optional whitelight renderers.
;               
; Use       : standalone
;    
; Inputs    : timage (image cube)
;             rescale, N, dsize, oom = widget size params
;             threshold = scaling param
;               
; Outputs   : 
;
; Keywords  : /reiser, /rtwl, /cpixon, /tpixon = turns on different renderers
;             /demo = runs a demo that requires no input data
;             /skipsum, /warn, /cache, /flush, /skipdragger = turns
;             off some functionality
;               
; Common    : none
;               
; Restrictions: requires the nrlgen widget 'dragger.pro'
;               
; Side effects: 
;               
; Category    : 3D, visualization, density, cubes, widgets
;               
; Prev. Hist. : None.
;
; Written     : Sandy Antunes, NRC, March-April 2009
;               
;-            
;  /reiser : use Paul's stand-alone WL renderer
;  /rtwl : use RT Whitelight
;  /cpixon : use classic Pixon
;  /tpixon : use tetrahedral Pixon (slow to instantiate!)
;  /skipsum : do _not_ plot the los density sums
;  threshold : cut-off threshold for the wireframe part
;  oom : order-of-magnitude, alternate way to specify a cut-off threshold
;  /rescale=N : rescale rendered plots upward by this factor

; note that the returned dragger angles phi, theta, gamma
; can be converted to lat/lon/roll (relative to the initial
; startpoint is 0,0,0) via:
;   cosmat=rotnmat(phi,theta,gamma); angles in degrees -> rotation matrix
;   latlonroll=rtrotmat2lonlatroll(cosmat); rotmat into our 3-position array
; we implement this in the waypoint save file of 'catch_dragger'

PRO rr_exit,myevent
  ; deletes all saved info then exits

  rr_base=WIDGET_INFO(myevent.top,/uname)
  ;print,'debug a, operating on',rr_base
  statevar_delete, rr_base, /all
  WIDGET_CONTROL,myevent.top,/destroy

END

PRO toggle_waypoints,myevent

  WIDGET_CONTROL, myevent.id, GET_UVALUE=u

  if (u eq 0) then begin
      u=1
      value='Disable waypoint saves'
  end else begin
      u=0
      value='Enable waypoint saves'
  end

  WIDGET_CONTROL, myevent.id, SET_UVALUE=u
  WIDGET_CONTROL, myevent.id, SET_VALUE=value

  rr_base=WIDGET_INFO(myevent.top,/uname)
  statevar_addrep, rr_base,'waypoints',u

END

FUNCTION quickrtwl,vol
  ; calls up RTWL in a minimal, default, stripped down way to make 1 image

  N=(size(vol))[1]
  modparam=timage2modparam(vol,/quiet)
  raytracewl,sbt,imsize=[N,N],losnbp=long(N),losrange=[-20,20],$
    modelid=25,modparam=modparam,neang=[0,0,0]*!dtor,/cor2,/quiet

  return,sbt.im

END

FUNCTION quickpixon,ptype,vol,pxwrapper,oPixon
;      timage=timage/1.0e6/0.2   ; norm to datamax ~ 1
;  oPixon->Setimage,image=vol;
  oPixon->Replace,image=vol;
  pxwrapper.object=ptype
  im=stereo_project(pxwrapper,oPixon)
  return,im
END


PRO quickrendnden,vol
  ; a little test to make a single render versus a straight summation

  im1=ijkproj(vol,'ij')
  im2=quickrtwl(vol)
  tv_multi,[[[im1]],[[im2]]]

END


PRO fastrotwrap,vol,phi
   ; wrapper that quickly rotates then renders+sumplots a given volume

   cuberot3d,vol,phi
   quickrendnden,vol

END

PRO set_rr_commons,base,timage,rescale,N,plotedge,pxwrapper, $
                   oCPixon,oTPixon,rtwl,reiser,cpixon,tpixon,plotsum,ptid

  ; sets up a common block so routines like 'dragger' can invoke plot_rrt
  ; the only item stored in this common is the base widget id where our
  ; structure pointer resides, so we can add and fetch data from it.

  rr_base=WIDGET_INFO(base,/uname)
  ;print,'debug b, operating on',rr_base

  statevar_init,rr_base; set up our pointer

  statevar_addrep,rr_base,'ptid',ptid;

  statevar_addrep,rr_base,'timage',timage

  if (n_elements(pxwrapper) eq 0) then pxwrapper=0
  if (n_elements(oCPixon) eq 0) then oCPixon=0
  if (n_elements(oTPixon) eq 0) then oTPixon=0
  statevar_addrep,rr_base,'pxwrapper',pxwrapper
  statevar_addrep,rr_base,'oCPixon',oCPixon
  statevar_addrep,rr_base,'oTPixon',oTPixon

  flags = { $
            N: N, $
            plotedge: plotedge, $
            rescale: rescale, $
            rtwl: rtwl, $
            cpixon: cpixon, $
            tpixon: tpixon, $
            reiser: reiser, $
            plotsum: plotsum $
          }
  statevar_addrep,rr_base,'flags',flags

  ; make placeholders for cacheing images in case of later saves
  sumi=fltarr(N*rescale,N*rescale)
  rtwli=0
  cpixoni=0
  tpixoni=0
  reiseri=0
  if (rtwl) then rtwli=sumi
  if (cpixon) then cpixoni=sumi
  if (tpixon) then tpixoni=sumi
  if (reiser) then reiseri=sumi
;  rrt_save = { sum: sumi, rtwl: rtwli, cpixon: cpixoni, tpixon: tpixoni}
  statevar_addrep,rr_base,'sum',sumi
  statevar_addrep,rr_base,'rtwl',rtwli
  statevar_addrep,rr_base,'cpixon',cpixoni
  statevar_addrep,rr_base,'tpixon',tpixoni
  statevar_addrep,rr_base,'reiser',reiseri

END

PRO save_rr, event

  rr_base=WIDGET_INFO(event.top,/uname)
  ;print,'debug c, operating on',rr_base

  savetype=widget_info(event.id,/UNAME)

  fname = strtrim(savetype,2) + '_rrdata.sav'
  if (savetype eq 'rtwl') then datum=statevar_fetch(rr_base,'rtwl')
  if (savetype eq 'cpixon') then datum=statevar_fetch(rr_base,'cpixon')
  if (savetype eq 'tpixon') then datum=statevar_fetch(rr_base,'tpixon')
  if (savetype eq 'reiser') then datum=statevar_fetch(rr_base,'reiser')
  if (savetype eq 'sum') then datum=statevar_fetch(rr_base,'sum')
  if (n_elements(datum) ne 0) then begin
      save,datum,file=fname
      print,'saved ',fname
  end

END

PRO catch_dragger,top,i,j,k,zoom
; catches 'GO' from embedded dragger

  rr_base=WIDGET_INFO(top,/uname)
  waypoints=statevar_fetch(rr_base,'waypoints');

  ; also produce a long/lat/roll and distance
  cosmat=rotnmat(i,j,k); angles in degrees -> rotation matrix
  llr=rtrotmat2lonlatroll(cosmat); rotmat into our 3-position array

  if (waypoints) then save_waypoint,i,j,k,zoom,llr[0],llr[1],llr[2]

  plot_rrt,top,i,j,k,zoom

END

; simple routine just appends positions to a waypoint file
PRO save_waypoint,i,j,k,zoom,lat,lon,roll
;  print,'Saving waypoint',i,j,k,zoom
  wpfile='rr_waypoints.txt'
  openw,lun,wpfile,/get_lun,/append
  printf,lun,i,j,k,zoom,lat,lon,roll
;  print,i,j,k,zoom,lat,lon,roll
  free_lun,lun

END


PRO plot_rrt,top,i,j,k,zoom

  rr_base=WIDGET_INFO(top,/uname)
  ;print,'debug d, operating on',rr_base

  ptid=statevar_fetch(rr_base,'ptid');
  wset,ptid

  plotflags=statevar_fetch(rr_base,'flags')
  N=plotflags.N
  plotedge=plotflags.plotedge
  rescale=plotflags.rescale
  ; we also use plotflags.rtwl and plotflags.pixon without intermediaries

;  phi=[i,j,k]
  phi=[-k,j,i]
  vol=statevar_fetch(rr_base,'timage'); always copy to prevent distortion

              ; could also use SSW/yohkoh/ucon/idl/slater/rot_3d.pro
  cuberot3d,vol,phi

  if (plotflags.plotsum) then im1=ijkproj(vol,'jk')

  if (plotflags.rtwl) then im2=quickrtwl(vol)
              
  if (plotflags.reiser) then im5=pr_render($
       statevar_fetch(rr_base,'pxwrapper'),vol)

  if (plotflags.cpixon) then im3=quickpixon('c',vol,$
       statevar_fetch(rr_base,'pxwrapper'),statevar_fetch(rr_base,'oCPixon'))

  if (plotflags.tpixon) then im4=quickpixon('t',vol,$
       statevar_fetch(rr_base,'pxwrapper'),statevar_fetch(rr_base,'oTPixon'))

  if (rescale gt 1) then begin
      if (plotflags.plotsum) then im1=congrid(im1,N*rescale,N*rescale)
      if (plotflags.rtwl) then im2=congrid(im2,N*rescale,N*rescale)
      if (plotflags.reiser) then im5=congrid(im5,N*rescale,N*rescale)
      if (plotflags.cpixon) then im3=congrid(im3,N*rescale,N*rescale)
      if (plotflags.tpixon) then im4=congrid(im4,N*rescale,N*rescale)
  end

  ; from bottom to top
  irow=0
  if (plotflags.plotsum) then begin
;      tvscl,not (not im1),plotedge,irow*N*rescale ; boolean projection
;      irow=irow+1
      tvscl,alog10(im1 > mean(im1)),plotedge,irow*N*rescale ; log density sum
      irow=irow+1
      tvscl,im1,plotedge,irow*N*rescale ; linear density sum

      statevar_addrep,rr_base,'sum',im1 ;keep in case of later save

  end

  if (plotflags.rtwl) then begin
      irow=irow+1
      tvscl,im2,plotedge,irow*N*rescale ; rtwl linear
      irow=irow+1
      tvscl,alog10(im2 > mean(im2)),plotedge,irow*N*rescale ; rtwl log

      statevar_addrep,rr_base,'rtwl',im2 ;keep in case of later save

  end

  if (plotflags.reiser) then begin
      irow=irow+1
      tvscl,im5,plotedge,irow*N*rescale ; reiser linear
      irow=irow+1
      tvscl,alog10(im5 > mean(im5)),plotedge,irow*N*rescale ; reiser log

      statevar_addrep,rr_base,'reiser',im5; keep in case of later save

  end

  if (plotflags.cpixon) then begin
      irow=irow+1
      tvscl,im3,plotedge,irow*N*rescale ; cpixon linear
      irow=irow+1
      tvscl,alog10(im3 > mean(im3)),plotedge,irow*N*rescale ; cpixon log

      statevar_addrep,rr_base,'cpixon',im3 ;keep in case of later save

  end

  if (plotflags.tpixon) then begin
      irow=irow+1
      tvscl,im4,plotedge,irow*N*rescale ; tpixon linear
      irow=irow+1
      tvscl,alog10(im4 > mean(im4)),plotedge,irow*N*rescale ; tpixon log

      statevar_addrep,rr_base,'tpixon',im4 ;keep in case of later save

  end

  wait,0.1                      ; enforce a minimum 0.1 second wait

END

PRO rr_cache,action,N,timage,pxwrapper,oCPixon,oTPixon

   cachefile='rr_restore_'+strtrim(string(N),2)+'.sav'
   
   if (action eq 'fetch' and file_exist(cachefile)) then begin
       print,'Restoring from previous session in '+cachefile
       restore,cachefile
   end else if (action eq 'save') then begin
       print,'Cacheing current session in '+cachefile
       save,timage,pxwrapper,oCPixon,oTPixon,file=cachefile
   end

END

PRO render_rot_gui,timage,$
                   cpixon=cpixon,tpixon=tpixon,reiser=reiser,rtwl=rtwl, $
                   rescale=rescale,demo=demo,N=N,$
                   skipdragger=skipdragger,dsize=dsize,skipsum=skipsum, $
                   warn=warn,cache=cache,flush=flush,threshold=threshold, $
                   oom=oom
  ; full-blown rotate-over-time plotting test

  ; first, get the likely global N
  if (n_elements(N) eq 0) then N1=64 else N1=N
  if (n_elements(timage) ne 0) then N=(size(timage))[1] else N=N1
  ;if (N ne N1) then print,"Warning, 'N=' parameter does not match given image, using N="+strtrim(string(N),2)
  if (n_elements(threshold) eq 0) then threshold=0

  ; first, see if this is restoring the most recent save
  cache=KEYWORD_SET(cache); saves intermediate state in case of crash/exit
  flush=KEYWORD_SET(flush); flushes earlier caches
  savecache=cache; use to track whether to re-save an already loaded cache
  ; so just 'cache' means load-if-exists, and save cache in all cases
  ; just 'flush' means delete-if-exists and do not save
  ; 'cache' and 'flush' means delete-if-exists, then save new info in cache
  if (cache eq 1 and flush eq 0) then begin

      if (n_elements(timage) ne 0) then temptimage=timage; save new image

      rr_cache,'fetch',N,timage,pxwrapper,oCPixon,oTPixon

      savecache=0;

      if (n_elements(temptimage) ne 0) then begin
          ; compare checksums to see if cached object timage matches
          ; the one provided at the command line.  If they do not match,
          ; update the objects with the command-line timage.
          checksum32,timage,c1
          checksum32,temptimage,c2
          if (c1 ne c2) then begin
              print,'Updating cached objects with new image'
              ; replace cached object's timage with new timage
              timage=temptimage
              temptimage=0; scurvy way to free no-longer-needed memory
              oCPixon->Replace,image=timage;
              oTPixon->Replace,image=timage;
              savecache=1; image changed, so will need to re-save cache
          end
      end

  end

  if (n_elements(timage) eq 0) then timage=fake_ne_cube(N,gen='cube',$
                                                        lit=fix(N/4))
  if (n_elements(rescale) eq 0) then rescale=1
  demo=KEYWORD_SET(demo)
  skipdragger=KEYWORD_SET(skipdragger)
  rtwl=KEYWORD_SET(rtwl); turns on RTWL renders
  reiser=KEYWORD_SET(reiser); turns on Reiser stand-alone renderer
  cpixon=KEYWORD_SET(cpixon); turns on cPixon renders
  tpixon=KEYWORD_SET(tpixon); turns on tPixon renders
  skipsum=KEYWORD_SET(skipsum); turns off default 'density summation' plots
  warn=KEYWORD_SET(warn); flag for checking for large tpixon creation
  if (skipsum) then plotsum=0 else plotsum=1; change to positive for clarity

  if (skipdragger eq 0) then common share1, WIDS, COMMONMP

  vol=timage; operate just on a copy, not the original

  if (n_elements(oom) ne 0) then begin
      dmax=max(vol)
      threshold=dmax/(10.0^oom)
  end

  if (plotsum) then nplots=2 else nplots=0; basic 2 plots is default

  if (reiser) then nplots=nplots+2
  if (cpixon) then nplots=nplots+2
  if (tpixon) then nplots=nplots+2
  if (rtwl) then nplots=nplots+2

  ; optional warning for slow/large Tpixon creation
  ;print,warn,N,tpixon
  if (warn eq 1 and N gt 16 and tpixon eq 1) then begin
      print,'Warning, creation of TPixon object of size ',N,' may take a while.'
      yes_no,'TPixon objects > 16 take a while to make: continue? (yes/no): ',a
      if (a eq 0) then stop,'Okay, aborting Render Rotation GUI'
  end

  if (n_elements(dsize) eq 0) then dsize=250; dragger square window size
  if (dsize lt 150) then dsize=150; enforce minimum displayable size
  dwidgets=190; fixex size (cost) of dragger widgets
  dtotal=dwidgets+dsize; so this is total dragger xsize

  plotedge=120; location of start of plots
  xsize=plotedge+N*rescale+10
  ysize=nplots*N*rescale+10

;  print,xsize,ysize,plotedge,N,rescale,nplots

  labelspacing=1.0/(1.0*nplots)
;  labeledge=10.0/xsize; location of start of labels before plots
  labeledge=0.1; location of start of labels before plots

  base=widget_base(resource_name='base', $
                   title='Render Rotation GUI',$
                   /ROW,TLB_FRAME_ATTR=1, MBAR=menubase)
  if (skipdragger eq 0) then draggerbase=widget_base(base,$
                           xsize=dtotal,ysize=dtotal,uname='draggerbase')
  displaybase=widget_draw(base,xsize=xsize,ysize=ysize,uname='displaybase')

  ; note that we also use 'mfilebase' as the widget to store our
  ; persistent data pointers, so make sure it is created or all will fail.
  mfilebase = WIDGET_BUTTON(menubase, VALUE='File',/MENU)
  WIDGET_CONTROL,base, set_uname=mfilebase; save for tracking
  ;mmm=WIDGET_INFO(base,/uname)
  ;print,'debug z: checking base uvale is ',mmm

  msave_button1 = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                                               VALUE='Save density image',$
                                               UNAME='sum',$
                                               EVENT_PRO='save_rr')
  if (rtwl) then msave_button2 = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                                               VALUE='Save RTWL',$
                                               UNAME='rtwl',$
                                               EVENT_PRO='save_rr')
  if (reiser) then msave_button5 = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                                                VALUE='Save Render',$
                                                UNAME='reiser', $
                                                EVENT_PRO='save_rr')
  if (cpixon) then msave_button3 = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                                                VALUE='Save cPixon',$
                                                UNAME='cpixon', $
                                                EVENT_PRO='save_rr')
  if (tpixon) then msave_button4 = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                                                VALUE='Save tPixon',$
                                                UNAME='tpixon', $
                                                EVENT_PRO='save_rr')
  mquit_button = WIDGET_BUTTON(mfilebase, SENSITIVE=1, $
                               VALUE='Quit...', $
                               UVALUE='quit', EVENT_PRO='rr_exit')

  maboutbase = WIDGET_BUTTON(menubase, VALUE='About',/MENU)
  mabout_button = WIDGET_BUTTON(maboutbase, SENSITIVE=1, $
                                VALUE='About...', $
                                UVALUE='about', EVENT_PRO='ignore')

  mmoviebase = WIDGET_BUTTON(menubase, VALUE='Movie',/MENU)
  mwaypt_button = WIDGET_BUTTON(mmoviebase, SENSITIVE=1, $
                                VALUE='Enable waypoint saves', $
                                UVALUE=0, UNAME='waypoint', $
                                EVENT_PRO='toggle_waypoints')

  WIDGET_CONTROL, base, /REALIZE

  ; return pixon-frame wireframe

  xxyyzz=timage2wireframe(rearrange(vol,[2,3,-1]),threshold=threshold)

  if (skipdragger eq 0) then $
    dragger,xxyyzz[*,0],xxyyzz[*,1],xxyyzz[*,2],$
    embed=draggerbase,nozoom=0,radius=20,sc=0.5,/solid,$
    window_size=[dsize,dsize]

  WIDGET_CONTROL,displaybase,GET_VALUE=ptid
  wset,ptid

;  window,/free,xsize=plotedge+N*rescale,ysize=nplots*N*rescale

  halfshift=labelspacing/4.0
  if (halfshift gt 1.0) then halfshift=labelspacing/2.0

  xl=labelspacing/2
;  print,labelspacing,halfshift,xl

  ; bottom to top
  if (plotsum) then begin
;      xyouts,labeledge,xl,'boolean'
;      xyouts,labeledge,xl-halfshift,'projection'
;      xl=xl+labelspacing

      print,'start ',xl
      xyouts,labeledge,xl,'density sum',/normal
      xyouts,labeledge,xl-halfshift,'(log>mean)',/normal
      xl=xl+labelspacing
      print,'new ',xl
      xyouts,labeledge,xl,'density sum',/normal
      xyouts,labeledge,xl-halfshift,'(linear)',/normal
  end

  if (rtwl) then begin

      xl=xl+labelspacing
      xyouts,labeledge,xl,'rtwl render',/normal
      xyouts,labeledge,xl-halfshift,'(linear)',/normal

      xl=xl+labelspacing
      xyouts,labeledge,xl,'rtwl render',/normal
      xyouts,labeledge,xl-halfshift,'(log>mean)',/normal

  end

  if (reiser) then begin

      xl=xl+labelspacing
      xyouts,labeledge,xl,'WL render',/normal
      xyouts,labeledge,xl-halfshift,'(linear)',/normal

      xl=xl+labelspacing
      xyouts,labeledge,xl,'WL render',/normal
      xyouts,labeledge,xl-halfshift,'(log>mean)',/normal

      prep_3drv,pxwrapper,views=1,N=N ; for Pixon render
          
  end

;  if (cpixon eq 1 and tpixon eq 1) then pixon_env,'b',both=1
; Force both to load if you need either-- this prevents clashes if
; you use later decide to use the other one or use both in a future
; invocation.  This is because, when using both CPixon and TPixon
; at the same time, the TPixon environment always has to be loaded
; first.  Fortunately, the 'pixon_env' routine handles this for you below.
  if (cpixon eq 1 or tpixon eq 1) then pixon_env,both=1

  if (cpixon) then begin

      xl=xl+labelspacing
      xyouts,labeledge,xl,'cPixon render',/normal
      xyouts,labeledge,xl-halfshift,'(linear)',/normal

      xl=xl+labelspacing
      xyouts,labeledge,xl,'cPixon render',/normal
      xyouts,labeledge,xl-halfshift,'(log>mean)',/normal

      if (n_elements(oCPixon) eq 0) then begin
          prep_3drv,pxwrapper,views=1 ; for Pixon render
          pimage=vol
          p_generate_pixon,'classic',pxwrapper,oCPixon,image=pimage
          if (cache) then savecache=1; either new or cache changed, so resave
      end
      ;stereo_osetup,pxwrapper,timage,oCPixon

  end

  if (tpixon) then begin

      xl=xl+labelspacing
      xyouts,labeledge,xl,'tPixon render',/normal
      xyouts,labeledge,xl-halfshift,'(linear)',/normal

      xl=xl+labelspacing
      xyouts,labeledge,xl,'tPixon render',/normal
      xyouts,labeledge,xl-halfshift,'(log>mean)',/normal

      if (n_elements(oTPixon) eq 0) then begin
          prep_3drv,pxwrapper,views=1 ; for Pixon render
          pimage=vol
          p_generate_pixon,'tetra',pxwrapper,oTPixon,image=pimage
          if (cache) then savecache=1; either new or cache changed, so resave
      end
      ;stereo_tsetup,pxwrapper,timage,oTPixon

  end

  set_rr_commons,base,timage,rescale,N,plotedge,pxwrapper,$
    oCPixon,oTPixon,rtwl,reiser,cpixon,tpixon,plotsum,ptid

  if (savecache) then begin
      rr_cache,'save',N,timage,pxwrapper,oCPixon,oTPixon
  end

  if (demo) then begin
      for i=0,180,15 do begin
          for j=0,90,15 do begin
              for k=0,90,15 do begin

                  plot_rrt,base,i,j,k

              end
          end
      end

  end

;  print,'checking ',dragger, ' with ',draggerbase
  if (skipdragger eq 0) then begin
;      print,'activating ',draggerbase
      XMANAGER, "render_rot_gui", draggerbase, /NO_BLOCK, $
        EVENT_HANDLER="d_widgetsEvent"
      WIDS=1; allows 'Go' button to invoke rotation
  end

END
