// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//-----------------------------------------------------------------------------
/// \file
/// \brief cd music interface
///

#ifdef HAVE_SDL

#if defined (DC) || defined (_WIN32_WCE) || defined(GP2X) || defined(_PS3)
#define NOSDLCD
#endif

#include <stdlib.h>
#ifndef NOSDLCD

#ifdef _MSC_VER
#pragma warning(disable : 4214 4244)
#endif

#include "SDL.h"

#ifdef _MSC_VER
#pragma warning(default : 4214 4244)
#endif

#endif

#include "../doomtype.h"
#include "../i_sound.h"
#include "../command.h"
#include "../m_argv.h"
#include "../s_sound.h"

#define MAX_CD_TRACKS 256

#ifdef _XBOX
INT32  SDL_SYS_CDInit(void)
{
	return(0);
}

void SDL_SYS_CDQuit(void)
{
	return;
}
#endif

UINT8 cdaudio_started = 0;   // for system startup/shutdown

consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cdUpdate  = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};

#ifndef NOSDLCD
static SDL_bool cdValid     = SDL_FALSE;
static SDL_bool cdPlaying   = SDL_FALSE;
static SDL_bool wasPlaying  = SDL_FALSE;
static SDL_bool cdEnabled   = SDL_FALSE;
static SDL_bool playLooping = SDL_FALSE;
static Uint8    playTrack   = 0;
static Uint8    maxTrack    = MAX_CD_TRACKS-1;
static Uint8    cdRemap[MAX_CD_TRACKS];
static INT32      cdvolume    = -1;
static SDL_CD  *cdrom       = NULL;
static CDstatus cdStatus    = CD_ERROR;

/**************************************************************************
 *
 * function: CDAudio_GetAudioDiskInfo
 *
 * description:
 * set number of tracks if CD is available
 *
 **************************************************************************/
static INT32 CDAudio_GetAudioDiskInfo(void)
{
	cdValid = SDL_FALSE;
	maxTrack = 0;

	if (!cdrom)
		return 0;//Alam: Lies!

	cdStatus = SDL_CDStatus(cdrom);

	if (!CD_INDRIVE(cdStatus))
	{
		CONS_Printf("%s", M_GetText("No CD in drive\n"));
		return -1;
	}

	if (cdStatus == CD_ERROR)
	{
		CONS_Printf(M_GetText("CD Error: %s\n"), SDL_GetError());
		return -1;
	}

	cdValid = SDL_TRUE;
	maxTrack = (Uint8)cdrom->numtracks;

	return 0;
}


/**************************************************************************
 *
 * function: I_EjectCD
 *
 * description:
 *
 *
 **************************************************************************/
static void I_EjectCD(void)
{
	if (!cdrom || !cdEnabled)
		return; // no cd init'd

	I_StopCD();

	if (SDL_CDEject(cdrom))
		CONS_Printf("%s", M_GetText("CD eject failed\n"));
}

/**************************************************************************
 *
 * function: Command_Cd_f
 *
 * description:
 * handles all CD commands from the console
 *
 **************************************************************************/
