; $Id: fans_changeviewdata.pro,v 1.20 2007/10/18 20:53:40 azuah Exp $
;###############################################################################
;
; NAME:
;   fans_changeviewdata
;
; PURPOSE:
;   Collection of event handlers and other support routines for the
;   FANS data reduction application defined in the file fans_datreduc.pro.
;
; CATEGORY:
;   FANS Data Reduction
;
; AUTHOR:
;   Richard Tumanjong Azuah
;   NIST Center for Neutron Research
;   100 Bureau Drive, Gaithersburg, MD 20899
;   United States
;   azuah@nist.gov; (301) 9755604
;   May, 2002
;
; LICENSE:
;  The software in this file is written by an employee of 
;  National Institute of Standards and Technology 
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used of if the code in this file is
;  included in another product.
;
;###############################################################################



;===============================================================================
; fans_maskDets
;
; PURPOSE:
;   Function removes masked detectors and returns the good ones in an
;   array
;
; PARAMETERS:
;   sPtr [in] - pointer to state structure
;
; RETURN VALUES:
;   Array of 'good' detectors to be used in subsequent data
;   reduction.
;
function fans_maskDets, sPtr

if ( (*sPtr).sampCount gt 0) then begin
    sampDataPtr = (*(*sPtr).sDataPtr)[(*sPtr).sampIndex]
    ndets = (*sampDataPtr).ndets
endif else ndets = 50
goodDets = indgen(ndets-2)+3    ; leave out monitors (dets 1 and 2)

widget_control, (*sPtr).mskOnID, get_uvalue=mskon
if (mskon and ptr_valid((*sPtr).mskListPtr)) then begin ; masked dets to be removed
    badDets = (*(*sPtr).mskListPtr)
    ;; remove bad dets from good dets
    nbad = n_elements(badDets)
    if (nbad ge ndets-2) then begin ; removing all detectors!!!
        widget_control, (*sPtr).oViewID, get_uvalue=on
        if (on) then erase      ; clear plot window
        return, -1
    endif
    for i = 0,nbad-1 do $
      goodDets = goodDets[where(goodDets ne badDets[i])]
endif

return, goodDets

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_Arcsin
;
; PURPOSE:
;   Function definition used to fit fast background data. It is
;   defined as follows:
;   f = p[0]+p[1]/x+p[2]/x^2+p[3]invsin[sqrt(20.45/x)/p[4]]
;   where
;   x=indep vars, p=params, f=dep var, pder=partial derivatives
;   p[4] is the monocromator d-spacing and should be constant
;   p[5] is a constant for converting between meV <--> wavenumber
;
; PARAMETERS:
;   xvalue [in] - array of independent values.
;
;   par [in] - array of function parameters.
;
; RETURN VALUES:
;   The function and partial derivative evaluated for the specified xvalue
;
function fans_Arcsin, xvalue,par
; evaluate function
xval = xvalue/par[5]
xsq = xval^2
invx = 1.0/xval
invxsq = invx^2
arg = (1.0/par[4])*sqrt(20.451*invx)
res = where( ABS(arg) gt 1.0 )
if (res[0] ne -1) then begin
    if ( arg[res[0]] lt 0.0 ) then begin  ; ie if -ve
        arg[res] = -1.0
    endif else begin
        arg[res] = 1.0
    endelse 
endif
arcsin = asin(arg)
; evaluate function
yval = par[0] + par[1]*invx + par[2]*invxsq + par[3]*arcsin

; evaluate partial derivatives wrt each parameter
omarcsin2 = 1 - arcsin^2
res = where(omarcsin2 le 0.0)
if (res[0] ne -1) then $
  omarcsin2[res] = 0.0001
pder = [  [replicate(1.0,n_elements(xval))], $
          [invx], $
          [invxsq],$
          [arcsin], $
          [-1.0*arcsin/par[4]/sqrt(omarcsin2)],$
          [replicate(1.0,n_elements(xval))] ]

return, [[yval],[pder]]

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_Cubic
;
; PURPOSE:
;   Function definition used to fit fast background data. It is
;   defined as follows:
;   f = p[0] + p[1]*x + p[2]*x^2 + p[3]x^3
;   where
;   x=indep vars, p=params, f=dep var, pder=partial derivatives wrt p
;
; PARAMETERS:
;   xval [in] - array of independent values.
;
;   par [in] - array of function parameters.
;
; RETURN VALUES:
;   The function and partial derivative evaluated for the specified xvalue
;
function fans_Cubic, xval,par
x2 = xval^2
x3 = xval^3
yval = par[0] + par[1]*xval + par[2]*x2 + par[3]*x3 

