/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Some definitions for internal use by the Windows library code.
 *
 *      By Stefan Schimanski.
 *
 *      See readme.txt for copyright information.
 */


#ifndef AINTWIN_H
#define AINTWIN_H

#ifndef ALLEGRO_H
   #error must include allegro.h first
#endif

#ifndef ALLEGRO_WINDOWS
   #error bad include
#endif


#include "winalleg.h"

#ifndef SCAN_DEPEND
   /* workaround for buggy MinGW32 headers */
   #ifdef ALLEGRO_MINGW32
      #ifndef HMONITOR_DECLARED
         #define HMONITOR_DECLARED
      #endif
      #if (defined _HRESULT_DEFINED) && (defined WINNT)
         #undef WINNT
      #endif
   #endif

   #include <objbase.h>  /* for LPGUID */
#endif


#ifdef __cplusplus
   extern "C" {
#endif

/* generals */
AL_VAR(HINSTANCE, allegro_inst);
AL_VAR(HANDLE, allegro_thread);
AL_VAR(CRITICAL_SECTION, allegro_critical_section);
AL_VAR(int, _dx_ver);

#define _enter_critical()  EnterCriticalSection(&allegro_critical_section)
#define _exit_critical()   LeaveCriticalSection(&allegro_critical_section)

AL_FUNC(int, init_directx_window, (void));
AL_FUNC(void, exit_directx_window, (void));
AL_FUNC(int, get_dx_ver, (void));
AL_FUNC(int, adjust_window, (int w, int h));
AL_FUNC(void, restore_window_style, (void));
AL_FUNC(void, save_window_pos, (void));


/* main window */
#define WND_TITLE_SIZE  128

AL_ARRAY(char, wnd_title);
AL_VAR(int, wnd_x);
AL_VAR(int, wnd_y);
AL_VAR(int, wnd_width);
AL_VAR(int, wnd_height);
AL_VAR(int, wnd_sysmenu);

AL_FUNCPTR(void, user_close_proc, (void));


/* gfx synchronization */
AL_VAR(CRITICAL_SECTION, gfx_crit_sect);
AL_VAR(int, gfx_crit_sect_nesting);

#define _enter_gfx_critical()  EnterCriticalSection(&gfx_crit_sect); \
                               gfx_crit_sect_nesting++
#define _exit_gfx_critical()   LeaveCriticalSection(&gfx_crit_sect); \
                               gfx_crit_sect_nesting--
#define GFX_CRITICAL_RELEASED  (!gfx_crit_sect_nesting)


/* switch routines */
AL_VAR(int, _win_app_foreground);

AL_FUNC(void, sys_directx_display_switch_init, (void));
AL_FUNC(void, sys_directx_display_switch_exit, (void));
AL_FUNC(int, sys_directx_set_display_switch_mode, (int mode));

AL_FUNC(void, _win_switch_in, (void));
AL_FUNC(void, _win_switch_out, (void));
AL_FUNC(void, _win_reset_switch_mode, (void));

AL_FUNC(int, _win_thread_switch_out, (void));


/* main window routines */
AL_FUNC(int, wnd_call_proc, (int (*proc)(void)));
AL_FUNC(void, wnd_schedule_proc, (int (*proc)(void)));


/* input routines */
AL_VAR(int, _win_input_events);
AL_ARRAY(HANDLE, _win_input_event_id);
AL_FUNCPTRARRAY(void, _win_input_event_handler, (void));

AL_FUNC(void, _win_input_init, (int need_thread));
AL_FUNC(void, _win_input_exit, (void));
AL_FUNC(int, _win_input_register_event, (HANDLE event_id, void (*event_handler)(void)));
AL_FUNC(void, _win_input_unregister_event, (HANDLE event_id));


/* keyboard routines */
AL_FUNC(int, key_dinput_acquire, (void));
AL_FUNC(int, key_dinput_unacquire, (void));


/* mouse routines */
AL_VAR(HCURSOR, _win_hcursor);
AL_FUNC(int, mouse_dinput_acquire, (void));
AL_FUNC(int, mouse_dinput_unacquire, (void));
AL_FUNC(int, mouse_dinput_grab, (void));
AL_FUNC(int, mouse_set_syscursor, (void));
AL_FUNC(int, mouse_set_sysmenu, (int state));


/* joystick routines */
AL_FUNC(int, _al_win_joystick_dinput_acquire, (void));
AL_FUNC(int, _al_win_joystick_dinput_unacquire, (void));


/* thread routines */
AL_FUNC(void, _win_thread_init, (void));
AL_FUNC(void, _win_thread_exit, (void));


/* sound routines */
AL_FUNC(_DRIVER_INFO *, _get_win_digi_driver_list, (void));
AL_FUNC(void, _free_win_digi_driver_list, (void));

AL_FUNC(DIGI_DRIVER *, _get_dsalmix_driver, (char *name, LPGUID guid, int num));
AL_FUNC(DIGI_DRIVER *, _get_woalmix_driver, (int num));

AL_FUNC(int, digi_directsound_capture_init, (LPGUID guid));
AL_FUNC(void, digi_directsound_capture_exit, (void));
AL_FUNC(int, digi_directsound_capture_detect, (LPGUID guid));
AL_FUNC(int, digi_directsound_rec_cap_rate, (int bits, int stereo));
AL_FUNC(int, digi_directsound_rec_cap_param, (int rate, int bits, int stereo));
AL_FUNC(int, digi_directsound_rec_source, (int source));
AL_FUNC(int, digi_directsound_rec_start, (int rate, int bits, int stereo));
AL_FUNC(void, digi_directsound_rec_stop, (void));
AL_FUNC(int, digi_directsound_rec_read, (void *buf));


/* midi routines */
AL_FUNC(_DRIVER_INFO *, _get_win_midi_driver_list, (void));
AL_FUNC(void, _free_win_midi_driver_list, (void));

AL_FUNC(void, midi_switch_out, (void));


/* error handling */
AL_FUNC(char* , win_err_str, (long err));
AL_FUNC(void, thread_safe_trace, (char *msg, ...));

#if DEBUGMODE >= 2
   #define _TRACE                 thread_safe_trace
#else
   #define _TRACE                 1 ? (void) 0 : thread_safe_trace
#endif

#define IS_OLD_WINDOWS (os_type==OSTYPE_WIN3  || os_type==OSTYPE_WIN95 || \
                        os_type==OSTYPE_WIN98 || os_type==OSTYPE_WINME || \
                        os_type==OSTYPE_UNKNOWN)

