------------------------------------------------------------------------------
--                                                                          --
--                An Ada2005 binding to the Qt C++ framework                --
--                                                                          --
------------------------------------------------------------------------------
--                                                                          --
-- Copyright © 2008, 2009 Vadim Godunko <vgodunko@gmail.com>                --
--                                                                          --
-- QtAda is free software;  you can  redistribute it and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion. QtAda is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 distributed with QtAda;  see file COPYING.  If not, write --
-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
-- Boston, MA 02110-1301, USA.                                              --
--                                                                          --
------------------------------------------------------------------------------
--  $Revision: 3266 $ $Date: 2009-04-18 13:27:41 +0400 (Sat, 18 Apr 2009) $
------------------------------------------------------------------------------
with Ada.Numerics.Elementary_Functions;
with Ada.Numerics.Float_Random;

with Qt4.Brushes;
with Qt4.Colors;
with Qt4.Objects;
with Qt4.Pens;
with Qt4.Regions;
with Qt4.Points;
with Qt4.Palettes.Constructors;
with Qt4.Timers.Constructors;
with Qt4.Widgets.Directors;

with Cannon_Fields.MOC;
pragma Warnings (Off, Cannon_Fields.MOC);
--  Child package MOC must be included in the application.

package body Cannon_Fields is

   use type Qt4.Q_Integer;

   Barrel_Rect : constant Qt4.Rects.Q_Rect
     := Qt4.Rects.Create (30, -5, 20, 10);

   -----------
   -- Angle --
   -----------

   function Angle (Self : not null access Cannon_Field'Class)
     return Qt4.Q_Integer
   is
   begin
      return Self.Current_Angle;
   end Angle;

   -----------------
   -- Cannon_Rect --
   -----------------

   function Cannon_Rect (Self : not null access Cannon_Field'Class)
     return Qt4.Rects.Q_Rect
   is
   begin
      return Result : Qt4.Rects.Q_Rect := Qt4.Rects.Create (0, 0, 50, 50) do
         Result.Move_Bottom_Left (Self.Rect.Bottom_Left);
      end return;
   end Cannon_Rect;

   ------------
   -- Create --
   ------------

   function Create (Parent : access Qt4.Widgets.Q_Widget'Class := null)
     return not null Cannon_Field_Access
   is
      Self    : constant not null Cannon_Field_Access := new Cannon_Field;
      Color   : constant Qt4.Colors.Q_Color
        := Qt4.Colors.Create (250, 250, 200);
      Palette : constant Qt4.Palettes.Q_Palette
        := Qt4.Palettes.Constructors.Create (Color);

   begin
      Qt4.Widgets.Directors.Constructors.Initialize (Self, Parent);

      Self.Current_Angle := 45;
      Self.Current_Force := 0;
      Self.Timer_Count := 0;
      Self.Auto_Shoot_Timer := Qt4.Timers.Constructors.Create (Self);

      Qt4.Objects.Connect
       (Self.Auto_Shoot_Timer,
        Qt4.Signal ("timeout()"),
        Self,
        Qt4.Slot ("moveShot()"));

      Self.Shoot_Angle := 0.0;
      Self.Shoot_Force := 0.0;
      Self.Target := Qt4.Points.Create (0, 0);
      Self.Set_Palette (Palette);
      Self.Set_Auto_Fill_Background (True);
      Self.New_Target;

      return Self;
   end Create;

   ------------------------
   -- Emit_Angle_Changed --
   ------------------------

   procedure Emit_Angle_Changed
    (Self      : not null access Cannon_Field'Class;
     New_Angle : Qt4.Q_Integer)
       is separate;

   ------------------------
   -- Emit_Force_Changed --
   ------------------------

   procedure Emit_Force_Changed
    (Self      : not null access Cannon_Field'Class;
     New_Force : Qt4.Q_Integer)
       is separate;

   --------------
   -- Emit_Hit --
   --------------

   procedure Emit_Hit (Self : not null access Cannon_Field'Class)
     is separate;

   -----------------
   -- Emit_Missed --
   -----------------

   procedure Emit_Missed (Self : not null access Cannon_Field'Class)
     is separate;

   -----------
   -- Force --
   -----------

   function Force (Self : not null access Cannon_Field'Class)
     return Qt4.Q_Integer
   is
   begin
      return Self.Current_Force;
   end Force;

   ---------------
   -- Move_Shot --
   ---------------

   procedure Move_Shot (Self : not null access Cannon_Field'Class)
   is
      Region : Qt4.Regions.Q_Region := Qt4.Regions.Create (Self.Shot_Rect);
      Shot_R : Qt4.Rects.Q_Rect;

   begin
      Self.Timer_Count := Self.Timer_Count + 1;
      Shot_R := Self.Shot_Rect;

      if Shot_R.Intersects (Self.Target_Rect) then
         Self.Auto_Shoot_Timer.Stop;
         Self.Emit_Hit;

      elsif Shot_R.X > Qt4.Widgets.Width (Self)
        or Shot_R.Y > Qt4.Widgets.Height (Self)
      then
         Self.Auto_Shoot_Timer.Stop;
         Self.Emit_Missed;

      else
         Region := Region.United (Qt4.Regions.Create (Shot_R));
      end if;

      Self.Update (Region);
   end Move_Shot;

   ----------------
   -- New_Target --
   ----------------

   procedure New_Target (Self : not null access Cannon_Field'Class)
   is
      Gen : Ada.Numerics.Float_Random.Generator;

   begin
      Ada.Numerics.Float_Random.Reset (Gen);
      Self.Target
        := Qt4.Points.Create
            (200
               + Qt4.Q_Integer
                  (Ada.Numerics.Float_Random.Random (Gen) * 190.0),
             10
               + Qt4.Q_Integer
                  (Ada.Numerics.Float_Random.Random (Gen) * 255.0));
      Self.Update;
   end New_Target;

   ------------------
   -- Paint_Cannon --
   ------------------

   procedure Paint_Cannon
    (Self    : not null access Cannon_Field'Class;
     Painter : in out Qt4.Painters.Q_Painter)
   is
   begin
      Painter.Set_Pen (Qt4.No_Pen);
      Painter.Set_Brush (Qt4.Brushes.Create (Qt4.Blue));

      Painter.Save;
      Painter.Translate (0.0, Qt4.Q_Real (Qt4.Widgets.Height (Self)));
      Painter.Draw_Pie (Qt4.Rects.Create (-35, -35, 70, 70), 0, 90 * 16);
      Painter.Rotate (Qt4.Q_Real (-Self.Current_Angle));
      Painter.Draw_Rect (Barrel_Rect);
      Painter.Restore;
   end Paint_Cannon;

   -----------------
   -- Paint_Event --
   -----------------

   overriding procedure Paint_Event
    (Self  : not null access Cannon_Field;
     Event : not null access Qt4.Paint_Events.Q_Paint_Event'Class)
   is
     pragma Unreferenced (Event);

     Painter : Qt4.Painters.Q_Painter := Qt4.Painters.Create (Self);

   begin
      Self.Paint_Cannon (Painter);

      if Self.Auto_Shoot_Timer.Is_Active then
         Self.Paint_Shot (Painter);
      end if;

      Self.Paint_Target (Painter);
   end Paint_Event;

   ----------------
   -- Paint_Shot --
   ----------------

   procedure Paint_Shot
    (Self    : not null access Cannon_Field'Class;
     Painter : in out Qt4.Painters.Q_Painter)
   is
   begin
      Painter.Set_Pen (Qt4.No_Pen);
      Painter.Set_Brush (Qt4.Brushes.Create (Qt4.Black));
      Painter.Draw_Rect (Self.Shot_Rect);
   end Paint_Shot;

   ------------------
   -- Paint_Target --
   ------------------

   procedure Paint_Target
    (Self    : not null access Cannon_Field'Class;
     Painter : in out Qt4.Painters.Q_Painter)
   is
   begin
      Painter.Set_Pen (Qt4.Pens.Create (Qt4.Colors.Create (Qt4.Black)));
      Painter.Set_Brush (Qt4.Brushes.Create (Qt4.Red));
      Painter.Draw_Rect (Self.Target_Rect);
   end Paint_Target;

   ---------------
   -- Set_Angle --
   ---------------

   procedure Set_Angle
    (Self  : not null access Cannon_Field'Class;
     Angle : Qt4.Q_Integer)
   is
      Aux : Qt4.Q_Integer := Angle;

   begin
      if Aux < 5 then
         Aux := 5;
      end if;

      if Aux > 70 then
         Aux := 70;
      end if;

      if Self.Current_Angle = Aux then
        return;
      end if;

      Self.Current_Angle := Aux;
      Self.Update;
      Self.Emit_Angle_Changed (Self.Current_Angle);
   end Set_Angle;

   ---------------
   -- Set_Force --
   ---------------

   procedure Set_Force
    (Self  : not null access Cannon_Field'Class;
     Force : Qt4.Q_Integer)
   is
      Aux : Qt4.Q_Integer := Force;

   begin
      if Aux < 0 then
         Aux := 0;
      end if;

      if Self.Current_Force = Aux then
         return;
      end if;

      Self.Current_Force := Aux;
      Self.Emit_Force_Changed (Self.Current_Force);
   end Set_Force;

   -----------
   -- Shoot --
   -----------

   procedure Shoot (Self : not null access Cannon_Field'Class) is
   begin
      if Self.Auto_Shoot_Timer.Is_Active then
        return;
      end if;

      Self.Timer_Count := 0;
      Self.Shoot_Angle := Qt4.Q_Float (Self.Current_Angle);
      Self.Shoot_Force := Qt4.Q_Float (Self.Current_Force);
      Self.Auto_Shoot_Timer.Start (0.005);
   end Shoot;

   ---------------
   -- Shot_Rect --
   ---------------

   function Shot_Rect (Self : not null access Cannon_Field'Class)
     return Qt4.Rects.Q_Rect
   is
      use type Qt4.Q_Real;
      use Ada.Numerics.Elementary_Functions;

   begin
      return Result : Qt4.Rects.Q_Rect := Qt4.Rects.Create (0, 0, 6, 6) do
         declare
            Gravity  : constant Qt4.Q_Real := 4.0;
            Time     : constant Qt4.Q_Real
              := Qt4.Q_Real (Self.Timer_Count) / 20.0;
            Velocity : constant Qt4.Q_Real := Qt4.Q_Real (Self.Shoot_Force);
            Radians  : constant Qt4.Q_Real
              := Qt4.Q_Real (Self.Shoot_Angle) * 3.14159265 / 180.0;
            Vel_X    : constant Qt4.Q_Real
              := Velocity * Qt4.Q_Real (Cos (Float (Radians)));
            Vel_Y    : constant Qt4.Q_Real
              := Velocity * Qt4.Q_Real (Sin (Float (Radians)));
            X_0      : constant Qt4.Q_Real
              := Qt4.Q_Real (Qt4.Rects.Right (Barrel_Rect) + 5)
                   * Qt4.Q_Real (Cos (Float (Radians)));
            Y_0      : constant Qt4.Q_Real
              := Qt4.Q_Real (Qt4.Rects.Right (Barrel_Rect) + 5)
                   * Qt4.Q_Real (Sin (Float (Radians)));
            X        : constant Qt4.Q_Real := X_0 + Vel_X * Time;
            Y        : constant Qt4.Q_Real
              := Y_0 + Vel_Y * Time - 0.5 * Gravity * Time * Time;
         begin
            Result.Move_Center
             (Qt4.Points.Create
               (Qt4.Q_Integer (Qt4.Q_Real'Rounding (X)),
                Qt4.Widgets.Height (Self)
                  - 1 - Qt4.Q_Integer (Qt4.Q_Real'Rounding (Y))));
         end;
      end return;
   end Shot_Rect;

   -----------------
   -- Target_Rect --
   -----------------

   function Target_Rect (Self : not null access Cannon_Field'Class)
     return Qt4.Rects.Q_Rect
   is
   begin
      return Result : Qt4.Rects.Q_Rect := Qt4.Rects.Create (0, 0, 20, 10) do
         Result.Move_Center
          (Qt4.Points.Create
            (Self.Target.X,
             Qt4.Widgets.Height (Self) - 1 - Self.Target.Y));
      end return;
   end Target_Rect;

end Cannon_Fields;
