#include "se_dsound.h"
#include "log.h"
#include <iostream>

namespace se
{
	ZSound::ZSound(IDirectSoundBuffer * buf)
	{
		_buf = buf;
	}

	ZSound::~ZSound()
	{
		_buf->Stop();
		_buf->Release();
		//delete _buf;
	}

	void ZSound::kill()
	{
		_buf->Stop();
	}

	void ZSound::set_volume(float volume)
	{
		int res = _buf->SetVolume((long)(-10000.f+volume*10000.f));
		if (res != DS_OK)
		{
			std::cout << "error on the volume change " << res << " : ";
			if (res == DSERR_CONTROLUNAVAIL) cout << "DSERR_CONTROLUNAVAIL\n";
			else if (res == DSERR_GENERIC) cout << "DSERR_GENERIC\n";
			else if (res == DSERR_INVALIDPARAM) cout << "DSERR_INVALIDPARAM\n";
			else if (res == DSERR_PRIOLEVELNEEDED) cout << "DSERR_PRIOLEVELNEEDED\n";
			else cout << "\n";
		}
		//long cur_volume;
		//_buf->GetVolume(&cur_volume);
		//std::cout << "cur_volume: " << cur_volume << "\n";
	}

	ZSoundEngineDSound::ZSoundEngineDSound(HWND hWnd)
	{
		CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
		CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&g_pGraph);
		g_pGraph->QueryInterface(IID_IMediaControl, (void**)&g_pControl);
		g_pGraph->QueryInterface(IID_IMediaEvent, (void**)&g_pEvent);
		g_pGraph->QueryInterface(IID_IMediaPosition, (void**)&g_pPosition);
		g_pGraph->QueryInterface(IID_IBasicAudio, (void **)&g_pBA);
		//g_pGraph->RenderFile(L"sfx//dream_sc2.mp3", NULL);

		Message("SE", "Muzica!!!");

		//g_pControl->Run();
		cur_music = -1;
		want_music = -1;
		want_volume = 0;

		LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;

		if( FAILED( DirectSoundCreate( NULL, &g_pDS, NULL ) ) )
			Error("SE", "cannot create direct sound");
		if( FAILED( g_pDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) )
			Error("SE", "cannot set cooperative level");

		DSBUFFERDESC dsbd;
		ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
		dsbd.dwSize        = sizeof(DSBUFFERDESC);
		dsbd.dwFlags       = DSBCAPS_PRIMARYBUFFER;
		dsbd.dwBufferBytes = 0;
		dsbd.lpwfxFormat   = NULL;
	       
		if( FAILED( g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
			Error("SE", "cannot create sound buffer");

		WAVEFORMATEX wfx;
		ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); 
		wfx.wFormatTag      = WAVE_FORMAT_PCM; 
		wfx.nChannels       = 2; 
		wfx.nSamplesPerSec  = 22050; 
		wfx.wBitsPerSample  = 16; 
		wfx.nBlockAlign     = wfx.wBitsPerSample / 8 * wfx.nChannels;
		wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

		if( FAILED( pDSBPrimary->SetFormat(&wfx) ) )
			Error("SE", "cannot set format");

		pDSBPrimary->Release();
		pDSBPrimary=0;

		for (int a=0; a<MAX_SOUND; a++)
			g_pDSBuffer[a] = 0;
	}

	ZSoundEngineDSound::~ZSoundEngineDSound()
	{
		for (int a=0; a<MAX_SOUND; a++)
			if (g_pDSBuffer[a])
				g_pDSBuffer[a]->Release();

		if (g_pDS)
			g_pDS->Release();

		if (g_pControl)
			g_pControl->Stop();

		if (g_pGraph)
		{
			if (g_pControl)
				g_pControl->Release();
			if (g_pPosition)
				g_pPosition->Release();
			if (g_pEvent)
				g_pEvent->Release();
			g_pGraph->Release();
		}

		CoUninitialize();

		Message("SE", "not Muzic");
	}

	bool ZSoundEngineDSound::load_music(int num, const char * path)
	{
		WCHAR * tmp = new WCHAR[256];
		//memset(tmp,0,sizeof(tmp));
		MultiByteToWideChar( 0, 0, path, (int)strlen(path)+1, tmp, 256);
		musics[num] = tmp;

		return true;
	}

