with
  Terminal_Interface.Curses;

with Ticker;

package body Bounce is
   use
     Terminal_Interface.Curses;

   procedure Increment (Counter   : in out Column_Position;
                        Direction : in     Integer) is
   begin
      Counter := Column_Position (Integer (Counter) + Direction);
   exception
      when Constraint_Error =>
         if Direction < 0 then
            Counter := Column_Position'First;
         else
            Counter := Column_Position'Last;
         end if;
   end Increment;

   procedure Increment (Counter   : in out Line_Position;
                        Direction : in     Integer) is
   begin
      Counter := Line_Position (Integer (Counter) + Direction);
   exception
      when Constraint_Error =>
         if Direction < 0 then
            Counter := Line_Position'First;
         else
            Counter := Line_Position'Last;
         end if;
   end Increment;

   procedure Set_Up is
   begin
      Init_Screen;
      Set_Cbreak_Mode (True);
      Set_Echo_Mode (False);

      C_Signals.Ignore (C_Signals.Signal_Interrupt);
      Add (Line   => Ball.Position.Y,
           Column => Ball.Position.X,
           Ch     => Ball.Symbol);
      Refresh;

      C_Signals.Attach (Handler => Move_Ball'Access,
                        To      => C_Signals.Signal_Alarm);
      Ticker.Start (0.020);
   end Set_Up;

   procedure Wrap_Up is
   begin
      Ticker.Stop;
      End_Screen;
   end Wrap_Up;

   procedure Move_Ball (Handling : in C_Signals.Signal_Types) is
      Old   : constant Point := Ball.Position;
      Moved : Boolean := False;
   begin
      C_Signals.Ignore (C_Signals.Signal_Alarm);

      if Ball.TTM.X > 0 then
         Ball.TTG.X := Ball.TTG.X - 1;
         if Ball.TTG.X <= 1 then
            Increment (Ball.Position.X, Ball.Direction.X);
            Ball.TTG.X := Ball.TTM.X;
            Moved := True;
         end if;
      end if;

      if Ball.TTM.Y > 0 then
         Ball.TTG.Y := Ball.TTG.Y - 1;
         if Ball.TTG.Y <= 1 then
            Increment (Ball.Position.Y, Ball.Direction.Y);
            Ball.TTG.Y := Ball.TTM.Y;
            Moved := True;
         end if;
      end if;

      if Moved then
         Add (Line   => Old.Y,
              Column => Old.X,
              Ch     => ' ');
         Add (Line   => Ball.Position.Y,
              Column => Ball.Position.X,
              Ch     => Ball.Symbol);
         Bounce_Or_Lose (Ball);
         Move_Cursor (Line => Lines - 1, Column => Columns - 1);
         Refresh;
      end if;

      C_Signals.Attach (Handler => Move_Ball'Access,
                        To      => C_Signals.Signal_Alarm);
   end Move_Ball;

   procedure Bounce_Or_Lose (Ball : in out Ping_Pong_Ball) is
   begin
      if Ball.Position.X <= Left_Edge then
         Ball.Direction.X := 1;
      elsif Ball.Position.X >= Right_Edge then
         Ball.Direction.X := -1;
      end if;

      if Ball.Position.Y <= Top_Row then
         Ball.Direction.Y := 1;
      elsif Ball.Position.Y >= Bottom_Row then
         Ball.Direction.Y := -1;
      end if;
   end Bounce_Or_Lose;
end Bounce;

