;+
; NAME:
;       iris_spec_cal
;
; PURPOSE:
;       A routine that inteactively determines a geometrical and
;       wavelength solution to correct IRIS data using a widget
;       interface.  In the resulting image, the spectral lines and
;       spatial lines from the slit fiducial marks should be aligned
;       to the rows and columns of the image, and the wavelength
;       should run linearly to sub-pixel accuracy.
;
; CALLING SEQUENCE:
;       iris_spec_cal, image, wavelength='', [savefile='', restorefile='']
;
; INPUTS:
;       image       - The spectrum from which the lines will be fitted.
;       wavelength  - A string containing the IRIS wavelength ID for the
;                     spectrum, either 'NUV', 'FUV1', or 'FUV2'.
;
; OPTIONAL INPUTS:
;       savefile    - A string which is the desired name of the
;                     savefile, if this is not defined, a file is
;                     generated using the system date and time when the
;                     procedure is run.
;       restorefile - A string containing the file name of a previous
;                     saved result which will be restored upon startup
;                     and available for application.
;       mask        - A byte array the same size as the image
;                     containing the pixels that will be considered in
;                     the analysis
;
; KEYWORD PARAMETERS:
;       None 
;
; OUTPUTS:
;       None
;
; OPTIONAL OUTPUTS:
;       When the "Save" button is pushed, a save file is created with
;       the specified file name containing common block variables:
;              rough
;              lines
;              fiducials
;              geo_solution
;              wave_solution
;
; COMMON BLOCKS:
;       spec_environment
;       cal_environment
;       wave_environment
;       fit_environment
;
; PROCEDURES USED:
;       iris_linelist
;       iris_calc_geo
;       iris_calc_wave
;
;       Contained in this file:
;           waveid_plot
;           img_display
;           update_display
;
;           spec_widget_event
;           spec_widget_setup
;
;           wave_widget_event
;           wave_widget
;
;           cal_widget_event
;           cal_widget_setup
;
; COMMENTS:
;
; EXAMPLES:
;          IDL> iris_spec_cal, image, wavelength='NUV'
;
; MODIFICATION HISTORY:
;       Started 2013-June-3 by Sarah A. Jaeggli, Montana State University
;
;               2013-June-11 SAJ Eliminated funnction READ_MOUSE, this
;               is insread handled by flags provided to the
;               SPEC_WIDGET handler.
;
;               2013-July-26 SAJ added mask keyword
;
;               2014-Jun-13 shows averaged line spectrum during line ID
;
;               2014-Jun-25 SAJ altered command line blocking so
;               command line is blocked
;
;               2014-July-28 SAJ standardized FUV1, FUV2 labeling to
;               match image header standards
;-
;------------------------------------------------------------------------------

PRO waveid_plot, pixel, id, flag

  COMMON spec_environment, img, spec_widx, didx

  wset, didx.img
  plot, [0,img.x], [0,img.y], xstyle=5, ystyle=5, /noerase, /nodata, $
        xmargin=[0,0], ymargin=[0,0]

  red=255 + 256L * (150 + 256L * 150)
  blue=150 + 256L * (150 + 256L * 255)

  if img.log then begin
     linespec=alog10(rebin(img.i, img.x, 1))
     max=alog10(img.max)
     min=alog10(img.min)
  endif else begin
     linespec=rebin(img.i, img.x, 1)
     max=img.max
     min=img.min
  endelse

  oplot, (linespec-min)/(max-min)*img.y

  for i=0,n_elements(pixel)-1 do begin
     if flag[i] eq 'yes' then color=blue else color=red

     oplot, [pixel[i], pixel[i]], $
            [img.y/2., img.y/2.+img.y/50.], color=color
     xyouts, pixel[i], img.y/2.+img.y/25., string(i, format='(I2)')+' '+id[i], $
             color=color, orientation=90
  endfor

END

;------------------------------------------------------------------------------

