/*
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* Mixer for low-level sound output drivers
 */

#ifndef _MIXER_H
#define _MIXER_H

#include "config.h"
#include "types.h"
#include "endian_uqm.h"

/**
 * The interface heavily influenced by OpenAL
 * to the point where you should use OpenAL's
 * documentation when programming the mixer.
 * (some source properties are not supported)
 *
 * EXCEPTION: You may not queue the same buffer
 * on more than one source
 */

#ifdef WORDS_BIGENDIAN
#	define MIX_IS_BIG_ENDIAN   true
#	define MIX_WANT_BIG_ENDIAN true
#else
#	define MIX_IS_BIG_ENDIAN   false
#	define MIX_WANT_BIG_ENDIAN false
#endif

/**
 * Mixer errors (see OpenAL errors)
 */
enum
{
	MIX_NO_ERROR = 0,
	MIX_INVALID_NAME = 0xA001U,
	MIX_INVALID_ENUM = 0xA002U,
	MIX_INVALID_VALUE = 0xA003U,
	MIX_INVALID_OPERATION = 0xA004U,
	MIX_OUT_OF_MEMORY = 0xA005U,

	MIX_DRIVER_FAILURE = 0xA101U
};

/**
 * Source properties (see OpenAL)
 */
typedef enum
{
	MIX_POSITION = 0x1004,
	MIX_LOOPING = 0x1007,
	MIX_BUFFER = 0x1009,
	MIX_GAIN = 0x100A,
	MIX_SOURCE_STATE = 0x1010,

	MIX_BUFFERS_QUEUED = 0x1015,
	MIX_BUFFERS_PROCESSED = 0x1016

} mixer_SourceProp;

/**
 * Source state information
 */
typedef enum
{
	MIX_INITIAL = 0,
	MIX_STOPPED,
	MIX_PLAYING,
	MIX_PAUSED,

} mixer_SourceState;

/** 
 * Sound buffer properties
 */
typedef enum
{
	MIX_FREQUENCY = 0x2001,
	MIX_BITS = 0x2002,
	MIX_CHANNELS = 0x2003,
	MIX_SIZE = 0x2004,
	MIX_DATA = 0x2005

} mixer_BufferProp;

/**
 * Buffer states: semi-private
 */
typedef enum
{
	MIX_BUF_INITIAL = 0,
	MIX_BUF_FILLED,
	MIX_BUF_QUEUED,
	MIX_BUF_PLAYING,
	MIX_BUF_PROCESSED

} mixer_BufferState;

/** Sound buffers: format specifier.
 * bits 00..07: bytes per sample
 * bits 08..15: channels
 * bits 15..31: meaningless
 */
#define MIX_FORMAT_DUMMYID     0x00170000
#define MIX_FORMAT_BPC(f)      ((f) & 0xff)
#define MIX_FORMAT_CHANS(f)    (((f) >> 8) & 0xff)
#define MIX_FORMAT_BPC_MAX     2
#define MIX_FORMAT_CHANS_MAX   2
#define MIX_FORMAT_MAKE(b, c) \
		( MIX_FORMAT_DUMMYID | ((b) & 0xff) | (((c) & 0xff) << 8) )

#define MIX_FORMAT_SAMPSIZE(f) \
		( MIX_FORMAT_BPC(f) * MIX_FORMAT_CHANS(f) )

typedef enum
{
	MIX_FORMAT_MONO8 = MIX_FORMAT_MAKE (1, 1),
	MIX_FORMAT_STEREO8 = MIX_FORMAT_MAKE (1, 2),
	MIX_FORMAT_MONO16 = MIX_FORMAT_MAKE (2, 1),
	MIX_FORMAT_STEREO16 = MIX_FORMAT_MAKE (2, 2)

} mixer_Format;

typedef enum
{
	MIX_QUALITY_LOW = 0,
	MIX_QUALITY_MEDIUM,
	MIX_QUALITY_HIGH,
	MIX_QUALITY_DEFAULT = MIX_QUALITY_MEDIUM,
	MIX_QUALITY_COUNT

} mixer_Quality;

typedef enum
{
	MIX_NOFLAGS = 0,
	MIX_FAKE_DATA = 1
} mixer_Flags;

/*************************************************
 *  Interface Types
 */

typedef intptr_t mixer_Object;
typedef intptr_t mixer_IntVal;