static void Command_Cd_f (void)
{
	const char *command;
	size_t ret, n;

	if (!cdaudio_started)
		return;

	if (COM_Argc() < 2)
	{
		CONS_Printf ("%s", M_GetText("cd [on] [off] [remap] [reset] [select]\n"
		"   [open] [info] [play <track>] [resume]\n"
		"   [stop] [pause] [loop <track>]\n"));
		return;
	}

	command = COM_Argv (1);

	if (!strncmp(command, "on", 2))
	{
		cdEnabled = SDL_TRUE;
		return;
	}

	if (!strncmp(command, "off", 3))
	{
		I_StopCD();
		cdEnabled = SDL_FALSE;
		return;
	}

	if (!strncmp(command, "select", 6))
	{
		INT32 newcddrive;
		newcddrive = atoi(COM_Argv(2));
		command = SDL_CDName(newcddrive);
		I_StopCD();
		cdEnabled = SDL_FALSE;
		SDL_CDClose(cdrom);
		cdrom = SDL_CDOpen(newcddrive);
		if (cdrom)
		{
			cdEnabled = SDL_TRUE;
			CONS_Printf(M_GetText("Opened CD-ROM drive %s\n"), command ? command : COM_Argv(2));
		}
		else CONS_Printf(M_GetText("Couldn't open CD-ROM drive %s: %s\n"), command ? command : COM_Argv(2), SDL_GetError());
		return;
	}

	if (!strncmp(command, "remap", 5))
	{
		ret = COM_Argc() - 2;
		if (ret <= 0)
		{
			for (n = 1; n < MAX_CD_TRACKS; n++)
			{
				if (cdRemap[n] != n)
					CONS_Printf("  %s -> %u\n", sizeu1(n), cdRemap[n]);
			}
			return;
		}
		for (n = 1; n <= ret; n++)
			cdRemap[n] = (Uint8)atoi(COM_Argv (n+1));
		return;
	}

	if (!strncmp(command, "reset", 5))
	{
		if (!cdrom) return;
		cdEnabled = SDL_TRUE;
		I_StopCD();
		for (n = 0; n < MAX_CD_TRACKS; n++)
			cdRemap[n] = (Uint8)n;
		CDAudio_GetAudioDiskInfo();
		return;
	}

	if (!cdValid)
	{
		if (CDAudio_GetAudioDiskInfo()==-1 && !cdValid)
		{
			CONS_Printf("%s", M_GetText("No CD in drive\n"));
			return;
		}
	}

	if (!strncmp(command, "open", 4))
	{
		I_EjectCD();
		cdValid = SDL_FALSE;
		return;
	}

	if (!strncmp(command, "info", 4))
	{
		CONS_Printf(M_GetText("%u tracks\n"), maxTrack);
		if (cdPlaying)
			CONS_Printf(M_GetText("Currently %s track %u\n"), playLooping ? M_GetText("looping") : M_GetText("playing"), playTrack);
		else if (wasPlaying)
			CONS_Printf(M_GetText("Paused %s track %u\n"), playLooping ? M_GetText("looping") : M_GetText("playing"), playTrack);
		CONS_Printf(M_GetText("Volume is %d\n"), cdvolume);
		return;
	}

	if (!strncmp(command, "play", 4))
	{
		I_PlayCD((UINT8)atoi(COM_Argv (2)), SDL_FALSE);
		return;
	}

	if (!strncmp(command, "loop", 4))
	{
		I_PlayCD((UINT8)atoi(COM_Argv (2)), true);
		return;
	}

	if (!strncmp(command, "stop", 4))
	{
		I_StopCD();
		return;
	}
	if (!strncmp(command, "pause", 5))
	{
		I_PauseCD();
		return;
	}

	if (!strncmp(command, "resume", 6))
	{
		I_ResumeCD();
		return;
	}

	CONS_Printf(M_GetText("Invalid CD command \"CD %s\"\n"), COM_Argv(1));
}
#endif

/**************************************************************************
 *
 * function: StopCD
 *
 * description:
 *
 *
 **************************************************************************/
void I_StopCD(void)
{
#ifndef NOSDLCD
	if (!cdrom || !cdEnabled)
		return;

	if (!(cdPlaying || wasPlaying))
		return;

	if (SDL_CDStop(cdrom))
		I_OutputMsg("cdromstop failed\n");

	wasPlaying = SDL_FALSE;
	cdPlaying = SDL_FALSE;
#endif
}

/**************************************************************************
 *
 * function: PauseCD
 *
 * description:
 *
 *
 **************************************************************************/
void I_PauseCD (void)
{
#ifndef NOSDLCD
	if (!cdrom || !cdEnabled)
		return;

	if (!cdPlaying)
		return;

	if (SDL_CDPause(cdrom))
		I_OutputMsg("cdrompause failed\n");

	wasPlaying = cdPlaying;
	cdPlaying = SDL_FALSE;
#endif
}

/**************************************************************************
 *
 * function: ResumeCD
 *
 * description:
 *
 *
 **************************************************************************/
// continue after a pause
void I_ResumeCD (void)
{
#ifndef NOSDLCD
	if (!cdrom || !cdEnabled)
		return;

	if (!cdValid)
		return;

	if (!wasPlaying)
		return;

	if (cd_volume.value == 0)
		return;

	if (SDL_CDResume(cdrom))
		I_OutputMsg("cdromresume failed\n");

	cdPlaying = SDL_TRUE;
	wasPlaying = SDL_FALSE;
#endif
}


/**************************************************************************
 *
 * function: ShutdownCD
 *
 * description:
 *
 *
 **************************************************************************/
void I_ShutdownCD (void)
{
#ifndef NOSDLCD
	if (!cdaudio_started)
		return;

	I_StopCD();

	CONS_Printf("I_ShutdownCD: ");
	SDL_CDClose(cdrom);
	cdrom = NULL;
	cdaudio_started = false;
	CONS_Printf("%s", M_GetText("shut down\n"));
	SDL_QuitSubSystem(SDL_INIT_CDROM);
	cdEnabled = SDL_FALSE;
#endif
}

/**************************************************************************
 *
 * function: InitCD
 *
 * description:
 * Initialize the first CD drive SDL detects and add console command 'cd'
 *
 **************************************************************************/
