-----------------------------------------------------------------------
--                              Ada Labs                             --
--                                                                   --
--                 Copyright (C) 2008-2023, AdaCore                  --
--                                                                   --
-- This program is free software: you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public License as    --
-- published by the Free Software Foundation, either version 3 of    --
-- the License, or (at your option) any later version.               --
--                                                                   --
-- This program is distributed in the hope that it will be useful,   --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     --
-- GNU General Public License for more details.                      --
--                                                                   --
-- You should have received a copy of the GNU General Public License --
-- along with this program.  If not, see                             --
-- <https://www.gnu.org/licenses/>.                                  --
-----------------------------------------------------------------------

with Float_Maths; use Float_Maths;

package body Solar_System is

   --  QUESTION 1
   --$ begin question
   --  declare an exception called Bodies_Collision
   --$ end question
   --$ begin answer
   Bodies_Collision : exception;
   --$ end answer

   procedure Init_Body
     (B            :        Bodies_Enum_T;
      Bodies       : in out Bodies_Array_T;
      Radius       :        Float;
      Color        :        RGBA_T;
      Distance     :        Float;
      Angle        :        Float;
      Speed        :        Float;
      Turns_Around :        Bodies_Enum_T;
      Visible      :        Boolean := True)
   is
   begin

      Bodies (B) :=
        (Distance     => Distance,
         Speed        => Speed,
         Angle        => Angle,
         Turns_Around => Turns_Around,
         Visible      => Visible,
         Color        => Color,
         Radius       => Radius,
         others       => <>);

   end Init_Body;

   --  implement a function to compute the X coordinate
   --  x of the reference + distance * cos(angle)
   function Compute_X
     (Body_To_Move : Body_T;
      Turns_Around : Body_T) return Float;

   --  implement a function to compute the Y coordinate
   --  y of the reference + distance * sin(angle)
   function Compute_Y
     (Body_To_Move : Body_T;
      Turns_Around : Body_T) return Float;

   function Compute_X
     (Body_To_Move : Body_T;
      Turns_Around : Body_T) return Float
   is
   begin
      return Turns_Around.X + Body_To_Move.Distance * Cos (Body_To_Move.Angle);
   end Compute_X;

   function Compute_Y
     (Body_To_Move : Body_T;
      Turns_Around : Body_T) return Float
   is
   begin
      return Turns_Around.Y + Body_To_Move.Distance * Sin (Body_To_Move.Angle);
   end Compute_Y;

   function Colliding (A : Body_T; B : Body_T) return Boolean is
   begin
      return (A.X - B.X) ** 2 + (A.Y - B.Y) ** 2 <= (A.Radius + B.Radius) ** 2;
   end Colliding;

   procedure Move (Body_To_Move : in out Body_T; Bodies : Bodies_Array_T) is
   begin
      Body_To_Move.X :=
        Compute_X (Body_To_Move, Bodies (Body_To_Move.Turns_Around));

      Body_To_Move.Y :=
        Compute_Y (Body_To_Move, Bodies (Body_To_Move.Turns_Around));

      Body_To_Move.Angle := Body_To_Move.Angle + Body_To_Move.Speed;

      for Body_Collision_Check of Bodies loop
         if Body_To_Move /= Body_Collision_Check
           and then Colliding (Body_To_Move, Body_Collision_Check)
         then
            --  QUESTION 2
            --$ begin question
            --  raise an exception in case of collision between both bodies
            null;
            --$ end question
            --$ begin answer
            raise Bodies_Collision;
            --$ end answer
         end if;
      end loop;

   end Move;

   procedure Move_All (Bodies : in out Bodies_Array_T) is
   begin

      --  loop over all bodies and call Move procedure
      for B of Bodies loop

         declare
         begin
            --  call the move procedure for each body
            Move (B, Bodies);

            --  QUESTION 3
         --$ begin question
            --  catch the exception around this call
            --  and set B.Speed to -B.Speed
         --$ end question
         --$ begin answer
         exception
            when Bodies_Collision =>
               B.Speed := -B.Speed;
         --$ end answer
         end;
      end loop;

   end Move_All;

end Solar_System;