PRO img_display

  COMMON spec_environment, img, spec_widx, didx

  WSET, didx.img

  if img.log then begin
     image=alog10(img.i)
     max=alog10(img.max)
     min=alog10(img.min)
  endif else begin
     image=img.i
     max=img.max
     min=img.min
  endelse

  TV, bytscl(congrid(image, img.x/img.scl, img.y/img.scl),min,max)

END

;------------------------------------------------------------------------------

PRO update_display, xcoord, ycoord

  COMMON spec_environment, img, spec_widx, didx

  ;update displayed coordinates
  WIDGET_CONTROL, spec_widx.xycoords, $
                  SET_VALUE=string(xcoord*img.scl, format='(I4)')+','+ $
                  string(ycoord*img.scl, format='(I4)')

  ;update intensity value
  WIDGET_CONTROL, spec_widx.intval, $
                  SET_VALUE=string(img.i[xcoord*img.scl,ycoord*img.scl], $
                                   format='(I5)')
  ;update horizontal plot
  WSET, didx.spec
  
  spec=img.i[*,ycoord*img.scl]

  plot, spec, xstyle=5, ystyle=5, yran=[img.min,img.max], ylog=img.log, $
        xmargin=[0,0], ymargin=[0,0]
  oplot, [xcoord,xcoord]*img.scl, [img.min,img.max], color=250

  ;update vertical plot
  WSET, didx.spat
  
  spec=img.i[xcoord*img.scl,*]

  plot, spec, indgen(img.y), xstyle=5, ystyle=5, $
        xmargin=[0,0], ymargin=[0,0], $
        xran=[img.min,img.max], xlog=img.log

  oplot, [img.min,img.max], [ycoord,ycoord]*img.scl, color=250

END

;------------------------------------------------------------------------------

