#include <stdlib.h> /* getenv()/exit()/atexit() */
#include <stdio.h>  /* NULL */

#include <SDL.h>
#include "gen_defs.h"
#include "threadwrap.h"
#include "sdlfuncs.h"

struct sdlfuncs xpbeep_sdl;

/* Make xp_dl do static linking */
#ifdef STATIC_SDL
#define STATIC_LINK
#endif
#include "xp_dl.h"

static int sdl_funcs_loaded = 0;
static bool sdl_funcs_failed = false;
static int sdl_initialized = 0;
static int sdl_audio_initialized = 0;
static dll_handle  sdl_dll;

int xpbeep_load_sdl_funcs(struct sdlfuncs *sdlf)
{
	/* Match the video-side loader (conio/sdlfuncs.c). The legacy SDL-1.2
	 * / SDL-1.1 fallbacks are kept for systems where SDL2 isn't available
	 * but the ancient libs still are; nothing new ships SDL1. The API
	 * subset we use (SDL_OpenAudio / SDL_AudioSpec / AUDIO_S16SYS / the
	 * lock/pause calls) is source-compatible between SDL1 and SDL2. */
	const char *libnames[] = {"SDL2", "SDL", "SDL-1.2", "SDL-1.1", NULL};

	if (sdl_funcs_failed)
		return -1;
	sdl_funcs_failed = true;
	if (sdl_funcs_loaded)
		return 0;
	putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1");
	sdlf->gotfuncs = 0;
	if ((sdl_dll = xp_dlopen(libnames, RTLD_LAZY | RTLD_GLOBAL, SDL_PATCHLEVEL)) == NULL)
		return -1;

	if ((sdlf->Init = xp_dlsym(sdl_dll, SDL_Init)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->Quit = xp_dlsym(sdl_dll, SDL_Quit)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->SemWait = xp_dlsym(sdl_dll, SDL_SemWait)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->SemPost = xp_dlsym(sdl_dll, SDL_SemPost)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->SDL_CreateSemaphore = xp_dlsym(sdl_dll, SDL_CreateSemaphore)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->SDL_DestroySemaphore = xp_dlsym(sdl_dll, SDL_DestroySemaphore)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->InitSubSystem = xp_dlsym(sdl_dll, SDL_InitSubSystem)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->QuitSubSystem = xp_dlsym(sdl_dll, SDL_QuitSubSystem)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->OpenAudio = xp_dlsym(sdl_dll, SDL_OpenAudio)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->CloseAudio = xp_dlsym(sdl_dll, SDL_CloseAudio)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->PauseAudio = xp_dlsym(sdl_dll, SDL_PauseAudio)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->LockAudio = xp_dlsym(sdl_dll, SDL_LockAudio)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->UnlockAudio = xp_dlsym(sdl_dll, SDL_UnlockAudio)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	if ((sdlf->GetAudioStatus = xp_dlsym(sdl_dll, SDL_GetAudioStatus)) == NULL) {
		xp_dlclose(sdl_dll);
		return -1;
	}
	sdlf->gotfuncs = 1;
	sdl_funcs_loaded = 1;
	sdl_funcs_failed = false;
	return 0;
}

int init_sdl_audio(void)
{
	if (!sdl_funcs_loaded) {
		if (xpbeep_load_sdl_funcs(&xpbeep_sdl) != 0)
			return -1;
	}
	if (!sdl_initialized) {
		if (xpbeep_sdl.Init(0) == 0)
			sdl_initialized = TRUE;
		else
			return -1;
	}
	if (sdl_audio_initialized)
		return 0;
	if (xpbeep_sdl.InitSubSystem(SDL_INIT_AUDIO) == 0) {
		sdl_audio_initialized = TRUE;
		return 0;
	}
	return -1;
}
