/**
 * Events test suite
 */

#include <stdio.h>

#include "SDL.h"
#include "SDL_test.h"

/* ================= Test Case Implementation ================== */

/* Test case functions */

/* Flag indicating if the userdata should be checked */
int _userdataCheck = 0;

/* Userdata value to check */
int _userdataValue = 0;

/* Flag indicating that the filter was called */
int _eventFilterCalled = 0;

/* Userdata values for event */
int _userdataValue1 = 1;
int _userdataValue2 = 2;

/* Event filter that sets some flags and optionally checks userdata */
int _events_sampleNullEventFilter(void *userdata, SDL_Event *event)
{
   _eventFilterCalled = 1;

   if (_userdataCheck != 0) {
       SDLTest_AssertCheck(userdata != NULL, "Check userdata pointer, expected: non-NULL, got: %s", (userdata != NULL) ? "non-NULL" : "NULL");
       if (userdata != NULL) {
          SDLTest_AssertCheck(*(int *)userdata == _userdataValue, "Check userdata value, expected: %i, got: %i", _userdataValue, *(int *)userdata);
       }
   }

   return 0;
}

/**
 * @brief Test pumping and peeking events.
 *
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_PumpEvents
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_PollEvent
 */
int
events_pushPumpAndPollUserevent(void *arg)
{
   SDL_Event event1;
   SDL_Event event2;
   int result;

   /* Create user event */
   event1.type = SDL_USEREVENT;
   event1.user.code = SDLTest_RandomSint32();
   event1.user.data1 = (void *)&_userdataValue1;
   event1.user.data2 = (void *)&_userdataValue2;

   /* Push a user event onto the queue and force queue update */
   SDL_PushEvent(&event1);
   SDLTest_AssertPass("Call to SDL_PushEvent()");
   SDL_PumpEvents();
   SDLTest_AssertPass("Call to SDL_PumpEvents()");

   /* Poll for user event */
   result = SDL_PollEvent(&event2);
   SDLTest_AssertPass("Call to SDL_PollEvent()");
   SDLTest_AssertCheck(result == 1, "Check result from SDL_PollEvent, expected: 1, got: %d", result);

   return TEST_COMPLETED;
}


/**
 * @brief Adds and deletes an event watch function with NULL userdata
 *
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_AddEventWatch
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_DelEventWatch
 *
 */
int
events_addDelEventWatch(void *arg)
{
   SDL_Event event;

   /* Create user event */
   event.type = SDL_USEREVENT;
   event.user.code = SDLTest_RandomSint32();;
   event.user.data1 = (void *)&_userdataValue1;
   event.user.data2 = (void *)&_userdataValue2;

   /* Disable userdata check */
   _userdataCheck = 0;

   /* Reset event filter call tracker */
   _eventFilterCalled = 0;

   /* Add watch */
   SDL_AddEventWatch(_events_sampleNullEventFilter, NULL);
   SDLTest_AssertPass("Call to SDL_AddEventWatch()");

   /* Push a user event onto the queue and force queue update */
   SDL_PushEvent(&event);
   SDLTest_AssertPass("Call to SDL_PushEvent()");
   SDL_PumpEvents();
   SDLTest_AssertPass("Call to SDL_PumpEvents()");
   SDLTest_AssertCheck(_eventFilterCalled == 1, "Check that event filter was called");

   /* Delete watch */
   SDL_DelEventWatch(_events_sampleNullEventFilter, NULL);
   SDLTest_AssertPass("Call to SDL_DelEventWatch()");

   /* Push a user event onto the queue and force queue update */
   _eventFilterCalled = 0;
   SDL_PushEvent(&event);
   SDLTest_AssertPass("Call to SDL_PushEvent()");
   SDL_PumpEvents();
   SDLTest_AssertPass("Call to SDL_PumpEvents()");
   SDLTest_AssertCheck(_eventFilterCalled == 0, "Check that event filter was NOT called");

   return TEST_COMPLETED;
}

/**
 * @brief Adds and deletes an event watch function with userdata
 *
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_AddEventWatch
 * @sa http://wiki.libsdl.org/moin.cgi/SDL_DelEventWatch
 *
 */
int
events_addDelEventWatchWithUserdata(void *arg)
{
   SDL_Event event;

   /* Create user event */
   event.type = SDL_USEREVENT;
   event.user.code = SDLTest_RandomSint32();;
   event.user.data1 = (void *)&_userdataValue1;
   event.user.data2 = (void *)&_userdataValue2;

   /* Enable userdata check and set a value to check */
   _userdataCheck = 1;
   _userdataValue = SDLTest_RandomIntegerInRange(-1024, 1024);

   /* Reset event filter call tracker */
   _eventFilterCalled = 0;

   /* Add watch */
   SDL_AddEventWatch(_events_sampleNullEventFilter, (void *)&_userdataValue);
   SDLTest_AssertPass("Call to SDL_AddEventWatch()");

   /* Push a user event onto the queue and force queue update */
   SDL_PushEvent(&event);
   SDLTest_AssertPass("Call to SDL_PushEvent()");
   SDL_PumpEvents();
   SDLTest_AssertPass("Call to SDL_PumpEvents()");
   SDLTest_AssertCheck(_eventFilterCalled == 1, "Check that event filter was called");

   /* Delete watch */
   SDL_DelEventWatch(_events_sampleNullEventFilter, (void *)&_userdataValue);
   SDLTest_AssertPass("Call to SDL_DelEventWatch()");

   /* Push a user event onto the queue and force queue update */
   _eventFilterCalled = 0;
   SDL_PushEvent(&event);
   SDLTest_AssertPass("Call to SDL_PushEvent()");
   SDL_PumpEvents();
   SDLTest_AssertPass("Call to SDL_PumpEvents()");
   SDLTest_AssertCheck(_eventFilterCalled == 0, "Check that event filter was NOT called");

   return TEST_COMPLETED;
}


/* ================= Test References ================== */

/* Events test cases */
static const SDLTest_TestCaseReference eventsTest1 =
        { (SDLTest_TestCaseFp)events_pushPumpAndPollUserevent, "events_pushPumpAndPollUserevent", "Pushes, pumps and polls a user event", TEST_ENABLED };

static const SDLTest_TestCaseReference eventsTest2 =
        { (SDLTest_TestCaseFp)events_addDelEventWatch, "events_addDelEventWatch", "Adds and deletes an event watch function with NULL userdata", TEST_ENABLED };

static const SDLTest_TestCaseReference eventsTest3 =
        { (SDLTest_TestCaseFp)events_addDelEventWatchWithUserdata, "events_addDelEventWatchWithUserdata", "Adds and deletes an event watch function with userdata", TEST_ENABLED };

/* Sequence of Events test cases */
static const SDLTest_TestCaseReference *eventsTests[] =  {
    &eventsTest1, &eventsTest2, &eventsTest3, NULL
};

/* Events test suite (global) */
SDLTest_TestSuiteReference eventsTestSuite = {
    "Events",
    NULL,
    eventsTests,
    NULL
};