PRO spec_widget_event, event

  COMMON spec_environment, img, spec_widx, didx
  COMMON cal_environment, cal_widx, mouseread
  COMMON wave_environment, wave_widx

  COMMON fit_environment, wave, savename, l0, dl, $
                          rough, lines, fiducials, geo_solution, $
                          wavelengths, pixels, wave_solution

  stash=widget_info(event.handler, /child)
  WIDGET_CONTROL, stash, get_uvalue=state

  widget_control, event.id, get_uvalue=action

  CASE action OF
     'LOG' : BEGIN
        ;set log flag
        img.log=1

        ;redisplay log-scaled image
        img_display
     END

     'LINEAR' : BEGIN
        ;set log flag
        img.log=0

        ;redisplay log-scaled image
        img_display
     END

     'INTSLIDER' : BEGIN
        img.max=event.value
        img_display
     END

     'GRID' : BEGIN
        wset, didx.img

        ;set up plot area with grid
        plot, [0,img.x], [0,img.y], xstyle=5, ystyle=5, /noerase, /nodata, $
              xmargin=[0,0], ymargin=[0,0]

        xgrid=0 & ygrid=0

        while xgrid lt img.x do begin
           oplot, [xgrid,xgrid], [0,img.y], linestyle=1
           xgrid+=100
        endwhile

        while ygrid lt img.y do begin
           oplot, [0,img.x], [ygrid,ygrid], linestyle=1
           ygrid+=100
        endwhile
     END

     'DISPLAY' : BEGIN
        ;get mouse coordinates
        xcoord=event.x
        ycoord=event.y

        ;update display
        update_display, xcoord, ycoord

        ;if the mouse read flag is set
        IF mouseread.flag EQ 1 THEN BEGIN

           ;and when a mouse button is pushed
           IF event.type EQ 0 THEN BEGIN

              ;if it is the left button
              if event.press eq 1 then begin
                 mouseread.x[mouseread.count]=event.x
                 mouseread.y[mouseread.count]=event.y

                 mouseread.count+=1

                 wset, didx.img

                 plot, [event.x*img.scl], [event.y*img.scl], $
                       psym=mouseread.psym, color=mouseread.color, $
                       xmargin=[0,0], ymargin=[0,0], xstyle=5, ystyle=5, $
                       xran=[0,img.x], yran=[0,img.y], /noerase
              endif

              ;if it is the right button
              if event.press eq 4 then begin

                 ;set mouseread flag to exit selection
                 mouseread.flag=0

                 ;clear message
                 message='Done'
                 WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

                 ;return xy points to proper variable
                 xytmp=([[mouseread.x[0:mouseread.count-1]], $
                        [mouseread.y[0:mouseread.count-1]]])*img.scl

                 case mouseread.name of 
                    'rough' : rough={xy:xytmp}
                    'lines' : lines={xy:xytmp}
                    'fiducials' : fiducials={xy:xytmp}

                    'wave' : begin
                       tmp=WIDGET_INFO(wave_widx.table, /TABLE_SELECT)
                       row=tmp[1] ;the offending row

                       ;update the table with the x pixel value
                       WIDGET_CONTROL, wave_widx.table, GET_VALUE=table_struct
                       table_struct[row].pixel_in=xytmp[0,0]
                 
                       ;recalculate the l0 and dl
                       good=where(table_struct.pixel_in ne 0)
                       num=n_elements(good)
                 
                       WIDGET_CONTROL, wave_widx.l0field, GET_VALUE=l0
                       WIDGET_CONTROL, wave_widx.dlfield, GET_VALUE=dl

                       l0=double(l0) & dl=double(dl)

                       ;if just one point, shift L0, otherwise do a linear fit
                       if num eq 1 then begin
                          l0+=(table_struct[row].pixel_out - $
                               table_struct[row].pixel_in)*dl
                       endif else begin
                          tmp=linfit(table_struct[good].pixel_in, $
                                     table_struct[good].wavelength)
                          l0=tmp[0]
                          dl=tmp[1]
                       endelse
                 
                       WIDGET_CONTROL, wave_widx.l0field, $
                                       SET_VALUE=string(l0, FORMAT='(F8.3)')
                       WIDGET_CONTROL, wave_widx.dlfield, $
                                       SET_VALUE=string(dl, FORMAT='(F7.5)')
                 
                       ;update calculated line positions
                       for i=0,n_elements(table_struct)-1 do $
                          table_struct[i].pixel_out=(table_struct[i].wavelength-l0)/dl

                       ;refresh table
                       WIDGET_CONTROL, wave_widx.table, SET_VALUE=table_struct

                       ;redisplay image and line ID plot
                       img_display
                       waveid_plot, table_struct.pixel_out, table_struct.id, $
                                    table_struct.fit

                       ;clear message
                       message=''
                       WIDGET_CONTROL, cal_widx.info, SET_VALUE=message
                    end

                 endcase

              endif

           ENDIF

        ENDIF

     END

  ENDCASE

END 

;------------------------------------------------------------------------------
 
