{*****************************************
 *          **** カレンダー ****         *
 *                                       *
 *        HAPPy のサンプルプログラム     *
 *         として作成しました。          *
 *                                       *
 *           Auther  H.Asano             *
 *****************************************}

{
   inputから、印字させたい年と月を入力すると、
   その月の前後1ケ月ずつ、合計3ケ月にわたってカレンダーを
   outputに出力します
}

program Calender(input,output) ;

type
  PrintRange = (before,now,after) ;   { before:前月 now:今月 after:来月}
  YoubiType  = 0..6 ;                 { 日曜日=0  土曜日=6             }

var
  Nissu      : array[1..12]      of 1..31     ;  { 月の日数を格納   }
  Year       : array[PrintRange] of integer   ;  { 印字する年を格納 }
  Month      : array[PrintRange] of integer   ;  { 印字する月を格納 }
  FirstYoubi : array[PrintRange] of YoubiType ;  { 1日の曜日        }


{*****************************************
 *      初期設定 (各月の日数を設定)      *
 *        とりあえず 2月は28日としておく *
 *****************************************}
procedure init ;
begin
  Nissu[ 1{月}] := 31{日} ; Nissu[ 2{月}] := 28{日} ; Nissu[ 3{月}] := 31{日} ;
  Nissu[ 4{月}] := 30{日} ; Nissu[ 5{月}] := 31{日} ; Nissu[ 6{月}] := 30{日} ;
  Nissu[ 7{月}] := 31{日} ; Nissu[ 8{月}] := 31{日} ; Nissu[ 9{月}] := 30{日} ;
  Nissu[10{月}] := 31{日} ; Nissu[11{月}] := 30{日} ; Nissu[12{月}] := 31{日}
end {init} ;

{***************************************
 *   y年m月d日の曜日を算出する関数     *
 *     この関数で使っている計算式の    *
 *     意味はよくわかりませんが、      *
 *     汎用関数として使えると思います  *
 ***************************************}
function Youbi(y{年},m{月},d{日}:integer) : YoubiType ;
  var m1,y1 : integer;
begin
  if m >= 3 then
    begin  m1 := m - 2   ; y1 := y      end
  else
    begin  m1 := m  + 10 ; y1 := y - 1  end ;
  Youbi := (y1 + y1 div 4 - y1 div 100 + y1 div 400
               + trunc(2.6*m1 - 0.19) + d    ) mod 7
end {Youbi} ;

{*****************************************
 *   year年が閏年の時､真を返す関数       *
 *     4年に一度だが､、100年に一度閏年で *
 *     なく、400年に一度閏年になります   *
 *****************************************}
function Uruu(year:integer) : Boolean ;
begin
  Uruu := (year mod 4 = 0) and (year mod 100 <> 0) or (year mod 400 = 0)
end {Uruu} ;

{***************************************
 *   カレンダーの印字処理              *
 ***************************************}
procedure Print ;
  var Day    : array[PrintRange] of integer ;  { 印字する日 }
      Finish : array[PrintRange] of Boolean ;  { 各月の印字が終わったら真 }
      youbi  : YoubiType  ;                    { for文の制御変数 }
      n      : PrintRange ;                    { for文の制御変数 }
begin
  for n := before to after do           { 初期設定 }
  begin
    Finish[n] := false ;
    Day   [n] := 1{日}
  end ;

  writeln ;                             { カレンダーの表題 }
  for n := before to after do
    write('****':9,Year[n]:5,'年',Month[n]:2,'月 ****') ;
  writeln ;
  for n := before to after do
    write('日 月 火 水 木 金 土':25) ;
  writeln ;

  repeat
    for n := before to after do          { 前月 今月 来月の 1行分   }
    begin
      write(' ':4) ;                     { 次の月のカラムまで進める }
      for youbi := 0{日曜} to 6{土曜} do { 各月の1週間分            }
      begin
        if (Day[n] = 1{日}) and (youbi < FirstYoubi[n]) or Finish[n]
          then write(' ':3)
        else                             { 印字していない日の時     }
        begin
          write(Day[n]:3) ;
          Day[n] := Day[n] + 1{日} ;
          Finish[n] :=  Day[n] > Nissu[Month[n]]  { その月の終わりの判定 }
        end
      end {for youbi}
    end {for n} ;
    writeln
  until Finish[before] and Finish[now] and Finish[after]

end {Print} ;

{***************************************
 *            メイン処理               *
 ***************************************}
begin {main}
  init ;                                { 初期設定 }

  repeat                                { 印字したい年を入力 }
    write('何年?(西暦2年〜9998年まで) ') ;
    readln(Year[now])
  until (2{年} <= Year[now]) and  (Year[now] <= 9998{年}) ;
                     { 2〜9998年に深い意味はありません       }

  repeat                                { 印字したい月を入力 }
    write('何月?(1月〜12月まで) ') ;
    readln(Month[now])
  until (1{月} <= Month[now]) and (Month[now] <= 12{月}) ;

  if Uruu(Year[now]) then Nissu[2{月}] := 29{日} ;   { 閏年補正 }

                                        { 印字する年､月を求める }
  Month[before] := Month[now]  - 1{月} ;
  Month[after ] := Month[now]  + 1{月} ;
  Year [before] := Year [now] ;
  Year [after ] := Year [now] ;
  if Month[now] = 1{月} then            { 今月が1月の時は、}
  begin                                 { 前月は去年の12月 }
    Month[before] := 12{月} ;
    Year [before] := Year[now] - 1{年}
  end
  else if Month[now] = 12{月} then      { 今月が12月の時は、}
  begin                                 { 来月は来年の1月   }
    Month[after] := 1{月} ;
    Year [after] := Year[now] + 1{年}
  end ;
                                        { 1日の曜日を求める }
  FirstYoubi[before] := Youbi(Year[before], Month[before], 1{日}) ;
  FirstYoubi[now   ] := Youbi(Year[now   ], Month[now   ], 1{日}) ;
  FirstYoubi[after ] := Youbi(Year[after ], Month[after ], 1{日}) ;

  Print                                 { 印字する }

end {main}.
