with Glib;              use Glib;
with Double_Buffer;     use Double_Buffer;
with Gdk.Color;         use Gdk.Color;
with Gdk.Drawable;      use Gdk.Drawable;
with Gdk.Threads;       use Gdk.Threads;
with Gtk.Drawing_Area;  use Gtk.Drawing_Area;
with Gdk.GC;            use Gdk.GC;
with Gtk.Window;        use Gtk.Window;
with Gtk.Enums;         use Gtk.Enums;
with Gtk.Box;           use Gtk.Box;
with Gtk.Label;         use Gtk.Label;
with Gtk.Widget;        use Gtk.Widget;
with Gtk.Main;          use Gtk.Main;
with Gtk.Handlers;      use Gtk.Handlers;
pragma Elaborate_All (Gtk.Handlers);

with Ada.Text_IO;       use Ada.Text_IO;

package body Anim_Task is

   White_Gc : Gdk.GC.Gdk_GC;
   Black_Gc : Gdk.GC.Gdk_GC;

   X_Pos : Gint := 10;

   package Void_Cb is new Gtk.Handlers.Callback (Gtk_Window_Record);

   Abort_Animations_Tasks : Boolean := False;
   pragma Volatile (Abort_Animations_Tasks);
   --  This variable should be set to true when we want to abort all the
   --  animation tasks. We could of course use a rendez-vous ...

   ------------------
   -- Draw_Complex --
   ------------------

   procedure Draw_Complex (Pixmap : Gdk_Drawable) is
   begin
      Draw_Rectangle (Pixmap, White_Gc, Filled => True,
                      X     => 0,   Y      => 0,
                      Width => 400, Height => 400);

      for J in Gint'(1) .. 30 loop
         Draw_Rectangle (Pixmap, Black_Gc, Filled => False,
                         X     => X_Pos,  Y      => 30 + J * 2,
                         Width => X_Pos + 100, Height => 100);
         Draw_Rectangle (Pixmap, Black_Gc, Filled => False,
                         X     => X_Pos + 20,  Y      => 60 + J * 2,
                         Width => X_Pos + 60, Height => 80);
         Draw_Rectangle (Pixmap, Black_Gc, Filled => False,
                         X     => X_Pos + 30,  Y      => 50 + J * 2,
                         Width => X_Pos + 80, Height => 90);
         Draw_Rectangle (Pixmap, Black_Gc, Filled => False,
                         X     => X_Pos - 20,  Y      => 120 + J * 2,
                         Width => X_Pos + 80, Height => 190);
      end loop;

      X_Pos := (X_Pos + 1) mod 140;
   end Draw_Complex;

   ----------
   -- Quit --
   ----------

   procedure Quit (Win : access Gtk_Window_Record'Class) is
      pragma Warnings (Off, Win);
   begin
      Abort_Animations_Tasks := True;
      Gtk.Main.Gtk_Exit (0);
   end Quit;

   -------------
   -- Animate --
   -------------

   procedure Animate
     (Buffer : Gtk_Double_Buffer;
      Area   : Gtk_Drawing_Area)
   is
      task Double_Buffer_Task;
      task Area_Task;

      task body Double_Buffer_Task is
      begin
         while not Abort_Animations_Tasks loop
            Gdk.Threads.Enter;
            Draw_Complex (Get_Pixmap (Buffer));
            Draw (Buffer);
            Gdk.Threads.Leave;
            delay 0.01;
         end loop;
      end Double_Buffer_Task;

      task body Area_Task is
      begin
         while not Abort_Animations_Tasks loop
            Gdk.Threads.Enter;
            Draw_Complex (Get_Window (Area));
            Gdk.Threads.Leave;
            delay 0.01;
         end loop;
      end Area_Task;

   begin
      Gdk.Threads.Enter;
      Gtk.Main.Main;
      Gdk.Threads.Leave;
   end Animate;

   ----------
   -- Init --
   ----------

   procedure Init is
      Win : Gtk_Window;
      Area   : Gtk_Drawing_Area;
      Buffer : Gtk_Double_Buffer;
      Vbox,
      Hbox : Gtk_Box;
      Label : Gtk_Label;

   begin
      Put_Line ("This demo shows how you can use a Double_Buffer widget");
      Put_Line (" to provide flicker-free animations in your applications.");
      Put_Line ("The code is almost the same as with a Gtk_Drawing_Area.");
      Put_Line (" (the drawing routines are exactly the same in this demo)");
      Put_Line (" except that in one case you draw in an off-screen pixmap");
      Put_Line (" That you need to copy to the screen when you are ready.");
      New_Line;
      Put_Line ("The animation is done thanks to two Ada95 tasks");

      --  This demo uses multi-tasking
      Gdk.Threads.Init;

      --  Double buffer demo

      Gtk_New (Win, Window_Toplevel);
      Set_Title (Win, "Animation demo");
      Void_Cb.Connect (Win, "destroy", Void_Cb.To_Marshaller (Quit'Access));

      Gtk_New_Hbox (Hbox, Homogeneous => True, Spacing => 10);

      Gtk_New_Vbox (Vbox, Homogeneous => False, Spacing => 20);
      Gtk_New (Label, "With double-buffering");
      Gtk_New (Buffer);
      Set_USize (Buffer, 200, 200);
      Pack_Start (Vbox, Label, Expand => False);
      Pack_Start (Vbox, Buffer);
      Pack_Start (Hbox, Vbox);

      Gtk_New_Vbox (Vbox, Homogeneous => False, Spacing => 20);
      Gtk_New (Label, "No double-buffering");
      Gtk_New (Area);
      Set_USize (Area, 200, 200);
      Pack_Start (Vbox, Label, Expand => False);
      Pack_Start (Vbox, Area);
      Pack_Start (Hbox, Vbox);

      Add (Win, Hbox);

      Show_All (Win);

      --  The window needs to be created before creating the GCs
      Gdk.GC.Gdk_New (White_Gc, Get_Window (Buffer));
      Gdk.GC.Set_Foreground
        (White_Gc, Gdk.Color.White (Gtk.Widget.Get_Default_Colormap));

      Gdk.GC.Gdk_New (Black_Gc, Get_Window (Buffer));
      Gdk.GC.Set_Foreground
        (Black_Gc, Gdk.Color.Black (Gtk.Widget.Get_Default_Colormap));

      Animate (Buffer, Area);
   end Init;

end Anim_Task;
