!!*********************************************************************
!! No Copyright - this is Freeware
!!*********************************************************************
!! File:       Calendar.f
!! Author:     Xie Wei Bao
!! Email:      tech@cup.btinternet.co.uk
!!
!! Purpose:
!!    Display a calendar
!!
!!*********************************************************************
      program main
      integer month, year
      call GetData (month, year)
      call GenerateCalendar (month, year)
      call PrintCalendar (month, year)
      stop
      end
!**********************************************************************
! Data definitions
!**********************************************************************
      block data
      common /calinfo/ mDaysPerMonth, mWeek,
     &       mDayStr, mMonthStr, mDateStr
      integer mDaysPerMonth(12)
      integer mWeek(0:4,0:6)
      character*9  mDayStr(0:6)
      character*17 mMonthStr(12)
      character*2  mDateStr(0:31)
      data mDaysPerMonth /
     &     31, 28, 31, 30, 31, 30,
     &     31, 31, 30, 31, 30, 31 /
      data mDayStr /
     &    ' Monday  ',
     &    ' Tuesday ',
     &    'Wednesday',
     &    ' Thursday',
     &    '  Friday ',
     &    ' Saturday',
     &    '  Sunday ' /
      data mMonthStr /
     &    '    J A N U A R Y',
     &    '  F E B R U A R Y',
     &    '        M A R C H',
     &    '        A P R I L',
     &    '            M A Y',
     &    '          J U N E',
     &    '          J U L Y',
     &    '      A U G U S T',
     &    'S E P T E M B E R',
     &    '    O C T O B E R',
     &    '  N O V E M B E R',
     &    '  D E C E M B E R' /
      data mDateStr /
     &    '  ', ' 1', ' 2', ' 3', ' 4', ' 5', ' 6', ' 7', ' 8', ' 9',
     &    '10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
     &    '20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
     &    '30', '31' /
      end
!**********************************************************************
! Compute the day of the week.  Day 0 = Monday
!**********************************************************************
      integer function DayOfWeek (iDay, iMonth, iYear)
      implicit none
!     parameters
      integer iDay, iMonth, iYear
!     common
!     local
      integer dd, mm, yy
      if (iMonth .gt. 2) then
          mm = iMonth + 1
          yy = iYear
      else
          mm = iMonth + 13
          yy = iYear - 1
      endif
      dd = iDay + int (mm * 30.6) + int ((yy * 1461) / 4)
!     add 4 to make 0=Monday
      dd = dd + 4
      DayOfWeek = mod (dd, 7)
      return
      end
!**********************************************************************
! Get the month to be printed
!**********************************************************************
      subroutine GetData (oMonth, oYear)
!     parameters
      integer oMonth, oYear
!     common
!     local
      write (*, '(''Month: '')')
      read *, oMonth
      write (*, '(''Year: '')')
      read *, oYear
      return
      end
!**********************************************************************
! Place the days in the correct positions
!**********************************************************************
      subroutine GenerateCalendar (iMonth, iYear)
      implicit none
!     parameters
      integer iMonth, iYear
!     common
      common /calinfo/ mDaysPerMonth, mWeek,
     &       mDayStr, mMonthStr, mDateStr
      integer mDaysPerMonth(12)
      integer mWeek(0:4, 0:6)
      character*9  mDayStr(0:6)
      character*17 mMonthStr(12)
      character*2  mDateStr(0:31)
      external DayOfWeek
      integer DayOfWeek
!     local
      integer week, day
      integer mday  ! month day 1-31
      integer lastday ! of month
      
      do 20 week = 0, 4, 1
          do 10 day = 0, 6, 1
              mWeek(week,day) = 0
10        continue
20    continue
!     This will work until 2100
      lastday = mDaysPerMonth(iMonth)
      if (iMonth .eq. 2 .and. mod (iYear, 4) .eq. 0)
     &    lastday = lastday + 1
!     Fill in the calendar
      day = DayOfWeek (1, iMonth, iYear)
      week = 0
      do 30 mday = 1, lastday, 1
          mWeek(week,day) = mday
          day = mod (day + 1, 7)
          if (day .eq. 0) week = mod (week + 1, 5)
30    continue
      return
      end
!**********************************************************************
! Generate a calendar for the month and year
!**********************************************************************
      subroutine PrintCalendar (iMonth, iYear)
      implicit none
!     parameters
      integer iMonth, iYear
!     common
      integer pLineLen, pYearOffsetLo, pYearOffsetHi
      parameter (
     &    pLineLen = 80,
     &    pYearOffsetLo = 6,
     &    pYearOffsetHi = 12)
      common /calinfo/ mDaysPerMonth, mWeek,
     &       mDayStr, mMonthStr, mDateStr
      integer mDaysPerMonth(12)
      integer mWeek(0:4, 0:6)
      character*9  mDayStr(0:6)
      character*17 mMonthStr(12)
      character*2  mDateStr(0:31)
!     local
      character line*(pLineLen)
      integer linelen
      integer mid, i, w, d
      integer posn, digit, yval, offset

!     wipe out the line
      do 10 i = 1, pLineLen, 1
          line(i:i) = ' '
10    continue

!     Centralize month and year
      linelen = len(mMonthStr(iMonth))
      mid = (pLineLen - linelen - pYearOffsetHi) / 2
      line(mid:) = mMonthStr(iMonth)
      yval = iYear
      do 20 offset = pYearOffsetHi, pYearOffsetLo, -2
          digit = mod(yval, 10) + ichar ('0')
          posn = offset + mid + linelen
          line(posn:posn) = char(digit)
          yval = yval / 10
20    continue
      write (*, '(A80//)') line
!     Print the weekdays
      write (*, 1000) (mDayStr(d), d = 0, 6, 1)
1000  format (7(2x,A9)/'+',7('----------+'))
!     For some silly reason, we cannot do this in a
!     nested implied loop
      do 30 w = 0, 4, 1
          write (*, 1010) (mDateStr(mWeek(w,d)), d = 0, 6, 1)
1010      format (
     &        '|',7(8x,A2,'|')/
     &        4('|',7(10x,'|')/),
     &        '+',7('----------+'))
30    continue
      return
      end