AL_FUNC(int, _alwin_open, (const char*, int, int));
#define _al_open _alwin_open

#ifdef __cplusplus
   }
#endif



/*----------------------------------------------------------------------*
 *									*
 *	New stuff							*
 *									*
 *----------------------------------------------------------------------*/

/* TODO: integrate this above */
/* TODO: a lot of this is repeated from aintunix.h */

#include "allegro/internal/aintern.h"

AL_BEGIN_EXTERN_C

/* threads */
struct _AL_THREAD
{
   /* private: */
   HANDLE thread;
   CRITICAL_SECTION cs;
   bool should_stop; /* XXX: use a dedicated terminate Event object? */
   void (*proc)(_AL_THREAD *self, void *arg);
   void *arg;
};

AL_INLINE(bool, _al_thread_should_stop, (_AL_THREAD *t),
{
   bool ret;
   EnterCriticalSection(&t->cs);
   ret = t->should_stop;
   LeaveCriticalSection(&t->cs);
   return ret;
})

struct _AL_MUTEX
{
   PCRITICAL_SECTION cs;
};

#define _AL_MUTEX_UNINITED	       { NULL }
#define _AL_MARK_MUTEX_UNINITED(M)     do { M.cs = NULL; } while (0)

AL_INLINE(void, _al_mutex_lock, (_AL_MUTEX *m),
{
   if (m->cs)
      EnterCriticalSection(m->cs);
})
AL_INLINE(void, _al_mutex_unlock, (_AL_MUTEX *m),
{
   if (m->cs)
      LeaveCriticalSection(m->cs);
})

struct _AL_COND
{
   long nWaitersBlocked;
   long nWaitersGone;
   long nWaitersToUnblock;
   HANDLE semBlockQueue;
   CRITICAL_SECTION semBlockLock;
   CRITICAL_SECTION mtxUnblockLock;
};

/* time */
AL_FUNC(void, _al_win_init_time, (void));

AL_END_EXTERN_C


#endif          /* !defined AINTWIN_H */

