/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Display switch handling.
 *
 *      By Stefan Schimanski.
 *
 *      See readme.txt for copyright information.
 */


#include "allegro.h"
#include "allegro/internal/aintern.h"
#include "allegro/platform/aintwin.h"


#ifndef ALLEGRO_WINDOWS
   #error something is wrong with the makefile
#endif

#define PREFIX_I                "al-wdispsw INFO: "
#define PREFIX_W                "al-wdispsw WARNING: "
#define PREFIX_E                "al-wdispsw ERROR: "


int _win_app_foreground = TRUE;

static HANDLE foreground_event = NULL;
static int allegro_thread_priority = THREAD_PRIORITY_NORMAL;



/* _win_reset_switch_mode:
 *  Resets the switch mode to its default state.
 */
void _win_reset_switch_mode(void)
{
   /* The default state must be SWITCH_BACKGROUND so that the
      threads don't get blocked when the focus moves forth and
      back during window creation and destruction.  This seems
      to be particularly relevant to WinXP.  */
   set_display_switch_mode(SWITCH_BACKGROUND);

   _win_app_foreground = TRUE;
   
   /* This has a nice side-effect: releasing the blocked threads. */
   SetEvent(foreground_event);
}



/* sys_directx_display_switch_init:
 */
void sys_directx_display_switch_init(void)
{
   foreground_event = CreateEvent(NULL, TRUE, TRUE, NULL);
   _win_reset_switch_mode();
}



/* sys_directx_display_switch_exit:
 */
void sys_directx_display_switch_exit(void)
{
   CloseHandle(foreground_event);
}



/* sys_directx_set_display_switch_mode:
 */
int sys_directx_set_display_switch_mode(int mode)
{
   switch (mode) {

      case SWITCH_BACKGROUND:
      case SWITCH_PAUSE:
         if (win_gfx_driver && !win_gfx_driver->has_backing_store)
            return -1;
	 break;

      case SWITCH_BACKAMNESIA:
      case SWITCH_AMNESIA:
         if (!win_gfx_driver || win_gfx_driver->has_backing_store)
            return -1;
	 break;

      default:
	 return -1;
   } 

   return 0;
}



/* _win_switch_in:
 *  Puts the library in the foreground.
 */
void _win_switch_in(void)
{
   int mode;

   _TRACE(PREFIX_I "switch in\n");

   _enter_gfx_critical();

   _win_app_foreground = TRUE;

   key_dinput_acquire();
   mouse_dinput_acquire();
   joystick_dinput_acquire();

   if (win_gfx_driver && win_gfx_driver->switch_in)
      win_gfx_driver->switch_in();

   /* handle switch modes */
   mode = get_display_switch_mode();

   if ((mode == SWITCH_AMNESIA) || (mode == SWITCH_PAUSE)) {
      _TRACE(PREFIX_I "AMNESIA or PAUSE mode recovery\n");
      SetEvent(foreground_event);

      /* restore old priority and wake up */
      SetThreadPriority(allegro_thread, allegro_thread_priority);
   }

   _switch_in();

   _exit_gfx_critical();
}



/* _win_switch_out:
 *  Puts the library in the background.
 */
void _win_switch_out(void)
{
   int mode;

   _TRACE(PREFIX_I "switch out\n");

   _enter_gfx_critical();

   _switch_out();

   _win_app_foreground = FALSE;

   key_dinput_unacquire();
   mouse_dinput_unacquire();
   joystick_dinput_unacquire();

   midi_switch_out();

   if (win_gfx_driver && win_gfx_driver->switch_out)
      win_gfx_driver->switch_out();

   /* handle switch modes */
   mode = get_display_switch_mode();

   if ((mode == SWITCH_AMNESIA) || (mode == SWITCH_PAUSE)) {
      _TRACE(PREFIX_I "AMNESIA or PAUSE mode suspension\n");
      ResetEvent(foreground_event);

      /* if the thread doesn't stop, lower its priority only if another window is active */ 
      allegro_thread_priority = GetThreadPriority(allegro_thread); 
      if ((HINSTANCE)GetWindowLongPtr(GetForegroundWindow(), GWLP_HINSTANCE) != allegro_inst)
         SetThreadPriority(allegro_thread, THREAD_PRIORITY_LOWEST);
   }

   _exit_gfx_critical();
}



/* _win_thread_switch_out:
 *  Handles a switch out event for the calling thread.
 *  Returns TRUE if the thread was blocked, FALSE otherwise.
 */
int _win_thread_switch_out(void)
{
   int mode = get_display_switch_mode();

   if ((mode == SWITCH_AMNESIA) || (mode == SWITCH_PAUSE)) {
      WaitForSingleObject(foreground_event, INFINITE);
      return TRUE;
   }

   return FALSE;
}