PRO spec_widget_setup

  COMMON spec_environment, img, spec_widx, didx

  basewidget = WIDGET_BASE(TITLE='IRIS Spectral Display', /COLUMN)

  ;set up information block
  info_block = WIDGET_BASE(basewidget, /ROW)

  ;display image coordinates
  XYlabel = WIDGET_LABEL(info_block, VALUE='X,Y')
  XYcoords = WIDGET_TEXT(info_block, uvalue='XYCOORDS', $
                   VALUE=string(0,format='(I4)') + ',' + $
                         string(0,format='(I4)'), $
                   XSIZE=9, YSIZE=1)

  ;display pixel value
  intlabel = WIDGET_LABEL(info_block, VALUE='I')
  intval = WIDGET_TEXT(info_block, uvalue='INT', $
                       VALUE=string(0,format='(I5)'), $
                       xsize=5, ysize=1)

  ;log/linear scaling option
  button = WIDGET_BUTTON(info_block, VALUE='log', UVALUE='LOG')
  button = WIDGET_BUTTON(info_block, VALUE='linear', UVALUE='LINEAR')

  ;intensity slider
  sliderlabel = WIDGET_LABEL(info_block, VALUE='I Scale')
  intslider = WIDGET_SLIDER(info_block, /drag, UVALUE='INTSLIDER', xsize=400, $
                            VALUE=img.max, maximum=img.max, minimum=img.min)

  ;apply grid button
  button = WIDGET_BUTTON(info_block, VALUE='Apply Grid', UVALUE='GRID')

  ;image display
  img_block = WIDGET_BASE(basewidget, /ROW)
  img_draw = WIDGET_DRAW(img_block, uvalue='DISPLAY', $
                         XSIZE=img.x/img.scl, YSIZE=img.y/img.scl, $
                         /MOTION_EVENT, /BUTTON_EVENTS, RETAIN=2)
  ;vertical plot
  spat_draw = WIDGET_DRAW(img_block, UVALUE='SPATIAL', $
                         XSIZE=75, YSIZE=img.y/img.scl)

  ;horizontal plot
  spec_block = WIDGET_BASE(basewidget, /ROW)
  spec_draw = WIDGET_DRAW(spec_block, UVALUE='SPECTRUM', $
                          XSIZE=img.x/img.scl, YSIZE=75)

  spec_widx={base:basewidget, $
             xycoords:xycoords, $
             intval:intval, $
             img:img_draw, $
             spec:spec_draw, $
             spat:spat_draw}

END

;------------------------------------------------------------------------------

PRO wave_widget_event, event

  COMMON cal_environment, cal_widx, mouseread
  COMMON wave_environment, wave_widx

  stash=widget_info(event.handler, /child)
  WIDGET_CONTROL, stash, get_uvalue=state

  widget_control, event.id, get_uvalue=action

  CASE action OF
     'TABLE' : BEGIN
        ;identify the kind of table event
        table_event=tag_names(event, /STRUCTURE_NAME)

        if table_event eq 'WIDGET_TABLE_CELL_SEL' then begin
              
           if event.sel_left le 4 and $
              event.sel_left ne -1 and $
              event.sel_left eq event.sel_right and $
              event.sel_top eq event.sel_bottom then begin

              tmp=WIDGET_INFO(wave_widx.table, /TABLE_SELECT)
              row=tmp[1]        ;the offending row

              WIDGET_CONTROL, wave_widx.table, GET_VALUE=table_struct
              nlines=n_elements(table_struct)

              ;reset table colors to original
              WIDGET_CONTROL, wave_widx.table, $
                              USE_TABLE_SELECT=[0,0,4,nlines-1], $
                              BACKGROUND_COLOR=[[223,223,223], $
                                                [223,223,223], $
                                                [223,223,223], $
                                                [223,223,223], $
                                                [223,223,223]]
              ;highlight row
              WIDGET_CONTROL, wave_widx.table, $
                              USE_TABLE_SELECT=[0,row,4,row], $
                              BACKGROUND_COLOR=[[0,223,0], $
                                                [0,223,0], $
                                                [0,223,0], $
                                                [0,223,0], $
                                                [0,223,0]]

              message='Do line selection in the display window.  Only the first click is considered.  Left click selects, right click exits'
              WIDGET_CONTROL, cal_widx.info, SET_VALUE=message

              ;pass flags to read mouse clicks from the window to the display
              ;widget handler
              mouseread={flag:1, name:'wave', psym:1, color:250, $
                         x:intarr(100), y:intarr(100), count:0}

           endif

           ;change the fit flag for the wavelength cal
           if event.sel_left eq 6 and $
              event.sel_left ne -1 and $
              event.sel_left eq event.sel_right and $
              event.sel_top eq event.sel_bottom then begin

              tmp=WIDGET_INFO(wave_widx.table, /TABLE_SELECT)
              row=tmp[1]        ;the offending row

              WIDGET_CONTROL, wave_widx.table, GET_VALUE=table_struct

              if table_struct[row].fit eq 'yes' then begin
                 table_struct[row].fit='no'
                 bgcolor=[255,150,150]
              endif else begin
                 table_struct[row].fit='yes'
                 bgcolor=[150,150,255]
              endelse

              WIDGET_CONTROL, wave_widx.table, SET_VALUE=table_struct
              WIDGET_CONTROL, wave_widx.table, /USE_TABLE_SELECT, $
                              BACKGROUND_COLOR=[bgcolor]
              waveid_plot, table_struct.pixel_out, table_struct.id, $
                           table_struct.fit

           endif
        endif

     END

  ENDCASE

