;+ 
;NAME:
; spd_ui_calendar.pro
;
;PURPOSE:
; Calendar selection widget for the GUI
;
;CALLING SEQUENCE:
; spd_ui_calendar,title, otime, gui_id, startyear
;
;INPUT:
; title: Title of the calendar window
; otime: Time object
; gui_id: Widget ID of the widget that called the calendar
; startyear: Start year in 'Year' list
; 
; 
;HISTORY:
;
; $LastChangedBy: $
; $LastChangedDate: $
; $LastChangedRevision: $
; $URL: $
;-


PRO spd_ui_calendar_update, wbase, state

    compile_opt idl2, hidden
  
  widget_control, state.month, get_value=month
  widget_control, state.year, get_value=year
  
  ; Find offset for first day of the month
  offset = (julday(month, 1, year)+1) mod 7
  
  ; Deal with leap years
  if ( julday(1, 1, year+1) - julday(1, 1, year) ) gt 365 then $
    state.numdays[1] = 29  else state.numdays[1] = 28
  
  
  ; Add/remove extra buttons for when month overflows
  if (offset + state.numdays[month-1]) gt 35 then begin
    if ~widget_info(state.temp, /valid_ID) then begin
      state.temp = widget_base(state.calBase, /row, xpad=0, ypad=0, space=0,frame=0)
      bID = widget_button(state.temp, value='', /no_release, xsize=40, ysize=35, $
                                          uvalue='DAY', uname='35')
      bID = widget_button(state.temp, value='', /no_release, xsize=40, ysize=35, $
                                          uvalue='DAY', uname='36')
    endif
    j=36
  endif else begin
    j=34
    if widget_info(state.temp, /valid_ID) then widget_control, state.temp, /destroy
  endelse
  
  
  ; Number the calendar buttons using the starting offset from above
  i=1
  for k=0, j do begin
    id = widget_info(wBase, find_by_uname=strtrim(k,2))
    if (k lt offset) or (k ge (offset + state.numdays[month-1])) then begin
      widget_control, id, set_value='', sensitive=0 
    endif else begin
      widget_control, id, set_value=strtrim(i,2), sensitive=1
      i++
    endelse
  endfor

  return

END ;---------------------------------------



PRO spd_ui_calendar_event, event
    
    compile_opt idl2, hidden
  
  catch, _err
  if _err then begin
    catch, /cancel
    ok = error_message('An error has occured and the calender must shut down.', /noname)
    widget_control, event.top, /destroy
  endif
    
