
with Interfaces.C.Strings;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Characters.Handling;
with Ada.Command_Line;
with GNAT.OS_Lib;
with SDL.Video;
with SDL.Events;
with SDL.Error;
with SDL.Joystick;
with SDL.Types; use SDL.Types;
with SDL.Keysym;

procedure TestJoystick is
   package C  renames Interfaces.C;
   use type C.int;
   use type C.unsigned;
   package CS renames Interfaces.C.Strings;
   use type CS.chars_ptr;
   package CH renames Ada.Characters.Handling;
   package CL renames Ada.Command_Line;
   use type SDL.Init_Flags;
   package V  renames SDL.Video;
   use type V.Surface_ptr;
   package Ev renames SDL.Events;
   package Er renames SDL.Error;
   package Jy renames SDL.Joystick;
   use type Jy.HAT_State;
   use type Jy.Joy_Button_State;
   use type Jy.Joystick_ptr;
   package Ks renames SDL.Keysym;
   use type Ks.Key;
  
   SCREEN_WIDTH  : constant := 640;
   SCREEN_HEIGHT : constant := 480;
 
   --  ======================================
   procedure WatchJoystick (joystick : Jy.Joystick_ptr) is
      screen : V.Surface_ptr;
      name   : CS.chars_ptr;
      done   : Boolean;
      event  : Ev.Event;
      x, y   : C.int;
      draw   : C.unsigned range 0 .. 1;
      axis_area : V.Rects_Array (0 .. 1);
      PollEvent_Result : C.int;
   begin
      --  Set a video mode to display joystick axis position
      screen := V.SetVideoMode (SCREEN_WIDTH, SCREEN_HEIGHT, 16, 0);
      if screen = null then
         Put_Line ("couldn't set video mode: " & Er.Get_Error);
         return;
      end if;

      --  Print info about the joystick we are watching
      name := Jy.JoystickName (Jy.JoystickIndex (joystick));
      Put ("Watching joystick " & C.int'Image (Jy.JoystickIndex (joystick)) & ": (");
      if name /= CS.Null_Ptr then
         Put_Line (CS.Value (name) & ")");
      else
         Put_Line ("Unknown Joystick)");
      end if;
      Put_Line ("Joystick has " &
                C.int'Image (Jy.JoystickNumAxes (joystick))    & " axes, "      &
                C.int'Image (Jy.JoystickNumHats (joystick))    & " hats"        &
                C.int'Image (Jy.JoystickNumBalls (joystick))   & " balls, and " &
                C.int'Image (Jy.JoystickNumButtons (joystick)) & " buttons");
      
      --  Initialize drawing rectangles
      axis_area := (others => (0,0,0,0));
      draw := 0;

      --  Loop, getting joystick events
      done := False;
      while not done loop
         loop
            Ev.PollEventVP (PollEvent_Result, event);
            exit when PollEvent_Result =0;
            Put_Line ("DEBUG2");
            case event.the_type is
               when Ev.JOYAXISMOTION =>
                  Put_Line ("Joystick " &
                            Uint8'Image (event.jaxis.which) &
                            Uint8'Image (event.jaxis.axis)  &
                            Sint16'Image (event.jaxis.value));
               when Ev.JOYHATMOTION =>
                  Put_Line ("Joystick " &
                            Uint8'Image (event.jhat.which) &
                            Uint8'Image (event.jhat.hat));
                  if event.jhat.value = Jy.HAT_CENTERED then
                     Put (" centered");
                  end if;
                  if (event.jhat.value and Jy.HAT_UP)    /= 0  then
                     Put (" up");
                  end if;
                  if (event.jhat.value and Jy.HAT_RIGHT) /= 0  then
                     Put (" right");
                  end if;
                  if (event.jhat.value and Jy.HAT_DOWN)  /= 0  then
                     Put (" down");
                  end if;
                  if (event.jhat.value and Jy.HAT_LEFT)  /= 0  then
                     Put (" left");
                  end if;
                  New_Line;
               when Ev.JOYBALLMOTION =>
                  Put_Line ("Joystick " &
                            Uint8'Image (event.jball.which) & " ball " &
                            Uint8'Image (event.jball.ball)  & " delta: (" &
                            Sint16'Image (event.jball.xrel) & "," &
                            Sint16'Image (event.jball.yrel) & ")");
               when Ev.JOYBUTTONDOWN =>
                  Put_Line ("Joystick " &
                            Uint8'Image (event.jbutton.which)  & " button " &
                            Uint8'Image (event.jbutton.button) & " down");
               when Ev.JOYBUTTONUP =>
                  Put_Line ("Joystick " &
                            Uint8'Image (event.jbutton.which)  & " button " &
                            Uint8'Image (event.jbutton.button) & " up");
               when Ev.KEYDOWN =>
                  if event.key.keysym.sym = Ks.K_ESCAPE then
                     done := True;
                  end if;
               when Ev.QUIT =>
                  done := True;
               when others => null;
            end case;
         end loop;
         --  Update visual joystick state
         for i in C.int range  0 .. Jy.JoystickNumButtons (joystick) - 1 loop
            declare
               area : V.Rects_Array (0..0);
            begin
               area (0) := (Sint16 (i * 34), SCREEN_HEIGHT - 34, 32, 32);
               if Jy.JoystickGetButton (joystick, i) = Jy.PRESSED then
                  V.FillRect (screen, area (0), 16#FFFF#);
               else
                  V.FillRect (screen, area (0), 16#0000#);
               end if;
               V.UpdateRects (screen, area'Length, area);
            end;
         end loop;
         
         --  Erase previous axes
         V.FillRect (screen, axis_area (draw), 16#0000#);

         --  Draw the X/Y axis
         draw := 1 - draw;
         x := C.int (Jy.JoystickGetAxis (joystick, 0)) + 32768;
         x := x * SCREEN_WIDTH;
         x := x / 65535;
         if x < 0 then
            x := 0;
         elsif x > SCREEN_WIDTH - 16 then
            x := SCREEN_WIDTH - 16;
         end if;
         y := C.int (Jy.JoystickGetAxis (joystick, 1)) + 32768;
         y := y / 65535;
         if y < 0 then
            y := 0;
         elsif y > SCREEN_HEIGHT - 16 then
            y := SCREEN_HEIGHT - 16;
         end if;
         axis_area (draw).x := Sint16 (x);
         axis_area (draw).y := Sint16 (y);
         axis_area (draw).w := 16;
         axis_area (draw).h := 16;
         V.FillRect (screen, axis_area (draw), 16#FFFF#);
         V.UpdateRects (screen, axis_area'Length, axis_area);
      end loop;
   end WatchJoystick;
   --  ======================================
   name : CS.chars_ptr;
   joystick : Jy.Joystick_ptr;
   argv_1 : C.int;

begin
   --  Initialize SDL (Note: video is required to start event loop)
   if SDL.Init (SDL.INIT_VIDEO or SDL.INIT_JOYSTICK) < 0 then
      Put_Line ("Could't initialize SDL: " & Er.Get_Error);
      GNAT.OS_Lib.OS_Exit (1);
   end if;
  
 
   --  Print information about the joysticks
   Put_Line ("There are " & C.int'Image (Jy.NumJoysticks) & " joysticks attached");
   for i in C.int range 0 .. Jy.NumJoysticks - 1 loop
      name := Jy.JoystickName (i);
      Put ("Joystick " & C.int'Image (i) & ": ");
      if name /= CS.Null_Ptr then
         Put_Line (CS.Value (name));
      else
         Put_Line ("Unknown Joystick");
      end if;
   end loop;
 
   if CL.Argument_Count > 0 and then
         CH.Is_Digit (CL.Argument (1) (1)) then
      declare
         package int_IO is new Ada.Text_IO.Integer_IO (C.int);
         use int_IO;
         last : Positive;
      begin
         Get (CL.Argument (1), argv_1, last);
      end;
      
      joystick := Jy.JoystickOpen (argv_1);
      if joystick = Jy.Null_Joystick_ptr then
         Put_Line ("Couldn't open joystick " & C.int'Image (argv_1) &
                   ": " & Er.Get_Error);
      else
         WatchJoystick (joystick);
         Jy.JoystickClose (joystick);
      end if;
   end if;

   SDL.QuitSubSystem (SDL.INIT_VIDEO or SDL.INIT_JOYSTICK);

   GNAT.OS_Lib.OS_Exit (0);

end TestJoystick;