END

;------------------------------------------------------------------------------

PRO wave_widget

  COMMON spec_environment, img, spec_widx, didx
  COMMON cal_environment, cal_widx, mouseread
  COMMON wave_environment, wave_widx

  COMMON fit_environment, wave, savename, l0, dl, $
                          rough, lines, fiducials, geo_solution, $
                          wavelengths, pixels, wave_solution

  linelist=iris_linelist(wave)
  nlines=n_elements(linelist)

  base = widget_base(title='IRIS Wavelength Table', /column)

  ;set up some menu buttons
  menu_block = WIDGET_BASE(base,/ROW)

  label = WIDGET_LABEL(menu_block, VALUE='L0')
  l0_field = WIDGET_TEXT(menu_block, xsize=9, UVALUE='L0FIELD', $
                         VALUE=string(l0, FORMAT='(F9.4)'))

  label = WIDGET_LABEL(menu_block, VALUE='dL')
  dl_field = WIDGET_TEXT(menu_block, xsize=8, UVALUE='DLFIELD', $
                         VALUE=string(dl, FORMAT='(F8.6)'))

  ;set up table parameters
  alignment = rebin([1, 2, 1, 2, 2, 2, 1], 7, nlines)
  editable  = rebin([0, 0, 0, 0, 0, 1, 0], 7, nlines)
  tmp=['(I2)','(F8.3)','','(F7.2)','(I4)','(I4)',''] & format=tmp
  for i=1,nlines-1 do format=[[format],[tmp]]

  column_widths=[20,60,50,55,50,50,50]

  wht=[223,223,223]
  gry=[175,175,175]
  red=[255,150,150]
  blu=[150,150,255]

  for i=0,nlines-1 do begin
     if linelist[i].flag eq 1 then clr=blu else clr=red
     if i eq 0 then background_color=[[wht],[wht],[wht],[wht], $
                                      [wht],[gry],[clr]] $
     else background_color=[[background_color],[wht],[wht],[wht],$
                            [wht],[wht],[gry],[clr]]
  endfor

  column_labels=['', 'Wavelength', 'ID', 'calc', 'guess', 'width', 'fit']

  ;set up table structure which contains all the data
  pixel_out=intarr(nlines)
  pixel_in =intarr(nlines)

  table_struct={linen:0, wavelength:0.0D, id:'', pixel_out:0, $
                pixel_in:0, width:0, fit:'yes'}
  table_struct=replicate(table_struct, nlines)

  table_struct[*].linen=indgen(nlines)
  table_struct.wavelength=linelist.wl
  table_struct.id=linelist.id
  table_struct[*].pixel_out=(linelist.wl-l0)/dl
  table_struct[*].pixel_in =0
  table_struct[*].width=round(linelist.dw/dl)
  table_struct[where(linelist.flag eq 1)].fit='yes'
  table_struct[where(linelist.flag eq 0)].fit='no'

  table=widget_table(base, /ALL_EVENTS, VALUE=table_struct, $
                     /ROW_MAJOR, XSIZE=7, YSIZE=nlines, UVALUE='TABLE', $
                     column_labels=column_labels, /NO_ROW_HEADERS, $
                     format=format, alignment=alignment, editable=editable, $
                     background_color=background_color, /FRAME, $
                     column_widths=column_widths)

  wave_widx={l0field:l0_field, dlfield:dl_field, table:table}

  WIDGET_CONTROL, base, /realize
  XMANAGER, 'wave_widget', base, GROUP_LEADER=cal_widx.base, $
            EVENT_HANDLER='wave_widget_event'

  ;overplot lines and labels on the display image
  waveid_plot, table_struct.pixel_out, table_struct.id, table_struct.fit

