// Copyright (C) 2020 Daniel Mall
#ifndef CAUDIOMIXER_H
#define CAUDIOMIXER_H
#include <string>

constexpr int AUDIOMIXER_MAXVOLUME = 255;

//! an audio file
class CAudio
{
public:
	virtual ~CAudio()
	{
	}
};

//! audio mixer
class CAudioMixer
{
public:
	virtual ~CAudioMixer()
	{
	}

	//! play music on loop
	virtual void PlayMusic(CAudio* pAudio, int nVolume) = 0;

	//! play sound one time
	virtual void PlaySound(CAudio* pAudio, int nVolume) = 0;

	//! turn on audio mixer
	virtual void TurnOnAudioMixer() = 0;

	//! turn off audio mixer
	virtual void TurnOffAudioMixer() = 0;

	//! turn on audio mixer music
	virtual void TurnOnMusic() = 0;

	//! turn off audio mixer music
	virtual void TurnOffMusic() = 0;

	//! turn on audio mixer sounds
	virtual void TurnOnSounds() = 0;

	//! turn off audio mixer sounds
	virtual void TurnOffSounds() = 0;

	//! remove the music and sounds from the audio queue
	virtual void ClearAudioQueue() = 0;

	//! start audio so call once at the start of program
	virtual void AudioDeviceOpen() = 0;

	//! end audio so call once at the end of program
	virtual void AudioDeviceClose() = 0;

	//! load audio from a file
	virtual CAudio* CreateAudioFromFile(std::string szFilename, bool bLoop, int nVolume) = 0;

	//! load audio from memory
	virtual CAudio* CreateAudioFromMemory(std::string szId, const unsigned char *pBuffer, int nBufferSize, bool bLoop, int nVolume) = 0;

	//! push audio to the output audio buffer
	virtual void PushAudioToDeviceQueue() = 0;
};

class CAudioAssetSource
{
public:
	std::string m_szFilename;
	std::string m_szId;
	void* m_pBuffer = nullptr;
	int m_nBufferSize = 0;
};

class CAudioWAV : public CAudioAssetSource
{
public:
	bool LoadWAV();

public:
	int m_nChannels = 0;
	int m_nSampleRate = 0;
	int m_nBitsPerSample = 0;
	int m_nSizeWav = 0;
	const char* m_pBufferWav = nullptr;
};

//check big vs little endian machine
inline bool LoadWAV_IsBigEndian(void)
{
	int a = 1;
	return !((char*)&a)[0];
}

inline int LoadWAV_ConvertToInt(const char* buffer, int len)
{
	int a = 0;

	if (!LoadWAV_IsBigEndian())
	{
		for (int i = 0; i < len; ++i)
		{
			((char*)&a)[i] = buffer[i];
		}
	}
	else
	{
		for (int i = 0; i < len; ++i)
		{
			((char*)&a)[3 - i] = buffer[i];
		}
	}

	return a;
}

//! load a WAV audio file
inline bool CAudioWAV::LoadWAV()
{
	const char* pNext = (char*) m_pBuffer;
	if (strncmp(pNext, "RIFF", 4) != 0)
	{
		// std::cout << "Error here, not a valid WAV file, RIFF not found in header\n"
		return false;
	}
	pNext += 4;

	//size of file. Not used. Read it to skip over it.
	pNext += 4;

	// Format, should be WAVE
	if (strncmp(pNext, "WAVE", 4) != 0)
	{
		// std::cout << "Error here, not a valid WAV file, WAVE not found in header.\n"
		return false;
	}
	pNext += 4;

	// Format Space Marker. should equal fmt (space)
	if (strncmp(pNext, "fmt ", 4) != 0)
	{
		// std::cout << "Error here, not a valid WAV file, Format Marker not found in header.\n"
		return false;
	}
	pNext += 4;

	// Length of format data. Should be 16 for PCM, meaning uncompressed.
	const int nLengthFormatData = LoadWAV_ConvertToInt(pNext, 4);
	if (nLengthFormatData != 16)
	{
		// std::cout << "Error here, not a valid WAV file, format length wrong in header.\n"
		return false;
	}
	pNext += 4;

	// Type of format, 1 = PCM
	if (LoadWAV_ConvertToInt(pNext, 2) != 1)
	{
		// std::cout << "Error here, not a valid WAV file, file not in PCM format.\n "
		return false;
	}
	pNext += 2;

	// Assume at this point that we are dealing with a WAV file.
	// Get number of channels. This value is needed by OpenAL
	m_nChannels = LoadWAV_ConvertToInt(pNext, 2);
	pNext += 2;

	// Get sampler rate.
	m_nSampleRate = LoadWAV_ConvertToInt(pNext, 4);
	pNext += 4;

	// Skip Byte Rate and Block Align. Maybe use later?
	pNext += 4; //Block Align
	pNext += 2; //ByteRate

	// Get Bits Per Sample
	m_nBitsPerSample = LoadWAV_ConvertToInt(pNext, 2);
	pNext += 2;

	// Skip character data, which marks the start of the data that we care about. 
	pNext += 4; //"data" chunk. 

	// Get size of the data
	m_nSizeWav = LoadWAV_ConvertToInt(pNext, 4);
	if (m_nSizeWav < 0)
	{
		// std::cout << "Error here, not a valid WAV file, size of file reports 0.\n "
		return false;
	}
	pNext += 4;

	m_pBufferWav = pNext;

	// In order for PCM audio to be able to be burned to a CD, it should be in the 44100 Hz, 16 - bit stereo format.
	return true;
}

#endif
