------------------------------------------------------------------------------
--                                                                          --
--                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: 3244 $ $Date: 2009-04-15 12:30:41 +0400 (Wed, 15 Apr 2009) $
------------------------------------------------------------------------------
with Qt4.Brushes;
with Qt4.Colors;
with Qt4.Line_Fs;
with Qt4.List_Of_Graphics_Items;
with Qt4.Pens;
with Qt4.Radial_Gradients.Constructors;
with Qt4.Styles;

with Qt4.Graphics_Scenes;
pragma Unreferenced (Qt4.Graphics_Scenes);

package body Nodes.Impl is

   use type Qt4.Q_Real;
   use type Qt4.Q_Integer;
   use type Qt4.Point_Fs.Q_Point_F;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Add_Edge
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   procedure Add_Edge (Self : not null access Node_Impl;
                       Edge : not null access Edges.Edge'Class)
   is
   begin
      Self.Edge_List.Append (Edge_Access (Edge));
      Edge.Adjust_1;
   end Add_Edge;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Advance
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Advance (Self : not null access Node_Impl) return Boolean is
   begin
      if Self.New_Pos = Qt4.Graphics_Items.Pos (Self) then
         return False;
      end if;

      Qt4.Graphics_Items.Set_Pos (Self, Self.New_Pos);

      return True;
   end Advance;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Bounding_Rect
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Bounding_Rect (Self : not null access constant Node_Impl)
     return Qt4.Rect_Fs.Q_Rect_F
   is
      pragma Unreferenced (Self);
      --  Параметр не используется и нужен для диспетчеризации вызова.

      Adjust : constant Qt4.Q_Real := 2.0;

   begin
      return
        Qt4.Rect_Fs.Create
         (-10.0 - Adjust, -10.0 - Adjust, 23.0 + Adjust, 23.0 + Adjust);
   end Bounding_Rect;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Calculate_Forces
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   procedure Calculate_Forces (Self : not null access Node_Impl) is
      use Edge_List_Package;

   begin
      if Qt4.Graphics_Items.Scene (Self) = null
        or Qt4.Graphics_Items.Scene (Self).Mouse_Grabber_Item = Self
      then
         Self.New_Pos := Qt4.Graphics_Items.Pos (Self);

         return;
      end if;

      declare
         X_Vel : Qt4.Q_Real := 0.0;
         Y_Vel : Qt4.Q_Real := 0.0;

      begin
         declare
            List :
              constant Qt4.List_Of_Graphics_Items.Q_List_Of_Q_Graphics_Item
                := Qt4.Graphics_Items.Scene (Self).Items;

         begin
            for J in 0 .. List.Count - 1 loop
               if List.Value (J).all in Node'Class then
                  declare
                     N : constant not null access Node'Class
                       := Node'Class (List.Value (J).all)'Access;

                     Line : constant Qt4.Line_Fs.Q_Line_F
                       := Qt4.Line_Fs.Create
                           (Qt4.Graphics_Items.Map_From_Item
                             (Self, N, 0.0, 0.0),
                            Qt4.Point_Fs.Create (0.0, 0.0));
                     Dx   : constant Qt4.Q_Real := Line.Dx;
                     Dy   : constant Qt4.Q_Real := Line.Dy;
                     L    : constant Qt4.Q_Real := 2.0 * (Dx * Dx + Dy * Dy);

                  begin
                     if L > 0.0 then
                        X_Vel := X_Vel + (Dx * 150.0) / L;
                        Y_Vel := Y_Vel + (Dy * 150.0) / L;
                     end if;
                 end;
               end if;
            end loop;
         end;

         declare
            Weight : constant Qt4.Q_Real
              := (Qt4.Q_Real (Self.Edge_List.Length) + 1.0) * 10.0;
            C      : Edge_List_Package.Cursor := Self.Edge_List.First;

         begin
            while C /= No_Element loop
               declare
                  Pos : Qt4.Point_Fs.Q_Point_F;
                  E   : constant Edge_Access := Element (C);

               begin
                  --  if E.Source_Node = Self then
                  --  XXX GNAT Pro 6.1.0w 20070520 некорректно производит
                  --  операцию сравнения.

                  if E.Source_Node = Nodes.Node'Class (Self.all)'Access then
                     Pos := Qt4.Graphics_Items.Map_From_Item
                             (Self, E.Dest_Node, 0.0, 0.0);

                  else
                     Pos := Qt4.Graphics_Items.Map_From_Item
                             (Self, E.Source_Node, 0.0, 0.0);
                  end if;

                  X_Vel := X_Vel + Pos.X / Weight;
                  Y_Vel := Y_Vel + Pos.Y / Weight;
               end;

               Next (C);
            end loop;
         end;

         if abs X_Vel < 0.1 and abs Y_Vel < 0.1 then
            X_Vel := 0.0;
            Y_Vel := 0.0;
         end if;

         declare
            Scene_Rect : constant Qt4.Rect_Fs.Q_Rect_F
               := Qt4.Graphics_Items.Scene (Self).Scene_Rect;

            P_X : Qt4.Q_Real;
            P_Y : Qt4.Q_Real;

         begin
            Self.New_Pos := Qt4.Graphics_Items.Pos (Self) +
                              Qt4.Point_Fs.Create (X_Vel, Y_Vel);

            P_X :=
              Qt4.Q_Real'Min
               (Qt4.Q_Real'Max (Self.New_Pos.X, Scene_Rect.Left + 10.0),
                Scene_Rect.Right - 10.0);
            Self.New_Pos.Set_X (P_X);

            P_Y :=
              Qt4.Q_Real'Min
               (Qt4.Q_Real'Max (Self.New_Pos.Y, Scene_Rect.Top + 10.0),
                Scene_Rect.Bottom - 10.0);
            Self.New_Pos.Set_Y (P_Y);
         end;
      end;
   end Calculate_Forces;

   -----------------
   -- Item_Change --
   -----------------

   overriding
   function Item_Change
    (Self   : not null access Node_Impl;
     Change : Qt4.Graphics_Items.Graphics_Item_Graphics_Item_Change;
     Value  : Qt4.Variants.Q_Variant)
       return Qt4.Variants.Q_Variant
   is
      use Edge_List_Package;

      C : Edge_List_Package.Cursor := Self.Edge_List.First;

   begin
      case Change is
         when Qt4.Graphics_Items.Item_Position_Change =>
            while C /= No_Element loop
               Element (C).Adjust_1;
               Next (C);
            end loop;

            Self.Graph.Item_Moved;

         when others =>
            null;
      end case;

      return
        Qt4.Graphics_Items.Directors.Q_Graphics_Item_Director
         (Self.all).Item_Change (Change, Value);
   end Item_Change;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Item_Type
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Item_Type (Self : not null access Node_Impl)
     return Qt4.Graphics_Items.Graphics_Item_Type
   is
      pragma Unreferenced (Self);
      --  Параметр не используется и нужен для диспетчеризации вызова.

   begin
      return Nodes.Graphics_Node_Item;
   end Item_Type;

   -----------------------
   -- Mouse_Press_Event --
   -----------------------

   overriding
   procedure Mouse_Press_Event
    (Self : not null access Node_Impl;
     Event : not null access
       Qt4.Graphics_Scene_Mouse_Events.Q_Graphics_Scene_Mouse_Event'Class)
   is
   begin
      Qt4.Graphics_Items.Update (Self);
      Qt4.Graphics_Items.Directors.Q_Graphics_Item_Director
       (Self.all).Mouse_Press_Event (Event);
   end Mouse_Press_Event;

   -------------------------
   -- Mouse_Release_Event --
   -------------------------

   overriding
   procedure Mouse_Release_Event
    (Self : not null access Node_Impl;
     Event : not null access
       Qt4.Graphics_Scene_Mouse_Events.Q_Graphics_Scene_Mouse_Event'Class)
   is
   begin
      Qt4.Graphics_Items.Update (Self);
      Qt4.Graphics_Items.Directors.Q_Graphics_Item_Director
       (Self.all).Mouse_Release_Event (Event);
   end Mouse_Release_Event;

   -----------
   -- Paint --
   -----------

   overriding
   procedure Paint
    (Self    : not null access Node_Impl;
     Painter : in out Qt4.Painters.Q_Painter'Class;
     Option  :
       Qt4.Style_Option_Graphics_Items.Q_Style_Option_Graphics_Item'Class;
     Widget  : access Qt4.Widgets.Q_Widget'Class := null)
   is
      pragma Unreferenced (Self);
      pragma Unreferenced (Widget);
      --  Параметры не используются.

      use type Qt4.Styles.State;

      Gradient : constant Qt4.Radial_Gradients.Q_Radial_Gradient
        := Qt4.Radial_Gradients.Constructors.Create (-3.0, -3.0, 10.0);

   begin
      Painter.Set_Pen (Qt4.No_Pen);
      Painter.Set_Brush (Qt4.Brushes.Create (Qt4.Dark_Gray));
      Painter.Draw_Ellipse (-7, -7, 20, 20);

      if Option.State + Qt4.Styles.State_Sunken /= Qt4.Styles.State_None then
         Gradient.Set_Center (3.0, 3.0);
         Gradient.Set_Focal_Point (3.0, 3.0);
         Gradient.Set_Color_At
          (0.0, Qt4.Colors.Create (Qt4.Yellow).Lighter (120));
         Gradient.Set_Color_At
          (1.0, Qt4.Colors.Create (Qt4.Dark_Yellow).Lighter (120));

      else
         Gradient.Set_Color_At (0.0, Qt4.Colors.Create (Qt4.Yellow));
         Gradient.Set_Color_At (1.0, Qt4.Colors.Create (Qt4.Dark_Yellow));
      end if;

      Painter.Set_Brush (Qt4.Brushes.Create (Gradient));
      Painter.Set_Pen
       (Qt4.Pens.Create (Qt4.Brushes.Create (Qt4.Black), 0.0));
      Painter.Draw_Ellipse (-10, -10, 20, 20);
   end Paint;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Shape
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Shape (Self : not null access constant Node_Impl)
     return Qt4.Painter_Paths.Q_Painter_Path
   is
      pragma Unreferenced (Self);
      --  Параметр не используется и нужен для диспетчеризации вызова.

   begin
      return Result : Qt4.Painter_Paths.Q_Painter_Path do
         Result.Add_Ellipse (-10.0, -10.0, 20.0, 20.0);
      end return;
   end Shape;

end Nodes.Impl;