void I_InitCD (void)
{
#ifndef NOSDLCD
	INT32 i;

	// Has been checked in d_main.c, but doesn't hurt here
	if (M_CheckParm ("-nocd"))
		return;

	CONS_Printf("%s", M_GetText("I_InitCD: Init CD audio\n"));

	// Initialize SDL first
	if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0)
	{
		CONS_Printf(M_GetText("Couldn't initialize SDL CDROM: %s\n"), SDL_GetError());
		return;
	}

	// Open drive
	cdrom = SDL_CDOpen(0);

	if (!cdrom)
	{
		const char *cdName = SDL_CDName(0);
		if (!cdName)
			CONS_Printf(M_GetText("Couldn't open CD-ROM drive %s: %s\n"), "\b", SDL_GetError());
		else
			CONS_Printf(M_GetText("Couldn't open CD-ROM drive %s: %s\n"), cdName, SDL_GetError());
		//return;
	}

	for (i = 0; i < MAX_CD_TRACKS; i++)
		cdRemap[i] = (Uint8)i;

	cdaudio_started = true;
	if (cdrom) cdEnabled = SDL_TRUE;

	if (CDAudio_GetAudioDiskInfo()==-1)
	{
		CONS_Printf("%s", M_GetText("No CD in drive\n"));
		cdValid = SDL_FALSE;
	}

	COM_AddCommand ("cd", Command_Cd_f);

	CONS_Printf("%s", M_GetText("CD audio Initialized\n"));
#endif
}



//
/**************************************************************************
 *
 * function: UpdateCD
 *
 * description:
 * sets CD volume (may have changed) and initiates play evey 2 seconds
 * in case the song has elapsed
 *
 **************************************************************************/
void I_UpdateCD (void)
{
#ifndef NOSDLCD
	static Uint32 lastchk = 0;

	if (!cdEnabled || !cdrom)
		return;

	I_SetVolumeCD(cd_volume.value);

	if (cdPlaying && lastchk < SDL_GetTicks())
	{
		lastchk = SDL_GetTicks() + 2000; //two seconds between chks

		if (CDAudio_GetAudioDiskInfo()==-1)
		{
			cdPlaying = SDL_FALSE;
			return;
		}

		if (cdStatus != CD_PLAYING && cdStatus != CD_PAUSED)
		{
			cdPlaying = SDL_FALSE;
			if (playLooping)
				I_PlayCD(playTrack, true);
		}
	}
#endif
}



/**************************************************************************
 *
 * function: PlayCD
 *
 * description:
 * play the requested track and set the looping flag
 * pauses the CD if volume is 0
 *
 **************************************************************************/

void I_PlayCD (UINT8 track, UINT8 looping)
{
#ifdef NOSDLCD
	(void)track;
	(void)looping;
#else
	if (!cdrom || !cdEnabled)
		return;

	if (!cdValid)
	{
		CDAudio_GetAudioDiskInfo();
		if (!cdValid)
			return;
	}

	track = cdRemap[track];

	if (track < 1 || track > maxTrack)
	{
		CONS_Printf(M_GetText("Bad track number %u.\n"), track);
		return;
	}

	// don't try to play a non-audio track
	if (cdrom->track[track].type == SDL_DATA_TRACK)
	{
		CONS_Printf(M_GetText("Track %u is not audio\n"), track);
		return;
	}

	if (cdPlaying)
	{
		if (playTrack == track)
			return;
		I_StopCD();
	}

	if (SDL_CDPlayTracks(cdrom, track, 0, 1, 0))
	{
		CONS_Printf(M_GetText("Error playing track %d: %s\n"), track, SDL_GetError());
		return;
	}

	playLooping = looping;
	playTrack = (Uint8)track;
	cdPlaying = SDL_TRUE;

	if (cd_volume.value == 0)
		I_PauseCD();
#endif
}


/**************************************************************************
 *
 * function: SetVolumeCD
 *
 * description:
 * SDL does not support setting the CD volume
 * use pause instead and toggle between full and no music
 *
 **************************************************************************/

boolean I_SetVolumeCD (INT32 volume)
{
#ifdef NOSDLCD
	(void)volume;
#else
	if (volume != cdvolume)
	{
		if (volume > 0 && volume < 16)
		{
			CV_SetValue(&cd_volume, 31);
			cdvolume = 31;
			I_ResumeCD();
		}
		else if (volume > 15 && volume < 31)
		{
			CV_SetValue(&cd_volume, 0);
			cdvolume = 0;
			I_PauseCD();
		}
	}
#endif
	return false;
}

#endif
