;+
; Project     : SOHO - CDS     
;                   
; Name        : XCR_REMOVE
;               
; Purpose     : Manual cosmic ray remover
;               
; Explanation : This program allows the user to flag cosmic ray pixels in a
;               dataset as MISSING, or to "unflag" pixels falsely identified
;               as cosmic rays by an automatic routine.
;
;               The user may browse through the data on exposure at a time,
;               using the previous/next exposure buttons.
;
;               The display consists of the original input data (top/left),
;               and the "working copy/output data" (bottom/right). It is
;               possible to supply a "suggestion" data set for the working
;               copy. this is usually done when using automatic cosmic ray
;               algorithms.
;
;               When the widget is in "MOVE" mode, clicking the middle mouse
;               button moves the "focus point", clicking the right mouse
;               button zooms in (centering on the focus point), clicking the
;               left mouse button zooms out.
;
;               Zooming in (or clicking the "Mode:MOVE" button) switches to
;               the "FLIP" mode, where clicking on an image pixel flips the
;               status of that pixel (MISSING/original value) in the working
;               copy data set. Zooming out (or clicking the "Mode:FLIP"
;               button) switches to the MOVE mode.
;
;               The default color scaling is designed to control the color of
;               MISSING pixels. The user may choose the hightlighting color to
;               be used by pressing the "HiLit:GREEN" button to select either
;               RED, GREEN, BLUE or BLACK.
;               
; Use         : XCR_REMOVE,IN_DATA,OUT_DATA,MISSING
;    
; Inputs      : IN_DATA, OUT_DATA
;
;               The input data is assumed to be 3 or 4-dimensional data with
;               detector exposures in the "plane" spanned by dimensions 1 and
;               3, i.e.:
;
;                  exposure_image = reform(data_in(*,xix,*,tix))
;
;               where xix and tix determines which exposure is looked at.
;
;               The output data variable may contain a "suggestion" data set
;               with some pixels already flagged as missing.
;
;               MISSING : A scalar that determines the value used to represent
;                         bad pixels.
;
; Opt. Inputs : None.
;               
; Outputs     : OUT_DATA : The value of the working copy data set when the
;                          user exits from the application.
;               
; Opt. Outputs: None.
;               
; Keywords    : EXPOSURE_IX : The index of the exposure to be displayed
;                             initially. exposure_ix = xix + n_xix * tix
;
; Calls       : cw_loadct(), cw_mouse(), cw_pzoom(), default, since_version()
;               trim(), xrealize, xtvscale()
;
; Common      : None.
;               
; Restrictions: None.
;               
; Side effects: Alters colortable
;               
; Category    : ?
;               
; Prev. Hist. : None.
;
; Written     : S. V. H. Haugan, UiO, 6 January 1997
;               
; Modified    : Version 2, William Thompson, GSFC, 8 April 1998
;			Changed !D.N_COLORS to !D.TABLE_SIZE for 24-bit displays
;
; Version     : Version 2, 8 April 1998
;-            


PRO xcr_remove_help,group=group
  
  txt = $
     ['This program allows the user to flag cosmic ray pixels in a',$
      'dataset as MISSING, or to "unflag" pixels falsely identified',$
      'as cosmic rays by an automatic routine.',$
      '',$
      'The user may browse through the data on exposure at a time,',$
      'using the previous/next exposure buttons.',$
      '',$
      'The display consists of the original input data (top/left),',$
      'and the "working copy/output data" (bottom/right). It is',$
      'possible to supply a "suggestion" data set for the working',$
      'copy. this is usually done when using automatic cosmic ray',$
      'algorithms.',$
      '',$
      'When the widget is in "MOVE" mode, clicking the middle mouse',$
      'button moves the "focus point", clicking the right mouse button',$
      'zooms in (centering on the focus point), clicking the left',$
      'mouse button zooms out.',$
      '',$
      'Zooming in (or clicking the "Mode:MOVE" button) switches to',$
      'the "FLIP" mode, where clicking on an image pixel flips the',$
      'status of that pixel (MISSING/original value) in the working',$
      'copy data set. Zooming out (or clicking the "Mode:FLIP"',$
      'button) switches to the MOVE mode.',$
      '',$
      'The default color scaling is designed to control the color of',$
      'MISSING pixels. The user may choose the hightlighting color to',$
      'be used by pressing the "HiLit:GREEN" button to select either',$
      'RED, GREEN, BLUE or BLACK.']
  
  xtext,txt,/just_reg,group=group