typedef struct _mixer_Source mixer_Source;

typedef struct _mixer_Buffer
{
	uint32 magic;
	bool locked;
	mixer_BufferState state;
	uint8 *data;
	uint32 size;
	uint32 sampsize;
	uint32 high;
	uint32 low;
	float (* Resample) (mixer_Source *src, bool left);
	/* original buffer values for OpenAL compat */
	void* orgdata;
	uint32 orgfreq;
	uint32 orgsize;
	uint32 orgchannels;
	uint32 orgchansize;
	/* next buffer in chain */
	struct _mixer_Buffer *next;

} mixer_Buffer;

#define mixer_bufMagic 0x4258494DU /* MIXB in LSB */

struct _mixer_Source
{
	uint32 magic;
	bool locked;
	mixer_SourceState state;
	bool looping;
	float gain;
	uint32 cqueued;
	uint32 cprocessed;
	mixer_Buffer *firstqueued; /* first buf in the queue */
	mixer_Buffer *nextqueued;  /* next to play, or 0 */
	mixer_Buffer *prevqueued;  /* previously played */
	mixer_Buffer *lastqueued;  /* last in queue */
	uint32 pos; /* position in current buffer */
	uint32 count; /* fractional part of pos */

	float samplecache;

};

#define mixer_srcMagic 0x5358494DU /* MIXS in LSB */

/*************************************************
 *  General interface
 */
uint32 mixer_GetError (void);

bool mixer_Init (uint32 frequency, uint32 format, mixer_Quality quality,
		mixer_Flags flags);
void mixer_Uninit (void);
void mixer_MixChannels (void *userdata, uint8 *stream, sint32 len);
void mixer_MixFake (void *userdata, uint8 *stream, sint32 len);

/*************************************************
 *  Sources
 */
void mixer_GenSources (uint32 n, mixer_Object *psrcobj);
void mixer_DeleteSources (uint32 n, mixer_Object *psrcobj);
bool mixer_IsSource (mixer_Object srcobj);
void mixer_Sourcei (mixer_Object srcobj, mixer_SourceProp pname,
		mixer_IntVal value);
void mixer_Sourcef (mixer_Object srcobj, mixer_SourceProp pname,
		float value);
void mixer_Sourcefv (mixer_Object srcobj, mixer_SourceProp pname,
		float *value);
void mixer_GetSourcei (mixer_Object srcobj, mixer_SourceProp pname,
		mixer_IntVal *value);
void mixer_GetSourcef (mixer_Object srcobj, mixer_SourceProp pname,
		float *value);
void mixer_SourceRewind (mixer_Object srcobj);
void mixer_SourcePlay (mixer_Object srcobj);
void mixer_SourcePause (mixer_Object srcobj);
void mixer_SourceStop (mixer_Object srcobj);
void mixer_SourceQueueBuffers (mixer_Object srcobj, uint32 n,
		mixer_Object* pbufobj);
void mixer_SourceUnqueueBuffers (mixer_Object srcobj, uint32 n,
		mixer_Object* pbufobj);

/*************************************************
 *  Buffers
 */
void mixer_GenBuffers (uint32 n, mixer_Object *pbufobj);
void mixer_DeleteBuffers (uint32 n, mixer_Object *pbufobj);
bool mixer_IsBuffer (mixer_Object bufobj);
void mixer_GetBufferi (mixer_Object bufobj, mixer_BufferProp pname,
		mixer_IntVal *value);
void mixer_BufferData (mixer_Object bufobj, uint32 format, void* data,
		uint32 size, uint32 freq);


/* Make sure the prop-value type is of suitable size
 * it must be able to store both int and void*
 * Adapted from SDL
 * This will generate "negative subscript or subscript is too large"
 * error during compile, if the actual size of a type is wrong
 */
#define MIX_COMPILE_TIME_ASSERT(name, x) \
	typedef int mixer_dummy_##name [(x) * 2 - 1]

MIX_COMPILE_TIME_ASSERT (mixer_Object,
		sizeof(mixer_Object) >= sizeof(void*));
MIX_COMPILE_TIME_ASSERT (mixer_IntVal,
		sizeof(mixer_IntVal) >= sizeof(mixer_Object));

#undef MIX_COMPILE_TIME_ASSERT

#endif /* _MIXER_H */
