# mission5.adb

    with Calendrier ;
    with Gada.Text_IO ;

    procedure Mission5 is

       package Cal renames Calendrier ;
       package Txt renames GAda.Text_IO ;

       -- Compte le nombre total de jours
       function Compter(An : Cal.T_Annee) return Integer is
          Count : Integer := 0 ;
       begin
          -- Pour chaque mois, additionner le nombre de jours
          for No_Mois in An'Range loop
    	 Count := Count + An(No_Mois).Nombre_Jours ;
          end loop ;

          return Count ;
       end Compter ;

       -- Renvoie vrai si dur1 > dur2
       function Duree_Plus_Grande (Dur1, Dur2 : Cal.T_Duree) return Boolean is
       begin
          return (Dur1.Heures > Dur2.Heures) or (Dur1.Heures = Dur2.Heures and Dur1.Minutes > Dur2.Minutes) ;
       end Duree_Plus_Grande ;

       type T_Date is record
          Le_Jour : Integer ;
          Le_Mois : Integer ;
       end record ;

       procedure Afficher_Date (Intitule : String ; Date : T_Date) is
       begin
          Txt.Put_Line(Intitule & " : " & Integer'Image(Date.Le_Jour) & " / " & Integer'Image(Date.Le_Mois)) ;
       end Afficher_Date ;

       function Plus_Long (An : Cal.T_Annee) return T_Date is

          -- Durée du jour le plus long vu jusqu'à présent
          Duree_Max : Cal.T_Duree := (Heures => 0, Minutes => 0) ;

          -- Jour le plus long vu jusqu'à présent
          Jour_Long : T_Date := (Le_Jour => 0, Le_Mois => 0) ;

          -- Mois courant
          Mois : Cal.T_Mois ;
       begin
          -- Pour chaque mois,
          for No_Mois in An'Range loop

    	 Mois := An(No_Mois) ;

    	 -- Regarder chaque jour
    	 for No_Jour in 1..Mois.Nombre_Jours loop
    	    if Duree_Plus_Grande(Mois.Jours(No_Jour).Duree_Jour, Duree_Max) then
    	       Duree_Max := Mois.Jours(No_Jour).Duree_Jour ;
    	       Jour_Long := (Le_Mois => No_Mois, Le_Jour => No_Jour) ;
    	    end if ;
    	 end loop ;
          end loop ;

          return Jour_Long ;
       end Plus_Long ;

       --
       --  Différentes procédures d'affichage
       --
       procedure Afficher_Nom_Jour(Nom : Cal.T_Nom_Jour) is
       begin
          case Nom is
    	 when Cal.Lun => Txt.Put("Lundi") ;
    	 when Cal.Mar => Txt.Put("Mardi") ;
    	 when Cal.Mer => Txt.Put("Mercredi") ;
    	 when Cal.Jeu => Txt.Put("Jeudi") ;
    	 when Cal.Ven => Txt.Put("Vendredi") ;
    	 when Cal.Sam => Txt.Put("Samedi") ;
    	 when Cal.Dim => Txt.Put("Dimanche") ;
          end case ;
       end Afficher_Nom_Jour ;

       procedure Afficher_Phase_Lune(Phase : Cal.T_Lune) is
       begin
          case Phase is
    	 when Cal.Nouvelle_Lune => Txt.Put("nouvelle lune") ;
    	 when Cal.Pleine_Lune => Txt.Put("pleine lune") ;
    	 when Cal.Premier_Quartier => Txt.Put("premier quartier") ;
    	 when Cal.Dernier_Quartier => Txt.Put("dernier quartier") ;
    	 when Cal.Intermediaire => null ;
          end case ;
       end Afficher_Phase_Lune ;

       procedure Afficher_Jour( Jour : Cal.T_Jour) is
       begin
          Afficher_Nom_Jour(Jour.Nom_Jour) ;
          Txt.Put(" " & Integer'Image(Jour.Duree_Jour.Heures) & "H" & Integer'Image(Jour.Duree_Jour.Minutes) & " ") ;
          Afficher_Phase_Lune(Jour.Phase_Lune) ;
       end Afficher_Jour ;

       procedure Afficher_Mois( Mois : Cal.T_Mois) is
       begin
          -- En-tête : le nom du mois
          Txt.New_Line ;
          Txt.Put_Line("--------  " & Mois.Nom_Mois & "  -------") ;
          Txt.New_Line ;

          -- Puis tous les jours
          for No_Jour in 1..Mois.Nombre_Jours loop
    	 Txt.Put(" No " & Integer'Image(No_Jour) & "  ") ;
    	 Afficher_Jour(Mois.Jours(No_Jour)) ;
    	 Txt.New_Line ;
          end loop ;

          Txt.New_Line ;
       end Afficher_Mois ;

       procedure Afficher_Annee( An : Cal.T_Annee) is
       begin
          for No_Mois in An'Range loop
    	 Afficher_Mois(An(No_Mois)) ;
          end loop ;
       end Afficher_Annee ;

       -- Algorithme de recherche
       function Chercher_Lune ( An : Cal.T_Annee ; Depart : T_Date ; Phase : Cal.T_Lune) return T_Date is

          -- Date renvoyée si rien ne convient.
          Resultat : T_Date := (0,0) ;
          Trouve : Boolean ;

          -- Position courante
          Actuel : T_Date ;

          -- Dernier mois de l'année
          Dernier_Mois : constant Integer := 12 ;

          -- On est obligé de mettre un use à cause du if sur la phase de la lune (bizarrerie Ada...)
          use Cal ;

       begin
          Trouve := False ;
          Actuel := Depart ;

          -- Lorsqu'on aura parcouru tous les jours jusqu'à la fin de l'année, la date passera au mois numéro 13.
          while (not Trouve) and Actuel.Le_Mois <= Dernier_Mois loop

    	 -- Bonne lune ?
    	 if An(Actuel.Le_Mois).Jours(Actuel.Le_Jour).Phase_Lune = Phase then
    	    -- Trouvé !
    	    Trouve := True ;
    	    Resultat := Actuel ;

    	 else
    	    -- Ce n'est pas ce qu'on cherche, il faut passer au suivant.
    	    -- D'habitude, il suffit de faire Index := Index +1, ici c'est un peu plus subtil.

    	    -- Est-on au dernier jour du mois ?
    	    if Actuel.Le_Jour = An(Actuel.Le_Mois).Nombre_Jours then
    	       -- Si oui, il faut changer de mois
    	       Actuel.Le_Jour := 1 ;
    	       Actuel.Le_Mois := Actuel.Le_Mois + 1 ;
    	    else
    	       -- Sinon, on passe au jour suivant du même mois
    	       Actuel.Le_Jour := Actuel.Le_Jour + 1 ;
    	    end if ;
    	 end if ;

          end loop ;

          return Resultat ;
       end Chercher_Lune ;

    begin

       -- Test de compter
       Txt.Put_Line(Integer'Image(Compter(Cal.Cal2042)) & " jours dans l'année 2042.") ;

       -- Jour le plus long (il affiche le 16 juin, dont la durée est sensiblement la même que le 21 juin, à 1 minute près).
       Afficher_Date("Jour le plus long", Plus_Long(Cal.Cal2042)) ;

       Afficher_Annee( Cal.Cal2042) ;

       Afficher_Date("Date de la première pleine lune après le 14 juillet", Chercher_Lune( Cal.Cal2042, (14,07), Cal.Pleine_Lune )) ;

    end Mission5 ;