END


;
; Switch operation mode, FLIPPING <-> MOVING
;
PRO xcr_remove_switchmode,info,mode,replot
  default,replot,1
  IF mode EQ "MOVE" THEN BEGIN
     widget_control,info.cwmouse,set_value=["","ZOOMP",""]
     widget_control,info.modeswitch,set_value="MOVE_MODE"
     widget_control,info.pzoom_new,set_value={cross:0,replot_cross:replot}
     widget_control,info.pzoom_org,set_value={cross:10,replot_cross:replot}
     device,/cursor_crosshair
  END ELSE IF mode EQ "FLIP" THEN BEGIN
     ;; 31/32/35/39/40/45/55/56/57/58/59/68/129 (55
     device,cursor_standard=56
     widget_control,info.cwmouse,set_value=["","FLIP",""]
     widget_control,info.modeswitch,set_value="FLIP_MODE"
     widget_control,info.pzoom_new,set_value={cross:0,replot:replot}
     widget_control,info.pzoom_org,set_value={cross:10,replot:replot}
  END
END

;
; Switch to new exposure_ix - save the old, and get the new.
;
PRO xcr_remove_changexp,info,newexp
  on_error,0
  
  handle_value,info.data_new_h,data_new,/no_copy
  handle_value,info.data_org_h,data_org,/no_copy
  handle_value,info.exp_new_h,exp_new,/no_copy
  
  sz = size(data_new)
  
  ;;
  ;; Save current data
  ;;
  IF n_elements(exp_new) GT 0 THEN BEGIN
     xix = info.exposure_ix MOD sz(2)
     tix = info.exposure_ix / sz(2)
     
     data_new(*,xix,*,tix) = exp_new
  END
  
  IF newexp NE -1 THEN BEGIN
     xix = newexp MOD sz(2)
     tix = newexp / sz(2)
     
     exp_new = reform(data_new(*,xix,*,tix))
     exp_org = reform(data_org(*,xix,*,tix))
  
     xcr_remove_switchmode,info,"MOVE",0
     
     widget_control,info.pzoom_org,set_value={zoom:1}
     widget_control,info.pzoom_new,set_value={zoom:1}
     widget_control,info.pzoom_org,set_value=exp_org
     widget_control,info.pzoom_new,set_value=exp_new
     
     widget_control,info.exp_label,set_value=trim(newexp)
     info.exposure_ix = newexp
     info.last_xi = -1
     info.last_yi = -1
     widget_control,info.undo_last_b,sensitive=0
  END
  
  handle_value,info.exp_org_h,exp_org,/set,/no_copy
  handle_value,info.data_org_h,data_org,/set,/no_copy
  handle_value,info.exp_new_h,exp_new,/set,/no_copy
  handle_value,info.data_new_h,data_new,/set,/no_copy
END

PRO xcr_remove_flip_pix,info,xi,yi
  handle_value,info.exp_org_h,exp_org,/no_copy
  handle_value,info.exp_new_h,exp_new,/no_copy
  IF info.last_xi EQ -1 THEN widget_control,info.undo_last_b,sensitive=1
  info.last_xi = xi
  info.last_yi = yi
  IF exp_new(xi,yi) EQ info.missing THEN exp_new(xi,yi) = exp_org(xi,yi) $
  ELSE                                   exp_new(xi,yi) = info.missing
  widget_control,info.pzoom_new,set_value=exp_new
  handle_value,info.exp_org_h,exp_org,/set,/no_copy
  handle_value,info.exp_new_h,exp_new,/set,/no_copy
END


PRO xcr_remove_usehilit,red=red,green=green,blue=blue,black=black
  tvlct,r,g,b,/get
  hc = !d.table_size-1
  rgb = [[r],[g],[b]]
  IF keyword_set(red) THEN rgb(hc,*) = [255b,0b,0b]
  IF keyword_set(green) THEN rgb(hc,*) = [0b,255b,0b]
  IF keyword_set(blue) THEN rgb(hc,*) = [0b,0b,255b]
  IF keyword_set(black) THEN rgb(hc,*) = [0b,0b,0b]
  
  tvlct,rgb(*,0),rgb(*,1),rgb(*,2)
END


PRO xcr_remove_event,ev
  widget_control,ev.top,get_uvalue=info,/no_copy
  
  widget_control,ev.id,get_uvalue=uval
  
  IF uval EQ 'PZOOM' THEN BEGIN
     IF ev.xtvscale NE 0 THEN uval = "XTVSCALE" $
     ELSE uval = cw_tmouse(info.cwmouse,ev.event)
  END
  
  
  CASE uval OF 
     
  'HELP':BEGIN
     xcr_remove_help,group=ev.top
     ENDCASE
     
  'XTVSCALE':BEGIN
     ;; Since both cw_pzooms return one event each for every rescaling, we
     ;; handle them individually here, instead of handling both twice
     widget_control,ev.id,set_value=ev.set
     ENDCASE
     
  'EXIT':BEGIN
     xcr_remove_changexp,info,-1 ;; Make sure last changes stored
     ;; Doing this creates a window if none are available     
     device,/cursor_crosshair    
  
     widget_control,ev.top,/destroy
     return
     ENDCASE
     
  'ZOOM+':BEGIN
     xcr_remove_switchmode,info,"FLIP",0
     widget_control,info.pzoom_org,set_value=ev.set
     widget_control,info.pzoom_new,set_value=ev.set
     ENDCASE
     
  'ZOOM-':BEGIN
     xcr_remove_switchmode,info,"MOVE"
     widget_control,info.pzoom_org,set_value=ev.set
     widget_control,info.pzoom_new,set_value=ev.set
     ENDCASE
     
  'ZOOMP':BEGIN
     widget_control,info.pzoom_org,set_value=ev.set
     widget_control,info.pzoom_new,set_value=ev.set
     ENDCASE
     
  'NEXT':BEGIN
     newexp = (info.exposure_ix + 1) MOD info.nexp
     xcr_remove_changexp,info,newexp
     ENDCASE
     
  'PREV':BEGIN
     IF info.exposure_ix EQ 0 THEN newexp = info.nexp-1 $
     ELSE newexp = info.exposure_ix-1
     xcr_remove_changexp,info,newexp
     ENDCASE
     
  'TVSCALE':BEGIN
     dummy = xtvscale(info.tvscaler,/show,/map,iconify=0)
     ENDCASE
     
  'RED':BEGIN
     xcr_remove_usehilit,/red
     ENDCASE
     
  'BLACK':BEGIN
     xcr_remove_usehilit,/black
     ENDCASE
     
  'GREEN':BEGIN
     xcr_remove_usehilit,/green
     ENDCASE
     
  'BLUE':BEGIN
     xcr_remove_usehilit,/blue
     ENDCASE
     
  'FLIP_MODE':BEGIN
     xcr_remove_switchmode,info,"FLIP"
     ENDCASE
     
  'MOVE_MODE':BEGIN
     xcr_remove_switchmode,info,"MOVE"
     ENDCASE
     
  'UNDO':BEGIN
     IF info.last_xi NE -1 THEN $
        xcr_remove_flip_pix,info,info.last_xi,info.last_yi
     ENDCASE
     
     
  'FLIP':BEGIN
     xcr_remove_flip_pix,info,ev.set.xfocus,ev.set.yfocus
     ENDCASE
  END
  
  widget_control,ev.top,set_uvalue=info,/no_copy
END



PRO xcr_remove,data_in,data_out,missing,exposure_ix=exposure_ix,group=group
  
  on_error,2
  IF !debug NE 0 THEN on_error,0
  
  
  IF n_params() LT 3 THEN $
     message,"Use: XCR_REMOVE,DATA_IN,DATA_OUT,MISSING"
  
  sz = size(data_in)
  
  IF sz(0) LT 2 THEN $
     message,"DATA_IN must be at least 2-dimensional "
  
  IF sz(0) EQ 2 THEN data_in = reform(data_in,sz(1),1,sz(2),/overwrite)
  
  default,data_out,data_in
  default,exposure_ix,0
  
  nsz = size(data_in)
  
  osz = size(data_out)
  IF total(osz NE nsz) NE 0.0 THEN BEGIN
     print,"Output variable must be undefined or same size as DATA_IN"
     print,"Ignoring current value of output variable"
     data_out = data_in
  END 
     
  
  IF nsz(0) EQ 4 THEN nexp = nsz(2)*nsz(4) ELSE nexp = nsz(2)
  
  ;; Make tight bases/buttons
  sml = 0
  IF since_version('4.0') THEN sml = 1
  tight = {xpad:sml,ypad:sml,space:sml}
  bheight = 25
  lheight = 16
  
  ;; MAIN base
  
  base = widget_base(/column,title='XCR_REMOVE')
  
  IF nsz(1) GT nsz(3) THEN BEGIN
     first = widget_base(base,/row,_extra=tight)
     third = widget_base(base,/column,_extra=tight)
     
     ;; Organization left->right
     cmouse_base = widget_base(first)
     menu_base = widget_base(first)
     color_base = widget_base(first)
  END ELSE BEGIN
     first = widget_base(base,/column,_extra=tight)
     third = widget_base(base,/row,_extra=tight)
     
     ;; Organization top->bottom
     menu_base = widget_base(first)
     cmouse_base = widget_base(first)
     color_base = widget_base(base)
  END
  
  ;; CW_MOUSE box.
  
  dummy = {CW_MOUSE_S, btext:'', mtext:'', action:'', avail:0,flags:0}
  mouse_men = $
     [{CW_MOUSE_S,'Zoom out','Zoom out','ZOOM-',1,0},$
      {CW_MOUSE_S,'Move','Move around','ZOOMP',2,0},$
      {CW_MOUSE_S,'FLIP','Flip pixel','FLIP',2,0},$
      {CW_MOUSE_S,'Zoom in','Zoom in','ZOOM+',4,0}]
  cwmouse_org = cw_mouse(cmouse_base,mouse_men,title='Mouse buttons')
  widget_control,cwmouse_org,set_value=["ZOOM-","ZOOMP","ZOOM+"]
  widget_control,cwmouse_org,set_value={disable:["ZOOMP","FLIP"]}
  
  ;; Menu section
  
  menubox = widget_base(menu_base,/column,_extra=tight,/frame)
  
  first_menu = widget_base(menubox,/row,_extra=tight)
  secnd_menu = widget_base(menubox,/row,_extra=tight)
  
  ex_menu = widget_button(first_menu,value='Exit',uvalue='EXIT')
  
  help_b = widget_button(first_menu,value='Help',uvalue='HELP')
  
  next_b = widget_button(first_menu,value='<- Prev. exp.',uvalue='PREV')
  prev_b = widget_button(first_menu,value='Next exp. ->',uvalue='NEXT')
  
  color_top = !d.table_size-2
  color_miss = !d.table_size-1
  tvscaler = xtvscale(missing=missing,map=0,group=base,sigrange=0,$
                      logarithmic=1,top=color_top,color_miss=color_miss)
  
  tvscale_b = widget_button(first_menu,value='Adjust color scaling',$
                            uvalue='TVSCALE')
  
  ;; MODE switch
  
  modes = ["MOVE","FLIP"]
  modeswitch = cw_flipswitch(secnd_menu,value='Mode: '+modes,$
                             uvalue=modes+"_MODE")
  
  ;; UNDO button
  
  undo_last_b = widget_button(secnd_menu,value='Undo!',uvalue='UNDO')
  IF since_version('4.0') THEN widget_control,undo_last_b,scr_ysize=bheight
  
  ;; Highlight color switch
  
  colors = ["GREEN","RED","BLACK","BLUE"]
  colorswitch = cw_flipswitch(secnd_menu,value='HiLit:'+colors,$
                              uvalue=colors)
  xcr_remove_usehilit,/green
  
  ;; Exposure_ix label
  
  dummy1 = widget_label(secnd_menu,value=' Exposure_ix:')
  exp_label = widget_label(secnd_menu,value=trim(exposure_ix))
  dummy2 = widget_label(secnd_menu,value='/'+trim(nexp-1))
  
  IF since_version('4.0.1') THEN BEGIN
     widget_control,exp_label,scr_ysize=lheight,/dynamic_resize
     widget_control,dummy1,scr_ysize=lheight,/dynamic_resize
     widget_control,dummy2,scr_ysize=lheight,/dynamic_resize
  END
  
  IF nsz(1) GT nsz(3) THEN BEGIN
     pzoom_base = third         ;widget_base(third,/column,_extra=tight)
     
     xshrink = 1024./nsz(1)
     yshrink = 512./nsz(3)
     shrinkf = 1./min([xshrink,yshrink,1])
     
     xsize = fix(nsz(1)/shrinkf)
     ysize = fix(nsz(3)/shrinkf)
  END ELSE BEGIN
     pzoom_base = third
     
     xshrink = 500./nsz(1)
     yshrink = 1000./nsz(3)
     shrinkf = 1./min([xshrink,yshrink,2])
     xsize = fix(nsz(1)/shrinkf)
     ysize = fix(nsz(3)/shrinkf)
     
     xsize = xsize > 200
  END
  
  pzoom_org = cw_pzoom(pzoom_base,uvalue='PZOOM',$
                       xwsize=xsize,ywsize=ysize,xdsize=xsize,ydsize=ysize,$
                       origo = [0,0],xstyle=4,ystyle=4,$
                       xtvscale=tvscaler,cross_color=color_top)
  pzoom_new = cw_pzoom(pzoom_base,uvalue='PZOOM',$
                       xwsize=xsize,ywsize=ysize,xdsize=xsize,ydsize=ysize,$
                       origo = [0,0],$
                       xtvscale=tvscaler,cross_color=color_top)
  widget_control,pzoom_org,set_value={cross:5}
  widget_control,pzoom_new,set_value={cross:0}
  
  ;; CW_LOADCT
  
  dummy = cw_loadct(color_base,/frame)
  
  data_new_h = handle_create()
  data_org_h = handle_create()
  exp_new_h = handle_create()
  exp_org_h = handle_create()
  
  info = {nexp : nexp,$
          missing : missing,$
          exposure_ix:exposure_ix,$
          exp_label:exp_label,$
          last_xi : -1,$
          last_yi : -1,$
          undo_last_b : undo_last_b,$
          modeswitch:modeswitch,$
          cwmouse:cwmouse_org,$
          tvscaler:tvscaler,$
          data_new_h:data_new_h,$
          data_org_h:data_org_h,$
          exp_new_h:exp_new_h,$
          exp_org_h:exp_org_h,$
          pzoom_org:pzoom_org,$
          pzoom_new:pzoom_new}
  
  xrealize,base,/center,group=group
  
  catch,error
  IF error NE 0 THEN BEGIN
     print,"Error:"+!err_string
     message,"Caught error, retrieving data",/continue
     catch,/cancel
     IF n_elements(data_in) EQ 0 THEN $
        handle_value,data_org_h,data_in,/no_copy ;; Get back original data
     IF n_elements(data_out) EQ 0 THEN $
        handle_value,data_new_h,data_out,/no_copy
     return
  END
  
  handle_value,data_org_h,data_in,/set,/no_copy
  
  IF n_elements(data_out) EQ 0 THEN BEGIN
     handle_value,data_org_h,data_in,/no_copy
     message,"Parameter 1 and 2 must not refer to the same variable!",/continue
     return
  END
  
  handle_value,data_new_h,data_out,/set,/no_copy
  
  xcr_remove_changexp,info,info.exposure_ix
  
  widget_control,base,set_uvalue=info,/no_copy
  
  xmanager,'xcr_remove',base,/modal
  
  ;; Get data blocks back
  
  handle_value,data_org_h,data_in,/no_copy
  handle_value,data_new_h,data_out,/no_copy
  
  catch,/cancel
  
  ;; Free handles
  handle_free,data_new_h
  handle_free,data_org_h
  handle_free,exp_new_h
  handle_free,exp_org_h
  
  CASE sz(0) OF 
     2: data_out = reform(data_out,sz(1),sz(2),/overwrite)
     3: data_out = reform(data_out,sz(1),sz(2),sz(3),/overwrite)
     4: data_out = reform(data_out,sz(1),sz(2),sz(3),sz(4),/overwrite)
  END
  
END