; evaluate partial derivatives wrt each parameter
pder = [  [replicate(1.0,n_elements(xval))], $
          [xval], $
          [x2], $ 
          [x3] ]

return, [[yval],[pder]]

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_Quad
;
; PURPOSE:
;   Function definition used to fit fast background data. It is
;   defined as follows:
;   f = p[0] + p[1]*x + p[2]*x^2
;   where
;   x=indep vars, p=params, f=dep var, pder=partial derivatives wrt p
;
; PARAMETERS:
;   xval [in] - array of independent values.
;
;   par [in] - array of function parameters.
;
; RETURN VALUES:
;   The function and partial derivative evaluated for the specified xvalue
;
function fans_Quad, xval,par
yval = par[0] + par[1]*xval + par[2]*xval^2

; evaluate partial derivatives wrt each parameter
pder = [  [replicate(1.0,n_elements(xval))], $
          [xval], $
          [xval^2] ]

return, [[yval],[pder]]

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_Linear
;
; PURPOSE:
;   Function definition used to fit fast background data. It is
;   defined as follows:
;   f = p[0] + p[1]*x
;   where
;   x=indep vars, p=params, f=dep var, pder=partial derivatives wrt p
;
; PARAMETERS:
;   xval [in] - array of independent values.
;
;   par [in] - array of function parameters.
;
; RETURN VALUES:
;   The function and partial derivative evaluated for the specified xvalue
;
function fans_Linear, xval,par
yval = par[0] + par[1]*xval

; evaluate partial derivatives wrt each parameter
pder = [  [replicate(1.0,n_elements(xval))], $
          [xval] ]

return, [[yval],[pder]]

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_fastPar
;
; PURPOSE:
;   Event handler. Fit fast background data by manually changing
;   function parameters.
;
; PARAMETERS:
;   event [in] - event structure to be handled.
;
pro fans_fastPar, event

widget_control, event.top, get_uvalue=sPtr

; retrieve parameters from the text widget
widget_control, event.id, get_value=parstr
if (n_elements(parstr) le 0) then return
fpar = [0.0,0.0,0.0,0.0,0.0,0.0]
tokens = strsplit(parstr[0],' ,',/extract) ; tokenize the string
for i=0,n_elements(tokens)-1 do begin ; convert each token into a float
    reads, tokens[i], af
    fpar[i] = af
endfor 
if ( (fpar[0] eq 0.0) and (fpar[1] eq 0.0) and (fpar[2] eq 0.0) ) then $ ; no fit parameters yet
  return

; generate curve from input poly parameters - first get energy array
index = (*sPtr).fastIndex
fdStructPtr = (*(*sPtr).fDataPtr)[index] ; get a ptr to the approp. fans data struct
omega = (*(*fdStructPtr).energyPtr) ; energy tranfer

; if detectors are to be masked, this has to be taken into account
; when fitting the fast bkgd data
reset = 0
widget_control, (*sPtr).mskOnID, get_uvalue=mskon
if (mskon) then begin 
    goodDets = -1
    goodDets = fans_maskDets(sPtr) ; get a list of good dets
    if ((ptr_valid((*sPtr).mskListPtr) eq 1) or goodDets[0] ne -1) then begin
        reset = 1               ; to make sure data reverts to orig cnts afterwards
        (*sPtr).updFast = 0     ; records that fast fit is in sync with mask dets
        nspec = (*fastDataPtr).nspec
        origTotCnts = (*(*fastDataPtr).totCntsPtr)   
        for i = 0,nspec-1 do begin  ; re-calculate total cnts using only good dets
            (*(*fastDataPtr).totCntsPtr)[i] = total((*(*fastDataPtr).detCntsPtr)[goodDets-1,i])
            (*(*fastDataPtr).totErrsPtr)[i] = sqrt(total(((*(*fastDataPtr).detErrsPtr)[goodDets-1,i])^2))
        endfor
    endif
endif

fans_plotData, sPtr, 'fast', err=err,emsg=emsg ; plot fast bkgd

; reset output calculation flag
(*sPtr).doOutpCalc = 1

; make 'sub FFit' widget active
widget_control, (*sPtr).subFastID, sensitive=1

; reset original total cnts if necessary
if (reset) then (*(*fastDataPtr).totCntsPtr) =  origTotCnts

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_fitFuncSel
;
; PURPOSE:
;   Event handler; Selecting a new fast bkgd fit function.
;
; PARAMETERS:
;   event [in] - event structure to be handled
;
pro fans_fitFuncSel, event
widget_control, event.top, get_uvalue=sPtr

