;+
; NAME:
;     WINDMETER
; PURPOSE:
;     A widget that displays a wind meter with a vector that indicates
;     wind speed and direction, with option to plot a time history of
;     the wind.
; CATEGORY:
;     OVRO APC UTILITY
; CALLING SEQUENCE:
;     id     = windmeter(/INIT,[GROUP=group][,LABEL=label]$
;                         [,BUTTONTEXT=buttontext])
;     status = windmeter(id,speed,dir)
;     status = windmeter(id,/DESTROY)
; INPUTS:
;     id        the widget ID returned by a previous, initializing
;                  call to WINDMETER (that used the /INIT switch).
;                  This input is ignored if INIT keyword is set.
;     speed     the new wind speed value to set the widget to [mph].
;                  This input is ignored if INIT keyword is set.
;     dir       the new wind direction to set the widget to [degrees].
;                  This input is ignored if INIT keyword is set.
; OPTIONAL (KEYWORD) INPUT PARAMETERS:
;     init      a keyword switch that governs which of two calling
;                  sequences is being used.  If set, a new widget is
;                  created.  If not set, an existing widget is to be
;                  updated.
;     destroy   a keyword switch that destroys the widget created by
;                  a previous call to WINDMETER (that used the /INIT switch)
;     group     the top base id of the calling program, so that the widget
;                  will be destroyed if the calling program exits.
;                  This input is ignored if INIT keyword is not set.
;     label     an optional title for the wind meter window.  If
;                  omitted, the title "Latest Wind Info" is used.
;                  This input is ignored if INIT keyword is not set.
;     buttontext
;               an optional text for a button, such as a cancel button
;                  to close the wind meter window.
;                  If omitted, no button is present.  If set and the user
;                  clicks on the button an event is generated.  To detect
;                  the event, call WIDGET_EVENT as discussed below.
;                  This input is ignored if INIT keyword is not set.
; ROUTINES CALLED:
; OUTPUTS:
;     id        the widget id of the compound widget (only when INIT
;                  keyword is set).
;     status    the status of the cancel button (only when INIT keyword
;                  is not set).  If the cancel button has been pressed,
;                  status='Cancel' and if not an empty string ('') is returned.
;                  If an error in calling sequence occurs (ID or VALUE
;                  is not supplied) then status='Cancel' also.  NB: If
;                  the widget is initialize without a button, status
;                  will always be an empty string ('')
; COMMENTS:
;     To use the routine, call it with the /INIT switch to create the
;     widget, then call it repeatedly without the /INIT switch to update
;     it.  When done with the widget, it should be destroyed.
;
; SIDE EFFECTS:
; RESTRICTIONS:
;     The wind meter widget must be explicitly destroyed.
; MODIFICATION HISTORY:
;     Written 31-Oct-1999 by Dale E. Gary
;       Uses structure from the PROGMETER routine.
;   Make sure that the original graphics window is restored.
;-

;-----------------------------------------------------------------
; This is a helper procedure called by WINDMETER when setting the
; value of the widget.  It actually draws the grid lines and
; wind vector.

pro windmeter_setvalue, id, value

   speed = value[0]

   ; Make sure dir is only between 0 and 360
   dir = value[1]<360>0

   time = value[2]

   ; Get state variables so that draw window id is known
   stash = WIDGET_INFO(id,/CHILD)
   WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

   ; Set the draw window as the current window, erase it and draw
   ; the bar using normalized coordinates
   wset,state.win
   erase

   ; Set the max scale to the next higher factor of 5 mph
   maxscale = ((fix(1.*speed/5)+1)*5)>20
   ; Plot the "target" polar grid
   x = cos(findgen(361)*!dtor)
   y = sin(findgen(361)*!dtor)
   for igrid = 5,maxscale,5 do begin
      linesty = 0
      if ((igrid mod 10) eq 5) then linesty = 1
      amp = (50*igrid/maxscale)
      plots,/dev,amp*x+50,amp*y+50,linesty=linesty
   endfor
   ; plot the "crosshair" axes
   plots,/dev,[0,100],[50,50]
   plots,/dev,[50,50],[0,100]

   ; Plot the wind vector
   plots,/dev,50+[0,(speed*50/maxscale)*sin(dir*!dtor)],50+[0,(speed*50/maxscale)*cos(dir*!dtor)],thick=3

   ; Add speed and direction to the arrays of saved values and increment NPTS
   if (state.npts eq 1000) then begin
      state.speed = shift(state.speed,-1)
      state.dir   = shift(state.dir,  -1)
      state.time  = shift(state.time, -1)
      state.npts = 999
   endif
   state.speed[state.npts] = speed
   state.dir[state.npts] = dir
   state.time[state.npts] = time
   state.npts = state.npts+1

   ; Resave state variables
   WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY
end

;-----------------------------------------------------------------
; This is a helper function called by WINDMETER when getting the
; widget ID of the button widget.

function windmeter_getvalue, id

   stash = WIDGET_INFO(id,/CHILD)
   WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

   ; Set the return value to the current state of the button
   ret = state.buttonid
   WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY

return,ret
end