END

;------------------------------------------------------------------------------

PRO cal_widget_event, event

  COMMON spec_environment, img, spec_widx, didx
  COMMON cal_environment, cal_widx, mouseread
  COMMON wave_environment, wave_widx

  COMMON fit_environment, wave, savename, l0, dl, $
                          rough, lines, fiducials, geo_solution, $
                          wavelengths, pixels, wave_solution

  stash=widget_info(event.handler, /child)
  WIDGET_CONTROL, stash, get_uvalue=state

  widget_control, event.id, get_uvalue=action

  CASE action OF
     'QUIT' : BEGIN
        WIDGET_CONTROL, cal_widx.base, /DESTROY

        ;reset color and plot options to original
        !p.font=didx.font
        device, decomposed=didx.color
        RETURN
     END

     'ROUGH' : BEGIN
        message='Select points along a single line to get a rough estimate for the spectral line geometry.  Left click selects, right click exits.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        ;pass flags to read mouse clicks from the window to the display
        ;widget handler
        mouseread={flag:1, name:'rough', psym:1, color:250, $
                   x:intarr(100), y:intarr(100), count:0}

     END

     'LINES' : BEGIN
        message='Select spectral lines for fitting.  Left click selects, right click exits.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        ;pass flags to read mouse clicks from the window to the display
        ;widget handler
        mouseread={flag:1, name:'lines', psym:1, color:1.25e7, $
                   x:intarr(100), y:intarr(100), count:0}

     END

     'FIDUCIALS' : BEGIN
        message='Select fiducial lines for fitting.  Left click selects, right click exits.'
        WIDGET_CONTROL, cal_widx.info, SET_VALUE=message

        ;pass flags to read mouse clicks from the window to the display
        ;widget handler
        mouseread={flag:1, name:'fiducials', psym:1, color:1.25e5, $
                   x:intarr(100), y:intarr(100), count:0}

     END

     'CALCGEO' : BEGIN
         ;pass all the line information to an external program which returns an
         ;array with the polywarp coefficients

        geo_solution=iris_calc_geo(img.i, rough.xy, lines.xy, fiducials.xy, $
                                   wave=wave, mask=img.mask)
     END


     'APPLYGEO' : BEGIN
        message='Geometry calibration has been applied.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        img.i=interpolate(img.i0, geo_solution.dmapx, geo_solution.dmapy, $
                          cubic=-0.5)

        img_display
     END

     'ID' : BEGIN
        message='Select lines from the list to identify them on the display image.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        wave_widget

     END

     'CALCWAVE' : BEGIN
        ;if the widget is open mine the data from the table
        IF XREGISTERED('wave_widget', /NOSHOW) NE 0 THEN BEGIN
           WIDGET_CONTROL, wave_widx.table, GET_VALUE=table_struct

           ;consider fit flags
           good=where(table_struct.fit eq 'yes')
           wavelengths=table_struct[good].wavelength
           pixels=table_struct[good].pixel_out
           widths=table_struct[good].width
        ENDIF

        karray=[[[geo_solution.kx]]  , [[geo_solution.ky]], $
                [[geo_solution.kxi]] , [[geo_solution.kyi]]]

        ;pass image and table info to external program for more 
        ;accurate calculation of the wavelength solution
        wave_solution=iris_calc_wave(img.i, wavelengths, pixels, widths, $
                                     karray, wave=wave)

     END


     'APPLYWAVE' : BEGIN
        message='Geometry+Wavelength calibration has been applied.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