(*sPtr).fitFunc = event.index
(*sPtr).fitPresent = 0          ; require refitting of fast bkgd
(*sPtr).doOutpCalc = 1          ; force re-evaluation of output data

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_Fastfit
;
; PURPOSE:
;   Event handler; Initiate a new fast bkgd fit.
;
; PARAMETERS:
;   event [in] - event structure to be handled
;
; KEYWORDS:
;   sPtr [in] - pointer to state structure
;
pro fans_Fastfit, event, sPtr=sPtr

; Basic error Handler
if (n_elements(!debug) && (!debug eq 0)) then begin
    catch, catchError
    if (catchError ne 0) then begin
        ;;print, 'Error handled!'
        eTitle = 'fans_fastfit: Error encountered'
        eMsg = 'An error or unusual condition was encountered!'
        eMsg = [eMsg,'Please, report the following to the DAVE team:']
        eMsg = [eMsg,!error_state.msg]
;    eMsg = [eMsg,'Function/module name: calledfunc()',!error_state.msg]
        void = dialog_message(/error,eMsg,title=eTitle,dialog_parent=event.top)
        catch, /cancel
        return
    endif
endif

if (n_elements(sPtr) eq 0) then $
  widget_control, event.top, get_uvalue=sPtr

index = (*sPtr).fastIndex
fastDataPtr = (*(*sPtr).fDataPtr)[index] ; get a ptr to fast data
omega = (*(*fastDataPtr).energyPtr) ; energy tranfers
dspace = (*fastDataPtr).monDSpace ; get the monochromator d-space value

; if detectors are to be masked, this has to be taken into account
; when fitting the fast bkgd data
reset = 0
widget_control, (*sPtr).mskOnID, get_uvalue=mskon
if (mskon) then begin 
    goodDets = -1
    goodDets = fans_maskDets(sPtr) ; get a list of good dets
    if ((ptr_valid((*sPtr).mskListPtr) eq 1) or goodDets[0] ne -1) then begin
        reset = 1               ; to make sure data reverts to orig cnts afterwards
        (*sPtr).updFast = 0     ; records that fast fit is in sync with mask dets
        nspec = (*fastDataPtr).nspec
        origTotCnts = (*(*fastDataPtr).totCntsPtr)
        origErrCnts = (*(*fastDataPtr).totErrsPtr)
        for i = 0,nspec-1 do begin ; re-calculate total cnts using only good dets
            (*(*fastDataPtr).totCntsPtr)[i] = total((*(*fastDataPtr).detCntsPtr)[goodDets-1,i])
            (*(*fastDataPtr).totErrsPtr)[i] = sqrt(total(((*(*fastDataPtr).detErrsPtr)[goodDets-1,i])^2))
        endfor
    endif
endif
totCnts = (*(*fastDataPtr).totCntsPtr)
totErrs = (*(*fastDataPtr).totErrsPtr)

case (*sPtr).fitFunc of
    0: begin
        funcName = 'fans_Arcsin'        
        par = [1.0,1.0,1.0,0.0,0.0,0.0]
        par[4] = dspace
        par[5] = ((*(*sPtr).mfoPtr)[3] eq 0)? 1.0 : (*sPtr).mev2wnos
        fixpar=[1,1,1,1,0,0]    ; <== fifth and sixth are fixed during fitting
    end
    1: begin
        par = [1.0,1.0]
        fixpar=[1,1]
        funcName = 'fans_Linear'        
    end
    2: begin
        par = [1.0,1.0,1.0]
        fixpar=[1,1,1]
        funcName = 'fans_Quad'        
    end
    3: begin
        par = [1.0,1.0,1.0,1.0]
        fixpar=[1,1,1,1]
        funcName = 'fans_Cubic'        
    end
endcase

; do a non-linear lsqfit using a Levenberg-Marquardt algorithm
yfit = lmfit(omega,totCnts,par,fita=fixpar,weights=1/(totErrs^2),function_name=funcName,/double)

; display fit results in fitted par widget
;mval = max(ABS(par))
;fstr = '(G12.4)'
;parstr = ''
;for i = 0,4 do begin
;    buf = strtrim(string(format=fstr,par[i]),2)
;    parstr = parstr+buf+' '
;endfor
;widget_control, (*sPtr).parID, set_value=strtrim(parstr,2)
(*(*sPtr).fitParsPtr) = par 
(*sPtr).fitPresent = 1
fans_plotData, sPtr, 'fast', err=err,emsg=emsg ; plot fast bkgd

; make 'sub FFit' widget active
widget_control, (*sPtr).subFastID, sensitive=1

