------------------------------------------------------------------------------
--               GtkAda - Ada95 binding for the Gimp Toolkit                --
--                                                                          --
--                     Copyright (C) 2001-2017, AdaCore                     --
--                                                                          --
-- This library is free software;  you can redistribute it and/or modify it --
-- under terms of the  GNU General Public License  as published by the Free --
-- Software  Foundation;  either version 3,  or (at your  option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE.                            --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
------------------------------------------------------------------------------

with Gdk.Color;             use Gdk.Color;
with Gdk.GC;                use Gdk.GC;
with Gdk.Rectangle;         use Gdk.Rectangle;
with Glib;                  use Glib;
with Gtk.Enums;             use Gtk.Enums;
with Gtk.Extra.Plot;        use Gtk.Extra.Plot;
with Gtk.Extra.Plot_Data;   use Gtk.Extra.Plot_Data;
with Gtk.Extra.Plot_Canvas; use Gtk.Extra.Plot_Canvas;
with Gtk.Extra.Plot_Canvas.Plot; use Gtk.Extra.Plot_Canvas.Plot;
with Gtk.Extra.Plot_Canvas.Text; use Gtk.Extra.Plot_Canvas.Text;
with Gtk.Frame;             use Gtk.Frame;
with Glib.Main;             use Glib.Main;
with Gtk.Scrolled_Window;   use Gtk.Scrolled_Window;
with Gtk.Widget;            use Gtk.Widget;
with Gtkada.Handlers;       use Gtkada.Handlers;
with Unchecked_Deallocation;
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;

package body Create_Plot_Realtime is

   Timer : G_Source_Id;
   Random_Generator : Ada.Numerics.Float_Random.Generator;

   type Timeout_Data is record
      Canvas  : Gtk_Plot_Canvas;
      Plot    : Gtk_Plot;
      Dataset : Gtk_Plot_Data;
      X       : Gdouble_Array_Access;
      Y       : Gdouble_Array_Access;
   end record;
   type Timeout_Data_Access is access Timeout_Data;

   package Plot_Timeout is new Glib.Main.Generic_Sources (Timeout_Data_Access);
   use Plot_Timeout;

   procedure Free is new Unchecked_Deallocation
     (Gdouble_Array, Gdouble_Array_Access);

   procedure Destroy (Widget : access Gtk_Widget_Record'Class);
   --  Called when the widget is destroyed, removes the timeout

   function Update (Data : Timeout_Data_Access) return Boolean;
   --  Update the contents of the widget periodically

   ----------
   -- Help --
   ----------

   function Help return String is
   begin
      return "This demo shows how a plot can be updated periodically to"
        & " show new data.";
   end Help;

   -------------
   -- Destroy --
   -------------

   procedure Destroy (Widget : access Gtk_Widget_Record'Class) is
      pragma Warnings (Off, Widget);
   begin
      Remove (Timer);
   end Destroy;

   ------------
   -- Update --
   ------------

   function Update (Data : Timeout_Data_Access) return Boolean is
      X, Y : Gdouble_Array_Access;
      Xmin, Xmax : Gdouble;
      Length : Natural := 1;
   begin
      if Data.X /= null then
         Length := Data.X'Length + 1;
      end if;

      X := new Gdouble_Array (1 .. Length);
      Y := new Gdouble_Array (1 .. Length);

      if Length /= 1 then
         X (1 .. Length - 1) := Data.X.all;
         Y (1 .. Length - 1) := Data.Y.all;
         X (Length) := X (Length - 1) + 1.0;
      else
         X (Length) := 1.0;
      end if;

      Y (Length) := Gdouble (Random (Random_Generator));

      Free (Data.X);
      Free (Data.Y);

      Data.X := X;
      Data.Y := Y;

      Set_Numpoints (Data.Dataset, Gint (Length));
      Set_X (Data.Dataset, X);
      Set_Y (Data.Dataset, Y);

      Get_Xrange (Data.Plot, Xmin, Xmax);
      if X (Length) > Xmax then
         Set_Xrange (Data.Plot, Xmin + 5.0, Xmax + 5.0);
         Paint (Data.Canvas);
         Queue_Draw (Data.Canvas);

      else
         --  Draw only the last point added
         Draw_Points (Data.Dataset, 1);
         Refresh (Data.Plot, Full_Area);
      end if;
      return True;
   end Update;

   ---------
   -- Run --
   ---------

   procedure Run (Frame : access Gtk.Frame.Gtk_Frame_Record'Class) is
      Scrollw1    : Gtk_Scrolled_Window;
      Canvas      : Gtk_Plot_Canvas;
      Color       : Gdk_Color;
      Active_Plot : Gtk_Plot;
      Dataset     : Gtk_Plot_Data;
      Plot_Child  : Gtk_Plot_Canvas_Plot;
      Text_Child  : Gtk_Plot_Canvas_Text;
   begin
      Set_Label (Frame, "gtk_plot realtime");

      --  Put the Plot in a scrolled window, in case it is too big for
      --  the screen
      Gtk_New (Scrollw1);
      Set_Border_Width (Scrollw1, 0);
      Set_Policy (Scrollw1, Policy_Always, Policy_Always);
      Add (Frame, Scrollw1);

      --  Create the canvas in which the plot will be drawn
      Gtk_New (Canvas, Gint (Get_Allocation_Width (Frame) - 10),
               Gint (Get_Allocation_Height (Frame) - 10), 1.0);
      Plot_Canvas_Unset_Flags (Canvas, Dnd_Flags);
      Add_With_Viewport (Scrollw1, Canvas);

      Widget_Callback.Connect
        (Canvas, "destroy", Widget_Callback.To_Marshaller (Destroy'Access));

      Color := Parse ("light blue");
      Alloc (Get_Default_Colormap, Color);
      Set_Background (Canvas, Color);
      Show (Canvas);

      --  Create the plot, the axis and legends
      Gtk_New (Active_Plot, 0.65, 0.45);
      Color := Parse ("light yellow");
      Alloc (Get_Default_Colormap, Color);
      Set_Background (Active_Plot, Color);

      Color := Parse ("white");
      Alloc (Get_Default_Colormap, Color);
      Set_Legends_Border (Active_Plot, Border_Shadow, 3);
      Legends_Move (Active_Plot, 0.6, 0.1);
      Set_Range (Active_Plot, 0.0, 20.0, 0.0, 1.0);

      Set_Ticks (Active_Plot, Axis_X, 2.0, 1);
      Set_Ticks (Active_Plot, Axis_Y, 0.1, 1);
      Axis_Set_Visible (Get_Axis (Active_Plot, Axis_Top), True);
      Axis_Set_Visible (Get_Axis (Active_Plot, Axis_Right), True);
      Grids_Set_Visible (Active_Plot, True, True, True, True);
      Axis_Hide_Title (Get_Axis (Active_Plot, Axis_Top));
      Axis_Hide_Title (Get_Axis (Active_Plot, Axis_Right));
      Axis_Set_Title (Get_Axis (Active_Plot, Axis_Left), "Intensity");
      Axis_Set_Title (Get_Axis (Active_Plot, Axis_Bottom), "Time");
      Grids_Set_Visible (Active_Plot, True, True, True, True);

      Gtk_New (Plot_Child, Active_Plot);
      Put_Child (Canvas, Plot_Child, 0.15, 0.15, 0.9, 0.9);
      Show (Active_Plot);

      Gtk_New (Text_Child,
               Font          => "Times-BoldItalic",
               Height        => 10,
               Text          => "Real Time Demo");
      Put_Child (Canvas, Text_Child, 0.45, 0.05, 0.85, 0.9);

      --  Create the data

      Gtk_New (Dataset);
      Add_Data (Active_Plot, Dataset);
      Show (Dataset);

      Color := Parse ("red");
      Alloc (Get_Default_Colormap, Color);

      Set_Legend (Dataset, "Random Pulse");
      Set_Symbol (Dataset,
                  Symbol_Diamond,
                  Symbol_Opaque,
                  10, 2.0, Color, Color);
      Set_Line_Attributes
        (Dataset, Line_Solid, Cap_Not_Last, Join_Miter, 1.0, Color);

      --  Paint everything
      Paint (Canvas);
      Refresh (Canvas);

      --  Register the periodic timer

      Timer := Plot_Timeout.Timeout_Add
        (300,
         Update'Access,
         new Timeout_Data'(Canvas  => Canvas,
                           Plot    => Active_Plot,
                           Dataset => Dataset,
                           X       => null,
                           Y       => null));
      Show_All (Frame);
   end Run;

end Create_Plot_Realtime;