pro windmeter_event, Event

   base = Event.handler
   stash = WIDGET_INFO(base, /CHILD)
   WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

   window,/free,xsiz=400,ysiz=300
   psav = !p
   !p.multi=[0,1,2,0,0]
   caldat,systime(/jul),mo,da,yr
   dstr = string(yr,mo,da,format='(I4.4,"/",i2.2,"/",i2.2)')
   utplot, state.time[0:state.npts-1], state.speed[0:state.npts-1], dstr, yran=[0,50]
   utplot, state.time[0:state.npts-1], state.dir[0:state.npts-1], yran = [0,360],psym=3
   !p=psav

   WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY

return
end

;-----------------------------------------------------------------
; This is the main routine.  It consists of three separate routines,
; actually, separated by if-then clauses, one routine for each of
; the three calling sequences.  If the /INIT switch is set, the
; widget is created.  If neither /INIT nor /DESTROY switches are
; set, the value of the widget is updated and the button state is
; checked.  If the /DESTROY switch is set, the widget is destroyed.
;

function windmeter,id,speed,dir,msec,INIT=init,DESTROY=destroy,GROUP=group,$
                   LABEL=label,BUTTONTEXT=buttontext,color=color

;
;  Save the current plotting window.
;
   dwindow = !d.window

   ; Section of code to be run if /INIT keyword is *not* set

   if (not keyword_set(init)) then begin

      ; First verify that an ID was given

      if (n_elements(id) eq 0) then begin
         msg = ['No widget ID specified in call to WINDMETER.',$
                'Must first call WINDMETER with /INIT to obtain ID.']
         xack,msg
         ;ans = widget_message(msg,/error)
         ret = 'Cancel'
         goto, exit_point
      endif

      ; ID was given, so see if /DESTROY keyword is set.  If so, destroy
      ; the widget and return

      if (keyword_set(destroy)) then begin
         xkill,id
         ret = 'Cancel'
         goto, exit_point
      endif

      ; Neither /INIT nor /DESTROY were set, so set the value of
      ; the widget and check the status of the button (if any),
      ; then return.

      if (n_elements(speed) eq 0 or n_elements(dir) eq 0) then begin
         msg = ['Wind speed and/or direction values not specified in call to WINDMETER.',$
                'Must supply speed and direction.  See documentation.']
         xack,msg
         ;ans = widget_message(msg,/error)
         ret = 'Cancel'
         goto, exit_point
      endif

      ; Set value and get button ID

      xshow,id
      WIDGET_CONTROL,id,SET_VALUE=[speed,dir,msec/1000.]
      WIDGET_CONTROL,id,GET_VALUE=buttonid
      ret = ''

      ; If the button ID is not zero, the button exists, so check state
      ; and return 'Cancel' if it has been pressed.

      if (buttonid ne 0L) then begin
         quit = WIDGET_EVENT(buttonid,/nowait)
         if (quit.id eq buttonid) then ret = 'Cancel'
      endif
      goto, exit_point
   endif

   ; If /INIT keyword was not set, the program returns to caller before
   ; reaching this point.  This is the cection of code to be run if /INIT
   ; keyword *is* set.

   ; Set some defaults if not given in calling sequence

   if (not keyword_set(label)) then label = 'Latest Wind Info'
   if (not keyword_set(group)) then group = 0

;   if (group eq 0L) then begin
;      junkbase =
;   endif else begin
;      junkbase = group
;   endelse

   ; Set the base widget, specifying my own routines to be run
   ; if WIDGET_CONTROL,GET_VALUE or SET_VALUE are called.
   scrsize = get_screen_size()
   base = WIDGET_BASE(group,/COLUMN, TITLE=label,GROUP = group, $
   					  XOFFSET=scrsize[0]-320,YOFFSET=10,  $
                      FUNC_GET_VALUE='windmeter_getvalue', $
                      PRO_SET_VALUE ='windmeter_setvalue', $
                      EVENT_PRO = 'windmeter_event')

   ; Set up the draw widget and optionally the button widget

   draw = WIDGET_DRAW(base,xsize=100,ysize=100)
   if (keyword_set(buttontext)) then begin

      ; The widget button is in its own base (bbase) so that it
      ; can be smaller width than full width of main base.  Its
      ; width will be automatically adjusted to the length of the
      ; text string.

      bbase = WIDGET_BASE(base,/ROW,/ALIGN_CENTER,ysize=25)
      button = WIDGET_BUTTON(bbase,value=buttontext,UVALUE='BUTTON')
   endif else button = 0L      ; This signifies that button is undefined

   ; Realize (draw) them
   xrealize, base,group=group,/center

   ; Get the window id of the draw widget
   WIDGET_CONTROL,draw,GET_VALUE=win

   ; Save some info that will be needed in the other functions
   state = {win:win, buttonid:button, $
            speed:fltarr(1000), dir:fltarr(1000), time:fltarr(1000), npts:0}
   WIDGET_CONTROL, WIDGET_INFO(base, /CHILD), SET_UVALUE=state, /NO_COPY

   ; The widget is created and drawn to the screen, so return the base
   ; ID so that the widget can be accessed by later calls to update it
   ; or destroy it.

   ret = base

;
;  Reset the plotting window to the original value and return.
;
exit_point:
   if (dwindow ne !d.window) and (dwindow ge 0) then begin
    device, window_state=window_state
    if n_elements(window_state) gt dwindow then     $
        if window_state(dwindow) then wset, dwindow
   endif
   return, ret
   end