	bool ZSoundEngineDSound::load_sound(int num, const char * path)
	{
		CWaveSoundRead * g_pWaveSoundRead = new CWaveSoundRead();

		if( FAILED( g_pWaveSoundRead->Open( (CHAR *)path ) ) )
			Error("SE", (string("cannot open ")+string(path)).c_str());

		DSBUFFERDESC dsbd;
		ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
		dsbd.dwSize        = sizeof(DSBUFFERDESC);
		dsbd.dwFlags       = DSBCAPS_STATIC | DSBCAPS_CTRLVOLUME;
		dsbd.dwBufferBytes = g_pWaveSoundRead->m_ckIn.cksize;
		dsbd.lpwfxFormat   = g_pWaveSoundRead->m_pwfx;

		if( FAILED( g_pDS->CreateSoundBuffer( &dsbd, &g_pDSBuffer[num], NULL ) ) )
			Error("SE", "cannot create sound buffer");

		// copy data to sound buffer
		BYTE*   pbWavData; // Pointer to actual wav data 
		UINT    cbWavSize; // Size of data
		VOID*   pbData  = NULL;
		VOID*   pbData2 = NULL;
		DWORD   dwLength;
		DWORD   dwLength2;

		// The size of wave data is in pWaveFileSound->m_ckIn
		INT nWaveFileSize = g_pWaveSoundRead->m_ckIn.cksize;

		// Allocate that buffer.
		pbWavData = new BYTE[ nWaveFileSize ];
		if( NULL == pbWavData )
			Error("SE", "out of memory");

		if( FAILED( g_pWaveSoundRead->Read( nWaveFileSize, pbWavData, &cbWavSize ) ) )           
			Error("SE", "cannon read file");

		// Reset the file to the beginning 
		g_pWaveSoundRead->Reset();

		// Lock the buffer down
		if( FAILED( g_pDSBuffer[num]->Lock( 0, dsbd.dwBufferBytes, &pbData, &dwLength, 
									   &pbData2, &dwLength2, 0L ) ) )
			Error("SE", "cannot lock buffer");

		// Copy the memory to it.
		memcpy( pbData, pbWavData, dsbd.dwBufferBytes );

		// Unlock the buffer, we don't need it anymore.
		g_pDSBuffer[num]->Unlock( pbData, dsbd.dwBufferBytes, NULL, 0 );
		pbData = NULL;

		// We dont need the wav file data buffer anymore, so delete it 
		if (pbWavData) delete pbWavData;

		g_pDSBuffer[num]->SetVolume(0);

		if (g_pWaveSoundRead) delete g_pWaveSoundRead;

		Message("SE", (string("sound ") + string(path) + string(" was loaded")).c_str());

		return true;
	}

	void ZSoundEngineDSound::run_music(int num, float volume, bool sudo)
	{
		want_music = num;
		want_volume = int(-10000 + volume*10000);
		if (cur_music != -1)
		{
			long vol;
			if (sudo)
				g_pBA->put_Volume(-10000);
			else
			{
				g_pBA->get_Volume(&vol);
				if (vol > -10000+50)
				{
					g_pBA->put_Volume(vol-50);
					return;
				}
			}
			g_pControl->Stop();
			IBaseFilter * fil = 0;
			g_pGraph->FindFilterByName(musics[cur_music], &fil);
			g_pGraph->RemoveFilter(fil);
			if (fil) fil->Release();
		}
		if (num >= 0)
		{
			IBaseFilter * fil = 0;
			IPin *pin = 0;
			g_pGraph->AddSourceFilter(musics[num], musics[num], &fil);
			if (!fil) Error("SE", "cannot run music ");
			fil->FindPin(L"Output", &pin);
			g_pGraph->Render(pin);
			g_pPosition->put_CurrentPosition(0);
			g_pBA->put_Volume(want_volume);
			if (pin) pin->Release();
			if (fil) fil->Release();
			g_pControl->Run();
		}
		cur_music = num;
	}

	XSound * ZSoundEngineDSound::play_sound(int i, float volume, bool loop)
	{
		IDirectSoundBuffer * buf;
		g_pDS->DuplicateSoundBuffer(g_pDSBuffer[i], &buf);
		buf->SetVolume(long(-10000.f+volume*10000.f));
		buf->Play(0,0,loop?DSBPLAY_LOOPING:0);
		ZSound * snd = new ZSound(buf);
		cur_sounds.insert(snd);

		return snd;
	}

	void ZSoundEngineDSound::stop_all_sounds()
	{
		while (cur_sounds.size() > 0)
		{
			set<ZSound *>::iterator it = cur_sounds.begin();
			delete (*it);
			cur_sounds.erase(it);
		}
	}

	void ZSoundEngineDSound::check_time()
	{
		if (cur_music >= 0)
		{
			REFTIME T, TX;
			g_pPosition->get_StopTime(&TX);
			g_pPosition->get_CurrentPosition(&T);
			if (T > TX-0.1)
			{
				g_pControl->Stop();
				g_pPosition->put_CurrentPosition(0);
				g_pControl->Run();
			}
		}

		if (cur_music != want_music) run_music(want_music);

		set<ZSound *>::iterator it = cur_sounds.begin();
		while (it != cur_sounds.end())
		{
			DWORD Stat = 0;
			(*it)->_buf->GetStatus( &Stat );
			if( !(Stat & DSBSTATUS_PLAYING) )
			{
				ZSound * snd = (*it);
				delete snd;
				it = cur_sounds.erase(it);
			}
			else it++;
		}

		/*if (Game.debug_mode)
		{
			char S[32];
			sprintf(S, "sounds:%d", cur_sounds.size());
			GE.DrawTextA(SCR_DX+280,SCR_DY+220,S);
		}*/
	}
}