;The user value of the top-level base widget is an anonymous structure that holds the widget IDs of the month, day,
; and year label widgets as well as stored values for hours, minutes, and seconds.
  WIDGET_CONTROL, event.TOP, GET_UVALUE = state, /no_copy
  WIDGET_CONTROL, event.ID, GET_UVALUE = uval
   
  IF Size(uval, /Type) NE 0 THEN BEGIN
    CASE uval OF
      'OK':begin
           ;Pull month/day/year values from display widgets
             widget_control, state.year, get_value = year
             widget_control, state.month, get_value = month
             widget_control, state.day, get_value = day
             
           ;Create time double from separate time elements and apply to time object,
           ;added fractional seconds, 2019-03-21, jmm
             If(state.fsecond Eq 0.0) Then Begin
                datetime = time_double(strtrim(year,2)+'-'+strtrim(month,2)+'-'+strtrim(day,2) $
                                       +'/'+strtrim(state.hour,2)+':'+strtrim(state.minute,2)+':'+strtrim(state.second,2))
                state.otime->setproperty, tdouble=datetime
             Endif Else Begin
                allsec = strtrim(state.second+state.fsecond,2)
                datetime_str = strtrim(year,2)+'-'+strtrim(month,2)+'-'+strtrim(day,2) $
                               +'/'+strtrim(state.hour,2)+':'+strtrim(state.minute,2)+':'+allsec
                datetime = time_double(datetime_str)
                state.otime->setproperty, tdouble=datetime
                state.otime->setproperty, tstring=spd_ui_timefix(datetime_str) ;otherwise tstring does not hold frac seconds value
             Endelse
             
             widget_control, event.top, set_uvalue=state, /no_copy
             Widget_Control, event.top, /Destroy
             return
           end
           
      'CANCEL':begin
                 widget_control, event.top, /Destroy
                 return
               end
               
      'HOURS':BEGIN
                IF event.valid then begin
                  IF event.value LT 0 OR event.value GE 24 THEN BEGIN
                    limit = event.value lt 0 ? 0:23
                    state.hour = limit
                    Widget_Control, event.id, set_value = limit
                  ENDIF ELSE BEGIN
                    state.hour=event.value
                  ENDELSE
                ENDIF
              END
              
      'MINUTES':BEGIN
                  IF event.valid then begin
                    IF event.value LT 0 OR event.value GE 60 THEN BEGIN
                      limit = event.value lt 0 ? 0:59
                      state.minute = limit
                      Widget_Control, event.id, set_value = limit
                    ENDIF ELSE BEGIN
                      state.minute=event.value
                    ENDELSE
                  ENDIF
                END
                
      'SECONDS':BEGIN
                  IF event.valid then begin
                    IF event.value LT 0 OR event.value GE 60 THEN BEGIN
                      limit = event.value lt 0 ? 0:59
                      state.second = limit
                      Widget_Control, event.id, set_value = limit
                    ENDIF ELSE BEGIN
                      state.second=event.value
                    ENDELSE
                  ENDIF
                END
      'FSEC':BEGIN
               IF event.valid then begin
                 IF event.value LT 0 OR event.value GE 1.0 THEN BEGIN
                   limit = event.value lt 0 ? 0:0.99999
                   state.fsecond = limit
                   Widget_Control, event.id, set_value = limit
                 ENDIF ELSE BEGIN
                   state.fsecond=event.value
                 ENDELSE
               ENDIF
             END
                
      'DAY':BEGIN
              widget_control, event.id, get_value=day
              widget_control, state.day, set_value=day
            END
            
      'YEAR':BEGIN
               widget_control, state.year, set_value=widget_info(event.id, /combobox_gettext)
               spd_ui_calendar_update, state.wBase, state
             END
             
      'MONTH':BEGIN
                widget_control, event.id, get_value=tlist
                widget_control, state.month, $ 
                  set_value= strtrim((where(tlist eq widget_info(event.id, /combobox_gettext)))[0]+1,2)
                spd_ui_calendar_update, state.wBase, state 
              END
              
    ENDCASE
  ENDIF 
  
  widget_control, event.top, set_uvalue=state, /no_copy
  
  Return
  
END ;--------------------------------------------------------------




PRO spd_ui_calendar, title, otime, gui_id, startyear=startyear

    compile_opt idl2, hidden

;Verify valid time object was passed in
   if obj_valid(otime) then begin
     intime=otime->getstructure()
   endif else begin
     otime=obj_new('spd_ui_time')
     intime=otime->getstructure()
   endelse

;Create base widgets to hold labels for the selected month, day, and year. Set the initial values of the labels.
   wBase = WIDGET_BASE(COLUMN = 1, $ 
      TITLE=title, /Align_Center, /modal, group_leader=gui_id) 
   wDateBase = WIDGET_BASE(wBase, /ROW)
   wYearBase = WIDGET_BASE(wDateBase, /COL, /align_center, xpad=5, ypad=2)
   wTimeBase = WIDGET_BASE(wDateBase, /COL, /base_align_right)
   wSubBase = WIDGET_BASE(wYearBase, /ROW) 
   wSubTimeBase = WIDGET_BASE(wTimeBase, /COL, xpad=1, tab_mode=1)
   wVoid = WIDGET_LABEL(wSubBase, VALUE = 'Year: ') 
   wYear = WIDGET_LABEL(wSubBase, VALUE = '1999')
   wSubBase = WIDGET_BASE(wYearBase, /ROW) 
   wVoid = WIDGET_LABEL(wSubBase, value = 'Month: ') 
   wMonth = WIDGET_LABEL(wSubBase, value = '10') 
   wSubBase = WIDGET_BASE(wYearBase, /ROW) 
   wVoid = WIDGET_LABEL(wSubBase, VALUE = 'Day: ') 
   wDay = WIDGET_LABEL(wSubBase, VALUE = '22')    
   
   ; Get size of largest spinner label and set for all if larger than standard 
   test = widget_label(wsubtimebase, value = 'Seconds: ')
   xls = widget_info(test, /geometry)
   widget_control, test, /destroy
   
   xls = xls.xsize gt 52 ? xls.xsize:52 
      
   wHour=spd_ui_spinner(wSubTimeBase, Label='Hour: ', xlabelsize=xls, Increment=1, Value=intime.hour, uval='HOURS')
   wMinute=spd_ui_spinner(wSubTimeBase, Label='Minute: ', xlabelsize=xls, Increment=1, Value=intime.min, uval='MINUTES')
   wSecond=spd_ui_spinner(wSubTimeBase, Label='Second: ', xlabelsize=xls, Increment=1, Value=intime.sec, uval='SECONDS')
   wFsec=spd_ui_spinner(wSubTimeBase, Label='Fraction: ', xlabelsize=xls, Increment=1.0e-6, Value=intime.fsec, uval='FSEC')