;        img.i=poly_2d(img.i0, wave_solution.kx, wave_solution.ky, $
;                      2, cubic=-0.5)

        img.i=interpolate(img.i0, wave_solution.dmapx, wave_solution.dmapy, $
                          cubic=-0.5)

        img_display
     END

     'REVERT' : BEGIN
        message='Image has been reset to the original.'
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        img.i=img.i0
        img_display
     END

     'SAVE' : BEGIN
        ;save any geometry results to a file
        message='Results have been saved to '+savename
        WIDGET_CONTROL, cal_widx.info , SET_VALUE=message

        save, rough, lines, fiducials, geo_solution, $
              wavelengths, pixels, wave_solution, $
              filename=savename
     END

  ENDCASE

END

;------------------------------------------------------------------------------

PRO cal_widget_setup

  COMMON cal_environment, cal_widx, mouseread

  basewidget = WIDGET_BASE(TITLE='IRIS Spectral Calibration', /ROW)
  
  ;calibration buttons
  cal_block = WIDGET_BASE(basewidget, /COLUMN)

  ;geometry calibration
  geo_block = WIDGET_BASE(cal_block,/COLUMN,/FRAME)
  label = WIDGET_LABEL(geo_block,VALUE='Geometry Calibration') 
  button = WIDGET_BUTTON(geo_block,VALUE='Get Rough Spectral Geometry', $
                         UVALUE='ROUGH')
  button = WIDGET_BUTTON(geo_block,VALUE='Find Lines',UVALUE='LINES')
  button = WIDGET_BUTTON(geo_block,VALUE='Find Fiducials',UVALUE='FIDUCIALS')
  button = WIDGET_BUTTON(geo_block, VALUE='Calculate Geometry', $
                         UVALUE='CALCGEO')
  button = WIDGET_BUTTON(geo_block, VALUE='Apply Geometry Correction', $
                         UVALUE='APPLYGEO')

  ;wavelength calibration
  wav_block = WIDGET_BASE(cal_block,/COLUMN,/FRAME)
  label = WIDGET_LABEL(wav_block, VALUE='Wavelength Calibration') 
  button = WIDGET_BUTTON(wav_block, VALUE='ID Lines', UVALUE='ID')
  button = WIDGET_BUTTON(wav_block, VALUE='Calculate Wavelength', $
                         UVALUE='CALCWAVE')
  button = WIDGET_BUTTON(wav_block, VALUE='Apply Wavelength Correction', $
                         UVALUE='APPLYWAVE')

  ;set up utility and informational block
  utility_block = WIDGET_BASE(basewidget, /COLUMN)

  ;instructional/informational text display
  info_widget = WIDGET_TEXT(utility_block, VALUE='', UVALUE='INFO', /WRAP, $
                            xsize=25, ysize=13)

  ;utility buttons
  button = WIDGET_BUTTON(utility_block, VALUE='Revert', UVALUE='REVERT')
  button = WIDGET_BUTTON(utility_block, VALUE='Save', UVALUE='SAVE')
  button = WIDGET_BUTTON(utility_block, VALUE='Quit', UVALUE='QUIT')

  cal_widx={base:basewidget, $
            info:info_widget}

END

;------------------------------------------------------------------------------

