------------------------------------------------------------------------------
--                                                                          --
--                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: 3394 $ $Date: 2009-06-07 17:22:41 +0400 (Sun, 07 Jun 2009) $
------------------------------------------------------------------------------
with Ada.Numerics.Generic_Elementary_Functions;

with Qt4.Brushes;
with Qt4.Line_Fs;
with Qt4.Pens;
with Qt4.Polygon_Fs;
with Qt4.Size_Fs;

package body Edges.Impl is

   use type Qt4.Q_Real;
   use type Qt4.Point_Fs.Q_Point_F;
   use type Nodes.Node_Access;

   package Q_Real_Elementary_Functions is
     new Ada.Numerics.Generic_Elementary_Functions (Qt4.Q_Real);

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Adjust
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   procedure Adjust_1 (Self : not null access Edge_Impl) is
   begin
      if Self.Source = null or Self.Dest = null then
         return;
      end if;

      declare
         P1          : constant Qt4.Point_Fs.Q_Point_F
           := Qt4.Graphics_Items.Map_From_Item (Self, Self.Source, 0.0, 0.0);
         P2          : constant Qt4.Point_Fs.Q_Point_F
           := Qt4.Graphics_Items.Map_From_Item (Self, Self.Dest, 0.0, 0.0);

         Line        : constant Qt4.Line_Fs.Q_Line_F
           := Qt4.Line_Fs.Create (P1, P2);

         Length      : constant Qt4.Q_Real := Line.Length;
         Edge_Offset : constant Qt4.Point_Fs.Q_Point_F
           := Qt4.Point_Fs.Create
               ((Line.Dx * 10.0) / Length, (Line.Dy * 10.0) / Length);

      begin
         Self.Prepare_Geometry_Change;
         Self.Source_Point := Line.P_1 + Edge_Offset;
         Self.Dest_Point := Line.P_2 - Edge_Offset;
      end;
   end Adjust_1;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Bounding_Rect
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Bounding_Rect (Self : not null access constant Edge_Impl)
     return Qt4.Rect_Fs.Q_Rect_F
   is
      Result    : Qt4.Rect_Fs.Q_Rect_F;
      Pen_Width : constant Qt4.Q_Real := 1.0;
      Extra     : constant Qt4.Q_Real := (Pen_Width + Self.Arrow_Size) / 2.0;
      Tmp_Rect  : Qt4.Rect_Fs.Q_Rect_F;

   begin
      if Self.Source = null or Self.Dest = null then
         return Result;
      end if;

      Tmp_Rect := Qt4.Rect_Fs.Create
                   (Self.Source_Point,
                    Qt4.Size_Fs.Create
                     (Self.Dest_Point.X - Self.Source_Point.X,
                      Self.Dest_Point.Y - Self.Source_Point.Y));

      return Tmp_Rect.Normalized.Adjusted (-Extra, -Extra, Extra, Extra);
   end Bounding_Rect;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Dest_Node
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Dest_Node (Self : not null access Edge_Impl)
     return access Nodes.Node'Class
   is
   begin
      return Self.Dest;
   end Dest_Node;

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

   begin
      return Edges.Graphics_Edge_Item;
   end Item_Type;

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

   overriding
   procedure Paint
    (Self    : not null access Edge_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 (Option);
      pragma Unreferenced (Widget);
      --  Параметры навязаны профилем операции но не используются.

      use Q_Real_Elementary_Functions;

   begin
      if Self.Source = null or Self.Dest = null then
         return;
      end if;

      --  Draw the line itself

      declare
         Line  : constant Qt4.Line_Fs.Q_Line_F
           := Qt4.Line_Fs.Create (Self.Source_Point, Self.Dest_Point);
         Angle : Qt4.Q_Real := Arccos (Line.Dx / Line.Length);

         Source_Arrow_P1 : Qt4.Point_Fs.Q_Point_F;
         Source_Arrow_P2 : Qt4.Point_Fs.Q_Point_F;
         Dest_Arrow_P1   : Qt4.Point_Fs.Q_Point_F;
         Dest_Arrow_P2   : Qt4.Point_Fs.Q_Point_F;
         Polygon1        : Qt4.Polygon_Fs.Q_Polygon_F;
         Polygon2        : Qt4.Polygon_Fs.Q_Polygon_F;
         Brush           : constant Qt4.Brushes.Q_Brush
           := Qt4.Brushes.Create (Qt4.Black);

      begin
         Painter.Set_Pen
          (Qt4.Pens.Create
            (Brush, 1.0, Qt4.Solid_Line, Qt4.Round_Cap, Qt4.Round_Join));
         Painter.Draw_Line (Line);

         if Line.Dy >= 0.0 then
            Angle := 2.0 * Ada.Numerics.Pi - Angle;
         end if;

         Source_Arrow_P1
           := Self.Source_Point +
                Qt4.Point_Fs.Create
                 (Sin (Angle + Ada.Numerics.Pi / 3.0) * Self.Arrow_Size,
                  Cos (Angle + Ada.Numerics.Pi / 3.0) * Self.Arrow_Size);

         Source_Arrow_P2
           := Self.Source_Point +
                Qt4.Point_Fs.Create
                 (Sin (Angle + Ada.Numerics.Pi - Ada.Numerics.Pi / 3.0)
                   * Self.Arrow_Size,
                  Cos (Angle + Ada.Numerics.Pi - Ada.Numerics.Pi / 3.0)
                   * Self.Arrow_Size);

         Dest_Arrow_P1
           := Self.Dest_Point +
                Qt4.Point_Fs.Create
                 (Sin (Angle - Ada.Numerics.Pi / 3.0) * Self.Arrow_Size,
                  Cos (Angle - Ada.Numerics.Pi / 3.0) * Self.Arrow_Size);

         Dest_Arrow_P2
           := Self.Dest_Point +
                Qt4.Point_Fs.Create
                 (Sin (Angle - Ada.Numerics.Pi + Ada.Numerics.Pi / 3.0)
                   * Self.Arrow_Size,
                  Cos (Angle - Ada.Numerics.Pi + Ada.Numerics.Pi / 3.0)
                   * Self.Arrow_Size);

         Painter.Set_Brush (Brush);

         Polygon1.Append (Line.P_1);
         Polygon1.Append (Source_Arrow_P1);
         Polygon1.Append (Source_Arrow_P2);

         Painter.Draw_Polygon (Polygon1);

         Polygon2.Append (Line.P_2);
         Polygon2.Append (Dest_Arrow_P1);
         Polygon2.Append (Dest_Arrow_P2);

         Painter.Draw_Polygon (Polygon2);
      end;

   exception
      when Ada.Numerics.Argument_Error =>
         --  Qt 4.5: this exception is break application.

         null;
   end Paint;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit>
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   procedure Set_Dest_Node (Self : not null access Edge_Impl;
                            Node : not null        Nodes.Node_Access)
   is
   begin
      Self.Dest := Node;
      Self.Adjust_1;
   end Set_Dest_Node;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Set_Source_Node
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   procedure Set_Source_Node (Self : not null access Edge_Impl;
                              Node : not null Nodes.Node_Access)
   is
   begin
       Self.Source := Node;
       Self.Adjust_1;
   end Set_Source_Node;

   ---------------------------------------------------------------------------
   --! <Subprogram>
   --!    <Unit> Source_Node
   --!    <ImplementationNotes>
   ---------------------------------------------------------------------------
   overriding
   function Source_Node (Self : not null access Edge_Impl)
     return access Nodes.Node'Class
   is
   begin
      return Self.Source;
   end Source_Node;

end Edges.Impl;