; switch on output calculation flag
(*sPtr).doOutpCalc = 1

; reset original total cnts if necessary
if (reset) then begin
    (*(*fastDataPtr).totCntsPtr) = origTotCnts
    (*(*fastDataPtr).totErrsPtr) = origErrCnts
endif

end
;-------------------------------------------------------------------------------



;===============================================================================
; fans_changeViewData
;
; PURPOSE:
;   Event handler; Change type (sample, fast bkgd, samp bkgd) of data
;   to be displayed/plotted.
;
; PARAMETERS:
;   event [in] - event structure to be handled
;
pro fans_changeViewData, event

; Basic error Handler
if (n_elements(!debug) && (!debug eq 0)) then begin
    catch, catchError
    if (catchError ne 0) then begin
        ;;print, 'Error handled!'
        eTitle = 'fans_changeviewdata: Error encountered'
        eMsg = 'An error or unusual condition was encountered!'
        eMsg = [eMsg,'Please, report the following to the DAVE team:']
        eMsg = [eMsg,!error_state.msg]
;    eMsg = [eMsg,'Function/module name: calledfunc()',!error_state.msg]
        void = dialog_message(/error,eMsg,title=eTitle,dialog_parent=event.top)
        catch, /cancel
        return
    endif
endif

widget_control, event.top, get_uvalue=sPtr

dataType = widget_info(event.id,/uname)

; radio buttons fire an event when they are selected/deselected and
; both will result in a call to fans_changeDispType. When a button is
; deslected/selected, its uvalue has to be changed to 0/1 respectively.
switch event.select of
    0: begin                    ; (exclusive) radio button deselected
        switch dataType of

            'samp': begin
                widget_control, (*sPtr).sViewID, set_uvalue=0
;                widget_control, (*sPtr).plotWinID, sensitive=0
                break
            end

            'bkgd': begin
                widget_control, (*sPtr).bViewID, set_uvalue=0
;                widget_control, (*sPtr).plotWinID, sensitive=0
                break
            end

            'fast': begin
                ;; switch off some functionality
                widget_control, (*sPtr).fViewID, set_uvalue=0
                widget_control, (*sPtr).fitBkgdID, sensitive=0
                ;widget_control, (*sPtr).fitLabID, sensitive=0
                ;widget_control, (*sPtr).parID, sensitive=0
                break
            end

            'outp': begin
                widget_control, (*sPtr).oViewID, set_uvalue=0
                break
            end

        endswitch

        break
    end
    
    1: begin                    ; (exclusive) radio button selected
        switch dataType of
            
            'samp': begin
                
                widget_control, (*sPtr).sViewID, set_uvalue=1
;                widget_control, (*sPtr).plotWinID, sensitive=1
                widget_control, (*sPtr).eSliderID, sensitive=1
                ;; Update current data
                index = (*sPtr).sampIndex
                dataPtr = (*(*sPtr).sDataPtr)[index]
                
                ;; Update plot and slider using selected data
                updatePlotSlider, sPtr,dataType,dataPtr,err=err,emsg=emsg
                break
            end

            'bkgd': begin
                widget_control, (*sPtr).bViewID, set_uvalue=1
;                widget_control, (*sPtr).plotWinID, sensitive=1
                widget_control, (*sPtr).eSliderID, sensitive=1
                ;; Update current data
                index = (*sPtr).bkgdIndex
                dataPtr = (*(*sPtr).bDataPtr)[index]

                ;; Update plot and slider using selected data
                updatePlotSlider, sPtr,dataType,dataPtr,err=err,emsg=emsg

                break
            end

            'fast': begin
                ;; switch on some functionality specific to Fast Bkgd
                widget_control, (*sPtr).fViewID, set_uvalue=1
                widget_control, (*sPtr).fitBkgdID, sensitive=1
                ;widget_control, (*sPtr).fitLabID, sensitive=1
                ;widget_control, (*sPtr).parID, sensitive=1
                widget_control, (*sPtr).eSliderID, sensitive=0

                ;; update plot
                fans_plotData, sPtr, 'fast', err=err,emsg=emsg

                break
            end

            'outp': begin
                widget_control, (*sPtr).oViewID, set_uvalue=1
                widget_control, (*sPtr).eSliderID, sensitive=0
                ;; Calculate output data
                fans_outpCalc, sPtr, err=err, emsg=emsg
                if (err ne 0) then begin
                    printerror,err,emsg
                    return
                endif

                ;; update plot
                fans_plotData, sPtr, 'outp', err=err,emsg=emsg

                break
            end

        endswitch


        
        break
    end

endswitch                       ; switch event.select
    
end