PRO iris_spec_cal, image, savefile=savefile, restorefile=restorefile, $
                   wavelength=wavelength

  COMMON spec_environment, img, spec_widx, didx
  COMMON cal_environment, cal_widx, mouseread
  COMMON fit_environment, wave, savename, l0, dl, $
                          rough, lines, fiducials, geo_solution, $
                          wavelengths, pixels, wave_solution

  ;do anything wavelength-specific here
  if keyword_set(wavelength) ne 1 then begin
     print, 'Specify a wavelength, e.g. wavelength="NUV","FUV1","FUV2"'
     RETURN
  endif
  wave=wavelength

  ;if a save file is not specified generate a unique file name
  ;using the current date and time
  if keyword_set(savefile) then savename=savefile $
  else begin
     caldat, systime(/julian, /UTC), d3, d2, d1, t1, t2, t3
     datetime=string(d1, d2, d3, t1, t2, t3, $
                     format='(I4,I2.2,I2.2,".",I2.2,I2.2,I2.2)')
     savename='iris_spec_cal.'+wave+'.'+datetime+'.sav'
  endelse

  ;restore previous fit results if provided
  if keyword_set(restorefile) then begin
     print, 'Restoring results in '+restorefile
     restore, restorefile
  endif

  ;determine image dimensions
  nx=(size(image))[1]
  ny=(size(image))[2]

  ;setup default pixel masks
  case wavelength of
     'FUV1' : begin
        mask_x0=0   & mask_x1=2042
        mask_y0=22  & mask_y1=1073
        
        l0 = 1331.60D           ;starting wavelength in Ang
        dl = 0.01298D           ;dispersion in Ang/pixel
     end

     'FUV2' : begin
        mask_x0=600 & mask_x1=2034
        mask_y0=22  & mask_y1=1073

        l0 = 1380.58D           ;starting wavelength in Ang
        dl = 0.01272D           ;dispersion in Ang/pixel
     end

     'NUV'  : begin
        mask_x0=34  & mask_x1=2045
        mask_y0=20  & mask_y1=1075

        l0 = 2781.8365D         ;starting wavelength in Ang
        dl = 0.02546D           ;dispersion in Ang/pixel
     end
  endcase

  mask=replicate(0B, nx, ny)
  mask[mask_x0:mask_x1,mask_y0:mask_y1]=1B


  ;determine image rescale factor for user display
  ss=get_screen_size()
  if nx gt 0.9*ss[0] or ny gt 0.9*ss[1] $
     then scl=max(ceil([nx/float(ss[0]), ny/float(ss[1])])) $
     else scl=1

  ;determine display range
  imax=max(image[where(mask eq 1)], min=imin)
  imin=0.01*imax>imin

  ;set up the image structure
  img={i:image, $               ;image buffer (will be modified)
       i0:image, $              ;original image (will not change)
       mask:mask, $
       x:nx, y:ny, $            ;full image dimensions
       min:imin, max:imax, $    ;image min and max for display scaling
       scl:scl, $               ;scaling factor for screen size
       log:0, $                 ;log display flag
       geo:0, wav:0}            ;calibraion flags

  ;set flags for display widget handler
  mouseread={flag:0, name:'', psym:0, color:0, $
             x:intarr(100), y:intarr(100), count:0}

  ;store color and plot options that will be overridden
  font=!p.font
  device, get_decomposed=decomp

  ;override color and plot options for widget displays
  !p.font=-1
  device, decomposed=1
  device, retain=2

  ;set up widget interfaces
  spec_widget_setup
  cal_widget_setup

  WIDGET_CONTROL, spec_widx.base, /REALIZE 
  WIDGET_CONTROL, cal_widx.base, /REALIZE

  ;get window indices for image and plot displays
  WIDGET_CONTROL, GET_VALUE=disp_img, spec_widx.img
  WIDGET_CONTROL, GET_VALUE=disp_spec, spec_widx.spec
  WIDGET_CONTROL, GET_VALUE=disp_spat, spec_widx.spat

  didx={img:disp_img, spec:disp_spec, spat:disp_spat, $
        font:font, color:decomp}

  img_display

  XMANAGER, 'iris_spec_cal', cal_widx.base, $
            EVENT_HANDLER='cal_widget_event', /NO_BLOCK

  XMANAGER, 'iris_spec_cal', spec_widx.base, GROUP_LEADER=cal_widx.base, $
            EVENT_HANDLER='spec_widget_event'


END 

