;+
; NAME:
;       XRASTER
;
; PURPOSE:
;
;       XRASTER is used to display 3-D spectroscopic data in the form
;       of a raster (i.e. intensity[lambda, slit pos, raster pos.].
;       One line is displayed as I[lambda, slit pos] with one display window
;       for each raster (using !p.multi). If more than one line, these
;       are added as extra rows of display windows.
;
;
; CATEGORY:
;       Hansteen/Wikstøl Data analysis SW
;
; CALLING SEQUENCE:
;       xraster, data_obj, hdr_obj, aux_obj, windows = windows, $
;                 group_leader = groupleader, ncolors=ncolors
;
; INPUTS:
;       data_obj: Data object. Must follow the structure of the superclass
;                 HW_DATA.
;       hdr_obj : Header object. Must follow the structure of the superclass
;             HW_HDR.
;       aux_obj : Auxiliary object. Must follow the structure of the
;            superclass HW_AUX.
;       windows : The index(es) of the line windows to be displayed
; KEYWORD PARAMETERS:
;       group_leader: Widget parent (if any).
;       ncolors: Number of colors for xraster. Default is
;                !d.n_colors<256.
;       filename: Name of selected data-file. Used in display window title
;
; OUTPUTS:
;       None
;
; CALLS:
;
;
; COMMON BLOCKS:
;
;
; PROCEDURE:
;       XRASTER defines the widgets and displays data. Display can be output
;       to ps-file or jpeg. To illustrate teh use of windows, consider an
;       observation consisting of intensity in 5 spectral lines with
;       50 wavelength pixels, 512 pixels along the slit and 300 raster
;       positions. The to display line numbers 2 and 3 (of line 0-4),
;       the call to xraster would be:
;       xraster, data_obj, hdr_obj,aux_obj, [2, 3], $
;                group_leader = group_leader, ncolors = ncolors
;       This would create a display with 2x300 windows of 50x512 pixels
;       (although the x-y size of the display windows are scaled)
;
; RESTRICTIONS:
;
;
; MODIFICATION HISTORY:
;          Jul 2002: Øivind Wikstøl
;       20-Apr-2004: Øivind Wikstøl - Added funtions to change wavl. scale 
;                                     [pix/Angstr.]
;       13-Nov-2006: Viggo Hansteen - Replaced call to tvimage with call to 
;                                     plot_image, this simplifies the logic 
;                                     quite a bit!
;       29-Sep-2007: A. Gardini     - Pointers freed by cleanup.
;       14-Feb-2008: A. Gardini     - Set the xvs maximum to 2^15-1.
;       22-Apr-2008: A. Gardini     - Set margin parameters in panel.
;
; $Id: xraster.pro,v 1.10 2008/04/22 08:31:53 gardini Exp $
;-
;
; save as postscript file
pro xraster_ps,event
  thisfile=dialog_pickfile(/write,file='idl.ps')
  if thisfile eq '' then return
  widget_control,event.top,get_uvalue=info
  thisdevice=!d.name
  set_plot,'ps',/copy
  device,file=thisfile,_extra=keywords,/inches,bits_per_pixel=8,/color
  pseudoevent={widget_button,id:0l, $
    top:event.top, handler:0l, select:1}
  widget_control,event.top,set_uvalue=info
  xraster_draw,pseudoevent
  device,/close_file
  set_plot,thisdevice
end

; save as jpeg file
pro xraster_jpeg,event
  thisfile=dialog_pickfile(/write,file='idl.jpg')
  if thisfile eq '' then return
  widget_control,event.top,get_uvalue=info
  wset,(*info).wid
  snapshot=tvrd()
  tvlct,r,g,b,/get
  s=size(snapshot)
  image24=bytarr(3,s[1],s[2])
  image24(0,*,*)=r(snapshot)
  image24(1,*,*)=g(snapshot)
  image24(2,*,*)=b(snapshot)
  write_jpeg,thisfile,image24,true=1,quality=75
end

;display image in the draw window:
pro xraster_draw, event
  widget_control, event.top, get_uvalue = info
  if !d.name ne 'PS' then  wset, (*info).wid
  nr=(*info).nexp
  nwin = (*info).nwin
  xsz = 0
  ysz = 0
  ; this loop is for determining max size of spectral line windows
  ; (since they can have varying size)

  for i = 0, nwin-1 do begin
    j=(*info).windows[i]
    *(*info).data-> getwin, j, wd, pos
    sz = size(wd)
    xsz = xsz > sz(1)
    ysz = ysz > sz(2)
  endfor
  ; determine size of each window. The scale factors
  ; (xpixels*2)x(ypixels/2), with a minimum of
  ; 50x100 and max of 100x250 pixels.
  xpix = (xsz*2) > 75 < 250
  ypix = (ysz/2) > 100 < 250
  nxticks = 3
  ; determine if draw window needs to be larger (by
  ; applying scroll bars.)
  xfac = fix((xpix*nr*2)/(*info).x_scroll_size)
  yfac = fix((ypix*nwin*1.2)/(*info).y_scroll_size)
  xvs = (*info).x_scroll_size*xfac  > (*info).x_scroll_size < 2L^15-1
  yvs = (*info).y_scroll_size*yfac  > (*info).y_scroll_size

  widget_control, (*info).drawid, draw_xsize = xvs, draw_ysize = yvs
;  !p.multi = [0, nr, (*info).nwin]
;  panel,0,0,nx=nr,ny=(*info).nwin,order=1

  ; set up plot scale:
  for i = 0, (*info).nwin-1 do begin
    j = (*info).windows[i]
    *(*info).data-> getwin, j, wd, pos
    wdmax=median(wd)*5
    sz = size(wd)
    lstart = pos[0]
    lstop = pos[0]+pos[1]-1
    ; wavelength scale detector B
    if lstart le 2147 then begin
      lambda = (*info).lambda_b[lstart:lstop]
    ; wavelength scale detector A
    endif else begin
      lambda = (*info).lambda_a[lstart-2148:lstop-2148]
    ; wavelength scale if outside of detector
    endelse
    xscale = interpol(lambda, xpix)
    yscale = interpol(indgen(sz(2)), ypix)
    origin=[min(xscale),min(yscale)]
    ; draw images
    !p.charsize=1.5
    for j = 0, nr-1 do begin
      drawimage = congrid(wd(*, *, j), xpix, ypix)
      sz=size(drawimage)
      scale=[(max(xscale)-min(xscale))/sz[1],(max(yscale)-min(yscale))/sz[2]]
      if j eq 0 then ytitle=(*info).ytitle else ytitle=''
      panel,j,i,nx=nr,ny=(*info).nwin,order=0,ydist=3,/xlabel,ytop=3,xright=5
      plot_image,drawimage,origin=origin,scale=scale,/nosquare , $
;            max=wdmax, $
           xtitle = (*info).xtitle, xticks = 4, $
           ytitle = ytitle 
    endfor
  endfor
  panel,/reset
  !p.multi = 0
  !p.charsize = 1.0
  ;create colorbar (if plot to 'PS' then skip colorbar:
  if !d.name ne 'PS' then begin
    widget_control, (*info).colorbarid, get_value = wid
    wset, wid
    ; first erase previous colorbar
    bar = intarr((*info).cb_xsz, (*info).d_ysz)*0
    tv, bar
    ; find max and min values of drawimage to produce color bar y-scale
    ymin = min(drawimage)
    ymax = max(drawimage)
    if ymax-ymin eq 0.0 then ymax=ymin+1
    format='(i6)'
    if ymax-ymin lt 10 then format='(f7.4)'
    hw_colorbar, position = [0.75, 0.15, 0.95, 0.9], range = [ymin, ymax], $
            /vertical , format=format, title=(*info).colorbar_title
  endif
end

; Popup window for line selection
pro xraster_pickline, event
  widget_control, event.top ,get_uvalue = info
  ; open window for line selection
  lineselect_widget = widget_base(title = 'Select line', $
                group_leader = (*info).tlb,/row)
  closefield = widget_base(lineselect_widget,/column)
  closebutton = widget_button(closefield, value = 'OK', $
                              event_pro = 'xraster_pickline_destroy')
  line_base = widget_base(lineselect_widget,/column,/frame)
  linelist = cw_bgroup(line_base, (*info).linelist, /return_index, $
                       /exclusive, event_func = 'xraster_pickline_pick')
  widget_control, lineselect_widget, set_uvalue = info
  widget_control, lineselect_widget, /realize
  xmanager, 'Select line', lineselect_widget, $
             /no_block, group_leader = (*info).tlb
end

; get the value of the selected line from the line list:
function xraster_pickline_pick, event
  widget_control, event.top, get_uvalue = info
  (*info).line = event.value+(*info).windows[0]
  return, 0
end

; close Line selection widget
pro xraster_pickline_destroy, event
  widget_control, event.top, get_uvalue = info
   if (*info).messenger eq (*info).animenu then begin
     xraster_anim, event
  endif
  widget_control, event.top,/destroy
end

; Controls the animation event: If only one line,
; start animation, if several line, then pop up
; line selection window first.
pro xraster_control_anim,event
  widget_control, event.top, get_uvalue = info
  if (*info).nwin lt 2 then begin
    xraster_anim, event
  endif else begin
    (*info).messenger = (*info).animenu
    xraster_pickline, event
  endelse
end

; create animation widget and launch animation
pro xraster_anim, event
  widget_control, event.top, get_uvalue = info
  *(*info).data-> getwin, (*info).line, wd, pos

  sz = size(wd)
  ndim = sz[0]
  xsize = sz[1]
  ysize = sz[2]

  if ndim lt 3 then begin
    ok = dialog_message('Data array must be 3-D to make animation!',/center)
    return
  endif

  ; bytscale data to save time in animation tool
  wdb = bytscl(wd)

  ; write data to assoc file if not already existing:
  filename = 'adata'+strtrim(string((*info).line), 2)+'.dat'
  afile_info = file_info(filename)
  if not afile_info.exists then begin
    afile = filename
    openw, lu, afile, /get_lun
    rec = assoc(lu, wdb)
    rec[0] = wdb
    close, lu & free_lun, lu
  endif

  ; start ximovie, with the delete keyword (afile is removed from disc
  ; when ximovie is closed
  ximovie, afile, xsize, ysize, group_leader = (*info).tlb, /fdelete
  return
end


pro xraster_wpix, event
  widget_control, event.top, get_uvalue = info
  ; change titles in aux object
  *(*info).aux-> setwscale,'pixels'
  *(*info).aux-> setxytitle,wscale='pixels'
  ; set titles for image plots
  (*info).xtitle = (*(*info).aux-> getxytitle())[(*info).xdim]
  (*info).ytitle = (*(*info).aux-> getxytitle())[(*info).ydim]
  ; set scale for images
  (*info).lambda_b = findgen(2148)
  (*info).lambda_a = findgen(2148) + 2148

  pseudoevent={widget_button,id:0L, $
               top:event.top, handler:0l, select:1}
  xraster_draw, pseudoevent
end

; change wavelength scale to Angstrom
pro xraster_wangstr, event
  widget_control, event.top, get_uvalue = info
   ; change titles in aux object
  *(*info).aux-> setwscale,string("305B);"
  *(*info).aux-> setxytitle,wscale=string("305B);"
  ; set titles for image plots
  (*info).xtitle = (*(*info).aux-> getxytitle())[(*info).xdim]
  (*info).ytitle = (*(*info).aux-> getxytitle())[(*info).ydim]

  lambda_struct = *(*info).data-> getlambda()
  (*info).lambda_a = lambda_struct.scale_a
  (*info).lambda_b = lambda_struct.scale_b 

  pseudoevent={widget_button,id:0L, $
               top:event.top, handler:0l, select:1}
  xraster_draw, pseudoevent
end

; select color table
pro xraster_colors, event
  widget_control, event.top, get_uvalue=info
  thisevent = tag_names(event, /structure_name)
  case thisevent of
  'WIDGET_BUTTON': begin
      xcolors, ncolors = (*info).ncolors, bottom = (*info).bottom, $
        title = 'xraster colors (' + strtrim((*info).wid, 2) + ')', $
        group_leader = event.top, notifyid = [event.id, event.top]
      endcase
  'XCOLORS_LOAD': begin
      (*info).r = event.r((*info).bottom:(*info).ncolors-1 + (*info).bottom)
      (*info).g = event.g((*info).bottom:(*info).ncolors-1 + (*info).bottom)
      (*info).b = event.b((*info).bottom:(*info).ncolors-1 + (*info).bottom)
      if !d.n_colors gt 256 then begin
        pseudoevent={widget_button,id:0l, $
          top:event.top, handler:0l, select:1}
       xraster_draw, pseudoevent
      endif
    endcase
  endcase
  widget_control, event.top, set_uvalue = info
end

; protect colors
pro xraster_protect_colors,event
  widget_control, event.top, get_uvalue = info
  tvlct, (*info).r, (*info).g, (*info).b, (*info).bottom
end

; resize main window
pro xraster_resize, event
  widget_control, event.top ,get_uvalue = info
  (*info).d_xsz = (event.x - (*info).lcol_xsz-(*info).cb_xsz) > 0
  (*info).d_ysz = event.y
  (*info).x_scroll_size = (*info).d_xsz
  (*info).y_scroll_size = (*info).d_ysz
  widget_control, (*info).drawid, draw_xsize = (*info).d_xsz, $
                   draw_ysize = (*info).d_ysz, xsize = (*info).d_xsz, $
                   ysize = (*info).d_ysz
  widget_control, (*info).colorbarid, draw_ysize = (*info).d_ysz

  pseudoevent={widget_button,id:0L, $
    top:event.top,handler:0l,select:1}
  xraster_draw, pseudoevent
end

; close xraster
pro xraster_destroy, event
  widget_control, event.top,/destroy
end

pro xraster_cleanup, tlb
  widget_control, tlb, get_uvalue = info
  ptr_free, (*info).xscale
  ptr_free, (*info).yscale
  ptr_free, (*info).data
  ptr_free, (*info).hdr
  ptr_free, (*info).aux
  ptr_free, info
end

pro xraster, data, hdr, aux, windows, group_leader = group, $
             ncolors = ncolors, filename = filename
@environment
;
  if n_params() lt 4 then begin
    message, $
      'xraster: A data object, header object, aux object and window'+ $
                ' must be given', /cont
    return
  endif

  if n_elements(ncolors) eq 0 then ncolors = (!d.n_colors < 256)
  if n_elements(filename) eq 0 then filename = ''
  ; drawing window size in relation to screen
  screensize=get_screen_size()
  sz=screensize*scfac
  d_xsz = sz[0]
  d_ysz = sz[1]
  nwin=n_elements(windows)       ; number of line windows selected by user
  line = windows[0]
  nraster = data->getnraster()   ; number of raster positions in data set
  nexp =  data->getnexp()        ; number of exposures
  xdim = 0   ; wavelength
  ydim = 1   ; slit pos
  xytitle = aux-> getxytitle()
  xtitle = xytitle[xdim]
  ytitle = xytitle[ydim]

  ; get wavelength scale for detectors B (170-215 Å)
  ; and A (250-295 Å)
  scale = aux->getwscale()
  if scale eq 'pixels' then begin
    lambda_b = findgen(2148)
    lambda_a = findgen(2148) + 2148
  endif else begin
    lambda = data-> getlambda()
    lambda_b = lambda.scale_b
    lambda_a = lambda.scale_a
  endelse

  ;create linelist
  linelist = strarr(nwin)
  for i = 0, nwin-1 do linelist[i] = 'Line ' + strtrim(string(windows[i]), 2)

;  initialize size of draw window :
;  window, /pixmap, /free, xsize = d_xsz, ysize = d_ysz
;  tv, bytscl(detector, top = ncolors)
;  pixid = !d.window

  ; base widget:
  xwt = 'XRaster -' +aux-> gettitle() + '   '   ; xraster window title
  tlb = widget_base(/row, title=xwt, tlb_size_events = 1, $
                    mbar=menubar, xoffset=100, yoffset=100)
  lcol = widget_base(tlb, /frame, /column)      ;left column.
  rcol = widget_base(tlb, /column)              ;right column.

  ; create pulldown menus on the base widget menubar
  filemenu=widget_button(menubar, value='File',/menu, uvalue='file')
  savemenu=widget_button(filemenu, value='Save as', uvalue='save', /menu)
  psmenu=widget_button(savemenu, value='Postscript', event_pro = 'xraster_ps')
  jpgmenu=widget_button(savemenu, value='JPG', event_pro = 'xraster_jpeg')
  exitmenu=widget_button(filemenu, value='Close', event_pro='xraster_destroy')
  optmenu=widget_button(menubar,value='Options', uvalue='options')
  colmenu=widget_button(optmenu, value='Colour table', $
                               event_pro='xraster_colors')
  animenu = widget_button(optmenu, value = 'Create Animation', uvalue='anim', $
                          event_pro = 'xraster_control_anim')
  wscalemenu=widget_button(optmenu, value='Change wavelength scale',/menu)
  angstr = string("305B)+'ngstr'+string("370B)+'m'
  pixmenu=widget_button(wscalemenu, value='Pixels',event_pro='xraster_wpix')
  angstrmenu=widget_button(wscalemenu, value=angstr,event_pro='xraster_wangstr')

  ;display window:
  displaybase = widget_base(rcol, /row)
  drawid=widget_draw(displaybase, retain = 2, $
                     xsize = d_xsz, x_scroll_size = d_xsz, $
                     ysize = d_ysz, y_scroll_size = d_ysz, $
                     /button_events, event_pro='xraster_draw')
  cb_xsz = 84  ; xsize of color bar draw widget
  ; create color bar to the right of display window:
  colorbarid = widget_draw(displaybase, retain = 2, xsize = cb_xsz, $
                           ysize = d_ysz)
  colorbar_title=data->gettitle()+' '+(data->getunit())[0]
  ; close button
  closefield = widget_base(lcol, /column)
  closebutton = widget_button(closefield, value = 'Close', $
                              event_pro = 'xraster_destroy')
  ; realize main window:

  widget_control, tlb, /realize, tlb_get_size = tlb_sz
  ; define size of widget and the menu column
  tlb_xsz = tlb_sz[0]  ; xsize of whole widget in pixels
  tlb_ysz = tlb_sz[1]  ; ysize of whole widget in pixels
  lcol_xsz = tlb_xsz - d_xsz - cb_xsz
  ; get window id of display window
  widget_control, drawid, get_value = wid
  wset, wid

  ;get and save color table
  tvlct, r, g, b, /get
  bottom = 0
  if (!d.n_colors le 256) then begin
    r = r[bottom:ncolors-1+bottom]
    g = g[bottom:ncolors-1+bottom]
    b = b[bottom:ncolors-1+bottom]
  endif

  ; define the info structure, used send information around
  info = {xscale:ptr_new(), $
          yscale:ptr_new(), $
          data:ptr_new(data), $
          hdr:ptr_new(hdr), $
          aux:ptr_new(aux), $
          xdim:xdim, $
          ydim:ydim, $
          nwin:nwin, $
          nraster:nraster, $
          nexp:nexp, $
          lambda_b:lambda_b, $
          lambda_a:lambda_a, $
          line:line, $
          windows:windows, $
          linelist:linelist, $
          lcol_xsz:lcol_xsz, $
          d_xsz:d_xsz, $
          d_ysz:d_ysz, $
          x_scroll_size:d_xsz, $
          y_scroll_size:d_ysz, $
          cb_xsz:cb_xsz, $
          tlb:tlb, $
          lcol:lcol, $
          rcol:rcol,  $
          animenu:animenu, $
          messenger:0, $
          r:r, g:g, b:b, $
          bottom:bottom, $
          ncolors:ncolors, $
          drawid:drawid, $
          colorbarid:colorbarid, $
          colorbar_title:colorbar_title,$
          xtitle:xtitle, $
          ytitle:ytitle, $
          xytitle:xytitle, $
          wid:wid}
  info = ptr_new(info, /no_copy)
  ; set user value of tlb widget to be the info ptr
  widget_control, tlb, set_uvalue = info
  ; create pseudoevent and send this event to xraster_draw,
  ; in order to draw the image

  pseudoevent={widget_button,id:0L, top:tlb, handler:0l, select:1}
;  widget_control, drawid, send_event = pesudoevent
;  widget_control,pseudoevent.top,set_uvalue=info
  xraster_draw,pseudoevent
  xmanager, 'Xraster', tlb, /no_block, event_handler = 'xraster_resize', $
            group_leader = group, cleanup = 'xraster_cleanup'
end
