=head1 NAME

SDLx::Controller - Handles the loops for events, movement and rendering

=head1 CATEGORY

Extension, Controller

=head1 SYNOPSIS

 use SDLx::Controller;

 # create our controller object
 my $app = SDLx::Controller->new;

 # but we usually do:
 my $app = SDLx::App->new;
 # because App is also a controller

 # register some callbacks
 $app->add_event_handler( \&on_event );
 $app->add_move_handler( \&on_move );
 $app->add_show_handler( \&on_show );

 # run our game loop
 $app->run;

=head2 DESCRIPTION

The core of an SDL application/game is the main loop, where you handle events
and display your elements on the screen until something signals the end of
the program. This usually goes in the form of:

 while (1) {
     ...
 }

The problem most developers face, besides the repetitive work, is ensuring that
the screen update is independent of the frame rate. Otherwise, your game will
run at different speeds on different machines, which is never a good thing (old
MS-DOS games, anyone?).

One way to circumvent this is by capping the frame rate so it's the same no
matter what, but this is not the right way to do it as it penalizes better
hardware.

This module provides an industry-proven standard for frame independent
movement. It calls the movement handlers based on time (hi-res seconds) rather
than frame rate. You can add/remove these handlers and control your main loop with
ease. This module also provides methods for your other timing needs,
such as pausing the game.

=head1 METHODS

=head2 new

 SDLx::Controller->new(
     dt             => 0.05,
     min_t          => 0,
     max_t          => 1e9999,
     delay          => 1 / 200,
     event_handlers => [ @event_callbacks ],
     move_handlers  => [ @move_callbacks ],
     show_handlers  => [ @show_callbacks ],
     stop_handler   => \&stop_handler,
     event          => $event,
     time           => 99,
 );

Creates and returns a new controller object with the specified params.
All params are optional and have sane defaults.

=over

=item dt

The time, in seconds, of a full movement step. Defaults to 0.1.
In most cases, the C<dt> could be set to any number and the game could run almost identically
(except for floating-point precision differences). This is because B<the C<dt> is an enforcement of the maximum time
between calling movement handlers>. The actual time between calling move handlers may be much less, as the movement handlers
are called at least once per frame. The specifics of this are explained in L</add_move_handler>.

Usually you wouldn't need this value to be lower than the time it takes an average
computer to complete a cycle and render the frame. When you do need to run multiple move handlers per frame though,
such as if you were checking collision between fast-moving objects, you can set the C<dt> to some low value
(less than 1/60). Otherwise, leaving it at 0.1 is fine.

Regardless of whether you need to enforce a maximum time between move handlers, this system has its benefits.
Modifying the C<dt> without touching any of the other code in your program will result in a time-scaling effect.
If you lower the C<dt>, everything will move faster than it did with it set higher, and vice-versa.
This is useful to add slo-mo and fast-forward features to the game. All you would have to do is change the C<dt>.

=item min_t

The minimum time, in seconds, that has to accumulate before any move or show handlers are called. Defaults to 1 / 60.
A C<min_t> of 1 / 60 ensures that the controller can update the screen at a maximum of 60 times per second.
A "V-Sync" such as this is necessary to prevent video "tear", which occurs when the app is updating faster than the monitor
can display. Setting it to 0, as seen above, will not delay the loop at all.

=item max_t

The maximum time, in seconds, that the movement handlers are allowed to handle in a single cycle of the run loop.
If more time has passed since the last cycle, this time will not be handled and the game will slow down.
This is to protect against the slippery slope effect that happens when lag causes more movement handlers to be called
which, in turn, causes more lag. Setting C<max_t> to the same value as C<dt> guarantees that movement handlers will each
only be called once per frame. Defaults to 0.1, meaning the game will slow down if it is running at lower than 10 FPS.
Setting it to 1e9999 (or infinity), as seen above, will disable this action.

=item delay

The time, in seconds or milliseconds, to delay after every full L</run> loop. Defaults to 0.
If you specify a number greater than or equal to 1, it will be treated as milliseconds instead of seconds.
B<Note:> Picking an appropriate delay based on the needs of your game can greatly reduce CPU load.

=item event_handlers

=item move_handlers

=item show_handlers

An array ref of the corresponding handler callbacks. All default to [].
This is basically a shortcut way of adding handlers.
They would otherwise be added with their corresponding C<add_*_handler> method.
See below for a full explanation of the L</run> loop and handlers.