;Calendar proper widgets
  calBase = widget_base(wBase, /col, /align_center)
  
;List of years created by adding the starting year to an integer array
; updated to include the possibility of years back to 1800s, egrimes 1/16/13
  if undefined(startyear) then startyear = 2000
  current_year = strmid(systime(), 3, /reverse_offset)
  num_of_years = current_year-startyear+2
  years = strtrim((indgen(num_of_years)+startyear),2) ; include years back to 1800s, for IUGONET
 
  ; create a string for showing the valid date range and
  ; remove the padding introduced by converting the years to strings
  valid_dates_tooltip = 'Valid dates: '+ strcompress(string(startyear), /rem) + ' - ' + strcompress(string(current_year), /rem)

  months = ['January','February','March','April','May','June','July','August','September','October','November','December']
  numDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

  dropBase = widget_base(calBase, /row, /align_center, space=60, ypad=5)
  yearbase = widget_base(dropBase, /row, /align_left)
  yearlabel = widget_label(yearbase, value = 'Year: ')
  yearlist = widget_combobox(yearbase, value = years, uvalue='YEAR', uname='year')
  monthbase = widget_base(dropbase, /row, /align_right)
  monthlabel = widget_label(monthbase, value = 'Month: ')
  monthlist = widget_combobox(monthbase, value = months, uvalue='MONTH', uname='month')
   
   
;Create labels for the caledar's days
  dayBase = widget_base(calBase, /row, /base_align_center, xpad=0, space=0)
  for j=0, 6 do dID = widget_Label(daybase, value=days[j], /align_center)
  
;Create 5 rows of 7 buttons for calendar days 
  rows = lonarr(5)
  for j=0, n_elements(rows)-1 do rows[j] = widget_base(calBase, /row, xpad=0, ypad=0, space=0,frame=0)
  for j=0, n_elements(rows)-1 do begin
    for i=0, 6 do bID = widget_button(rows[j], value='', /no_release, ysize=35, $
                                      uvalue='DAY', uname=strtrim((7*j)+(i),2), ToolTip = valid_dates_tooltip)
  endfor
 
;Create the ok and cancel buttons
   buttonBase = WIDGET_BASE(wBase, /row, /align_center)
   okButton = WIDGET_BUTTON(buttonBase, value='OK', uValue='OK', ToolTip = valid_dates_tooltip)
   cancelButton = WIDGET_BUTTON(buttonBase, value='Cancel', uValue='CANCEL')
   
   
;Realize the top-lvel base widget 
   WIDGET_CONTROL, wBase, /REALIZE 


;Set widget values
   year = strtrim(intime.year, 2)

   WIDGET_CONTROL, yearlist, set_combobox_select = where(years eq year)
   WIDGET_CONTROL, monthlist, set_combobox_select = intime.month-1
   WIDGET_CONTROL, wYear, SET_VALUE=widget_info(yearList,/combobox_gettext)
   WIDGET_CONTROL, wMonth, SET_VALUE=strtrim(intime.month,2)
   WIDGET_CONTROL, wDay, SET_VALUE=STRTRIM(intime.date, 2) 
   
;Hour/min/sec/fsec stored directly in state variables, month/day/year stored in their corresponding display widgets
   state = {wBase:wBase, calBase:calBase, month:wMonth, temp:-1L, day:wDay, year:wYear, $
            hour:intime.hour, minute:intime.min, second:intime.sec, fsecond:intime.fsec, otime:otime, numdays:numdays}
 
   spd_ui_calendar_update, wbase, state
   
   WIDGET_CONTROL, wBase, set_uvalue=state, /no_copy 
   
;Call XMANAGER to manage the widget events, and end the procedure.
   XMANAGER, 'spd_ui_calendar', wBase 
   
   return
 
END ;--------------------------------------------------------------
