use calloop::{
    timer::{TimeoutAction, Timer},
    EventLoop, LoopSignal,
};

fn main() {
    // Create the event loop. The loop is parameterised by the kind of shared
    // data you want the callbacks to use. In this case, we want to be able to
    // stop the loop when the timer fires, so we provide the loop with a
    // LoopSignal, which has the ability to stop the loop from within events. We
    // just annotate the type here; the actual data is provided later in the
    // run() call.
    let mut event_loop: EventLoop<LoopSignal> =
        EventLoop::try_new().expect("Failed to initialize the event loop!");

    // Retrieve a handle. It is used to insert new sources into the event loop
    // It can be cloned, allowing you to insert sources from within source
    // callbacks.
    let handle = event_loop.handle();

    // Create our event source, a timer, that will expire in 2 seconds
    let source = Timer::from_duration(std::time::Duration::from_secs(2));

    // Inserting an event source takes this general form. It can also be done
    // from within the callback of another event source.
    handle
        .insert_source(
            // a type which implements the EventSource trait
            source,
            // a callback that is invoked whenever this source generates an event
            |event, _metadata, shared_data| {
                // This callback is given 3 values:
                // - the event generated by the source (in our case, timer events are the Instant
                //   representing the deadline for which it has fired)
                // - &mut access to some metadata, specific to the event source (in our case, a
                //   timer handle)
                // - &mut access to the global shared data that was passed to EventLoop::run or
                //   EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
                //
                // The return type is just () because nothing uses it. Some
                // sources will expect a Result of some kind instead.
                println!("Timeout for {:?} expired!", event);
                // notify the event loop to stop running using the signal in the shared data
                // (see below)
                shared_data.stop();
                // The timer event source requires us to return a TimeoutAction to
                // specify if the timer should be rescheduled. In our case we just drop it.
                TimeoutAction::Drop
            },
        )
        .expect("Failed to insert event source!");

    // Create the shared data for our loop.
    let mut shared_data = event_loop.get_signal();

    // Actually run the event loop. This will dispatch received events to their
    // callbacks, waiting at most 20ms for new events between each invocation of
    // the provided callback (pass None for the timeout argument if you want to
    // wait indefinitely between events).
    //
    // This is where we pass the *value* of the shared data, as a mutable
    // reference that will be forwarded to all your callbacks, allowing them to
    // share some state
    event_loop
        .run(
            std::time::Duration::from_millis(20),
            &mut shared_data,
            |_shared_data| {
                // Finally, this is where you can insert the processing you need
                // to do do between each waiting event eg. drawing logic if
                // you're doing a GUI app.
            },
        )
        .expect("Error during event loop!");
}