=item stop_handler

An extra, but separate, event callback to handle all L<stopping|/stop> of the app.
It is the same in almost every aspect to an event handler (see L</add_event_handler>): same received arguments,
called in the same place.

One difference is that it is called in L</pause> so that the app can be stopped while paused.
Another difference is that it should always apply to the app; while you add, remove and clear handlers,
it won't be touched. This is good, because you'd (probably) always want your app to able to be stopped.
Because of this, it's a good idea to use the stop handler regardless of whether you will be using L</pause>.

Defaults to C<\&SDLx::Controller::default_stop_handler>: a callback that L<stops|/stop> the event loop on an C<SDL_QUIT> event.
Specify a code ref to use a different callback to handle stopping, or a false value to not use a stop handler.
If you want to provide your own stop handler, you should give it the code of the default stop handler:

 my ($event, $self) = @_;
 $self->stop() if $event->type == SDL_QUIT;

followed by any other code to handle events also triggering the app to stop, such as the user pressing Esc.

=item event

The L<SDL::Event> object that events going to the event callbacks are L<polled|SDL::Events/poll_event> in to.
Defaults to C<< SDL::Event->new() >>.

=item time

The time, in seconds, that you want the L</run> loop to say it has been going for.
This has no effect on the run loop. All it will do is alter what L</time> returns. See L</time>.
Defaults to 0. You'll seldom have to set this param.

=back

=head2 run

 $app->run;

After creating and setting up your handlers (see below), call this method to enter the main loop.
This loop will run until L</stop> is called.

All added handlers will be called during the run loop, in this order:

=over

=item 1. Events

=item 2. Movements

=item 3. Displaying

=back

Please refer to each handler below for full information on what they do.
Note that the second argument every callback receives is the app object.

=head2 add_event_handler

 my $index = $app->add_event_handler(
     sub {
         my ($event, $app) = @_;
         # handle event ...
     }
 );

Adds a callback to the end of the event handler list.
You can add as many subs as you need.
For each L<SDL::Event> from the user, all registered callbacks will be called in order and supplied with it.
Returns the index of the added callback.

More specifically: events from the user will, one by one, be L<polled|SDL::Events/poll_event> into the app's L</event> object.
This event will then be passed to all of the registered callbacks as the first argument.
The second argument is the app.

Below is an example of an event handler that sets a variable to true when the left mouse button is pressed,
and back to false when it is lifted.

 our $click = 0;

 sub on_click {
     my ($event, $app) = @_;
     my $state =
         $event->type == SDL_MOUSEBUTTONDOWN ? 1 :
         $event->type == SDL_MOUSEBUTTONUP   ? 0 : undef
     ;
     return unless defined $state; # not a mouse click

     if($event->button_button == SDL_BUTTON_LEFT) {
         $click = $state;
     }
 }
 $app->add_event_handler(\&on_click);

For full details on the event object passed to the event handlers, see L<SDL::Event>.
For other event related functions and a full list of the event constants, see L<SDL::Events>.

=head2 add_move_handler

 my $index = $app->add_move_handler(
     sub {
         my ($step, $app, $time) = @_;
         # handle moving ...
     }
 );

Adds a callback to the end of the movement handler list.
You can add as many subs as you need.
All registered callbacks will be triggered in order for as many C<dt> as have happened between calls,
and once more for the remaining time less than C<dt>.
A reasonable C<dt> for a game will usually be a number greater than
the time you would ever expect to have passed between frames. This means that your movement handlers will ordinarily
only be called once per frame. See the discussion of C<dt> in L</new>.
Returns the index of the added callback.

The first argument passed to the callbacks is the fraction of C<dt> time that the move callback should handle.
This will be 1 for a full step and less than 1 for a partial step.
Inversely, the time that each move callback should handle is equal to the step argument multiplied by the C<dt>.
All movement values should be multiplied by the step value.

It is possible for the argument to be 0 if no time has passed since the last cycle.
It's best to protect against this by supplying the app a small L</delay> value.

The second argument passed to the callbacks is the app object.

The third is the value returned by L</time>. See L</time>.

You should use these handlers to update your in-game objects, check collisions, etc.
Below is an example of how you might move an object. Note that the movement value, a velocity in this case,
is multiplied by the step argument.

 our $ball = MyBall->new;

 sub move_ball {
     my ($step, $app, $t) = @_;
     $ball->move_x( $ball->x_vel * $step );
     $ball->move_y( $ball->y_vel * $step );
 }
 $app->add_move_handler(\&move_ball);

=head2 add_show_handler

 my $index = $app->add_show_handler(
     sub {
         my ($delta, $app) = @_;
         # handle showing ...
     }
 );

Adds a callback to the end of the rendering handler list.
You can add as many subs as you need.
All registered callbacks will be triggered in order, once per cycle of the L</run> loop.
Returns the index of the added callback.

The first argument passed is the time, in seconds, since the previous show.
This can be used to display a rough FPS value by dividing 1 by it.

The second argument is the app object.

 our $ball = MyBall->new;

 sub show_ball {
     my ($delta, $app) = @_;

     # the drawing below works if the app is an SDLx::App
     # and not just a controller
     $app->draw_circle_filled(
         [ $ball->x, $ball->y ], $ball->radius,
         $ball->color
     );
 }
 $app->add_show_handler(\&show_ball);

=head2 event_handlers

=head2 move_handlers

=head2 show_handlers

 my $event_handlers = $app->event_handlers;
 my $move_handlers = $app->move_handlers;
 my $show_handlers = $app->show_handlers;

Returns the corresponding array ref so that you can directly modify the handler list.

=head2 remove_event_handler

=head2 remove_move_handler

=head2 remove_show_handler

 my $removed_handler = $app->remove_event_handler( $index );
 my $removed_handler = $app->remove_event_handler( $callback );
 my $removed_handler = $app->remove_move_handler( $index );
 my $removed_handler = $app->remove_move_handler( $callback );
 my $removed_handler = $app->remove_show_handler( $index );
 my $removed_handler = $app->remove_show_handler( $callback );

Removes the handler with the given index from the respective handler list.

You can also pass a coderef.
The first coderef in the handler list that matches this will be removed.

Returns the removed handler.

=head2 remove_all_event_handlers

=head2 remove_all_move_handlers

=head2 remove_all_show_handlers

 $app->remove_all_event_handlers();
 $app->remove_all_move_handlers();
 $app->remove_all_show_handlers();

Removes all handlers from the respective handler list. None of these will remove the app's L</stop_handler>.

=head2 remove_all_handlers

 $app->remove_all_handlers();

Shortcut to remove all handlers at once. This will not remove the app's L</stop_handler>.

=head2 stop

 $app->stop;

Tells the controller to end the run loop. This only has meaning when called from within a
handler of the run loop. The L</run> loop will complete the current cycle
(handling events, moves and shows) and then return. This graceful way of ending the game loop
is preferred and it is the way the default L</stop_handler> does it.

Once the L</run> loop has been stopped, it can be started again without problems.
This technique should be used to do operations that take a long time outside of the timing of the app.

 $app->run;

 do_something_that_takes_a_long_time();

 $app->run;

This code snippet could be used to play the first part of a game, then load the next part
and resume playing. When the first run loop is stopped, the expensive operation will be executed.
Once that has completed, the second run loop will resume the game, ignoring the time that passed outside the run loop.
If the expensive operation was performed from within the run loop,
upon completing the operation the move handlers would take into account all the time passed.

=head2 stopped

 my $stopped = $app->stopped;

Returns true if the run loop is stopped (before and after being in the run loop).
Also returns true when the run loop is about to stop.
That is, true when the app will complete the current run cycle before stopping.

=head2 pause

 $app->pause(
     sub {
         my ($event, $app) = @_;
         # handle event ...

         return 1 if ... ; # unpause
         return; # stay paused
     }
 );

Pauses the application with a call to C<SDL::Events::wait_event|SDL::Events/wait_event>. This only has meaning when called
from within a handler of the run loop. Events can then be used to
unpause the app. This is done outside the timing of the app with the same technique as explained in L</stop>.

Takes one argument, which is a callback. The application completes the current run loop, then starts waiting
for the next event with L<wait_event|SDL::Events/wait_event>. This means that C<pause> can be called by any kind of handler
in the run loop. If L</stop> is called during the same run cycle, before or after calling C<pause>,
the app will just stop instead of pausing.

When L<wait_event|SDL::Events/wait_event> receives an event, it is passed to the callback as the first argument.
Just like an event handler, the second argument passed is the app.
If the callback then returns a true value, C<pause> will return.
If the callback returns a false value, the app will stay paused and the process will be repeated.

If a L</stop_handler> is defined, then each event will also be passed to that. This will allow the app to be stopped
while being paused. If the stop handler calls L</stop>, then the app will unpause and then stop.
If your app doesn't have a stop handler, then you'll have to handle stopping yourself in the pause callback.
The app will also unpause if the callback calls L</stop>, regardless of whether the callback then returns true or false.

Below is an example of C<pause> used to implement a pause and unpause when the app loses and gains focus.
As a neat shortcut, the callback is recursively defined and used as both an event handler and the pause callback.

 sub window {
     my ($event, $app) = @_;
     if($event->type == SDL_ACTIVEEVENT) {
         if($event->active_state & SDL_APPINPUTFOCUS) {
             if($event->active_gain) { # gained focus
                 return 1; # unpause
             }
             else {
                 $app->pause(\&window);
                 # recursive, but only once since the window
                 # can't lose focus again without gaining it first
             }
         }
     }
     return;
 }
 $app->add_event_handler(\&window);

=head2 paused

 my $paused = $app->paused;

Returns true if the run loop is currently paused. Also returns true when the run loop is about to pause.
That is, true when the app will complete the current run cycle before pausing.

Both of these cases can be useful for handling pausing. Being able to tell if the app is currently paused is useful
from within the L</pause> callback:

 sub toggle_pause { # press P to toggle pause
     my ($event, $app) = @_;
     if($event->type == SDL_KEYDOWN) {
         if($event->key_sym == SDLK_p) {
             # We're paused, so end pause
             return 1 if $app->paused;

             # We're not paused, so pause
             $app->pause(\&toggle_pause);
         }
     }
     return;
 }
 $app->add_event_handler(\&toggle_pause);

Knowing if the app is about to pause is useful from within the app's handlers. If L</pause> is called from within
an event handler, then the move and show handlers can check and respond to L</paused> in the remainder of the run cycle.
In this time, both C<paused> and L</stopped> will return true. When the app is actually paused, only C<paused> will return true.
This true value is actually the callback you passed with L</pause>, and can be checked to tell between different pauses.
For example, a show handler could make the screen say PAUSED when L</paused> is true. This is preferred to having the
event handler display this, because L</stop> could be called in the meantime.

=head2 dt

=head2 min_t

=head2 delay

=head2 stop_handler

=head2 event

 my $dt           = $app->dt;
 my $min_t        = $app->min_t;
 my $max_t        = $app->max_t;
 my $delay        = $app->delay;
 my $stop_handler = $app->stop_handler;
 my $event        = $app->event;
 $app->dt          ($dt);
 $app->min_t       ($min_t);
 $app->max_t       ($max_t);
 $app->delay       ($delay);
 $app->stop_handler($stop_handler);
 $app->event       ($event);

If an argument is passed, modifies the corresponding parameter to the argument.
For each of these parameters the L</run> loop will give the handlers its old value until the start of the next cycle.
The L</pause> callback, however, will always receive the new value.
See L</new> for details on what these params do.

Returns the corresponding value.

=head2 time

 my $time = $app->time;
 $app->time($time);

Returns the sum of all the C<dt>s that have been handled by all move handlers. In other words, the total amount of time
that has passed in the run loop. When the run loop is L<stopped|/stop> and resumed, this value is not reset.
This should be a useful value to have, but isn't a replacement for L<SDL::get_ticks|SDL/get_ticks>.
Use L<Time::HiRes::time|Time::HiRes/time> instead of L<get_ticks|SDL/get_ticks>.

Specify a value to count from that time. This will have no effect on the run loop itself, but may be useful for
the code in your handlers.

=head2 sleep

 $app->sleep($time);

Causes the app to sleep for the specified time, in hi-res seconds, or forever if no argument is specified.
Use this instead of L<SDL::delay|SDL/delay>.

=head1 AUTHORS

See L<SDL/AUTHORS>.

=head2 ACKNOWLEDGEMENTS

The idea and base for the L</run> loop comes from Lazy Foo's L<< Frame Independent
Movement|http://www.lazyfoo.net/SDL_tutorials/lesson32/index.php >> tutorial,
and Glenn Fiedler's L<< Fix Your Timestep|http://gafferongames.com/game-physics/fix-your-timestep/ >> article on timing.

=head1 SEE ALSO

L<SDLx::App>, L<SDL::Event>, L<SDLx::Surface>
