#include "mof/sound/SoundDevice.hpp"
#include "mof/ConsoleIO.hpp"
#include <stdio.h>
#include <boost/regex.hpp>
#include "mof/sound/WaveFile.hpp"
#include "mof/sound/OggVorbisFile.hpp"
#include <stdlib.h>
#include <mof/tstring.hpp>
#include "mof/sound/StaticSoundBuffer.hpp"
#include "mof/sound/StreamSoundBuffer.hpp"
#include "mof/sound/DirectSoundDevice.hpp"
#include "mof/sound/sound_recoder.hpp"
#include <dsound.h>

#define CTRL_FLAGS DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME

namespace
{
	const int WORDSIZE = 2;//量子化バイト数
	std::shared_ptr<mof::sound::DirectSoundDevice> pDevice_;
	mof::sound::sound_recoder rec_;

//{{{ loadStaticSound
mof::StaticSoundBuffer* loadStaticSound(const mof::tstring& filename , mof::SoundFile* soundFile){
	
	if(!soundFile->open(filename.c_str())){
		*ConsoleOut::getInstance() << _T("Can't Open File --- ") << filename << std::endl; 
		return NULL;
	}
	
	//---セカンダリバッファの生成
	DSBUFFERDESC dsbdesc;
	ZeroMemory( &dsbdesc , sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = CTRL_FLAGS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCDEFER | DSBCAPS_STATIC;
	dsbdesc.dwBufferBytes = soundFile->getSize();
	dsbdesc.lpwfxFormat = soundFile->getFormat();

	LPDIRECTSOUNDBUFFER pTempBuffer;
	LPDIRECTSOUNDBUFFER8 pSecondaryBuffer;

	HRESULT hr = pDevice_->raw()->CreateSoundBuffer(&dsbdesc , &pTempBuffer , NULL);
	if(FAILED(hr)){
		*ConsoleOut::getInstance() << _T("Failed --- CreateSecondarySoundBuffer") << std::endl;
		return NULL;
	}

	hr = pTempBuffer->QueryInterface(IID_IDirectSoundBuffer8 , (LPVOID*)&pSecondaryBuffer);
	pTempBuffer->Release();
	if (FAILED(hr)){
		*ConsoleOut::getInstance() << _T("Failed --- QueryInterface") << std::endl;
		return NULL;
	}


	mof::StaticSoundBuffer* soundBuffer = new mof::StaticSoundBuffer(pDevice_, pSecondaryBuffer , soundFile);
	//pSecondaryBuffer->Release();
	if(soundBuffer->initialize())return soundBuffer;
	delete soundBuffer;
	return NULL;

}
//}}}
//{{{ loadStreamSound
mof::StreamSoundBuffer* loadStreamSound(const mof::tstring& filename , mof::SoundFile* soundFile){
	
	if(!soundFile->open(filename.c_str())){
		*ConsoleOut::getInstance() << _T("Can't Open File --- ") << filename << std::endl; 
		return NULL;
	}
	
	//---セカンダリバッファの生成
	WAVEFORMATEX* format = soundFile->getFormat();
	DSBUFFERDESC dsbdesc;
	ZeroMemory( &dsbdesc , sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = CTRL_FLAGS | DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS  | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
	dsbdesc.dwBufferBytes = format->nChannels *  format->nAvgBytesPerSec * 2;//2秒分
	dsbdesc.dwBufferBytes -= dsbdesc.dwBufferBytes % 2;//2で割り切れるように
	dsbdesc.lpwfxFormat = format;

	LPDIRECTSOUNDBUFFER pTempBuffer;
	LPDIRECTSOUNDBUFFER8 pSecondaryBuffer;
	LPDIRECTSOUNDNOTIFY8 pSoundNotify;

	HRESULT hr = pDevice_->raw()->CreateSoundBuffer(&dsbdesc , &pTempBuffer , NULL);
	if(FAILED(hr)){
		*ConsoleOut::getInstance() << _T("Failed --- CreateSecondarySoundBuffer") << std::endl;
		return NULL;
	}

	hr = pTempBuffer->QueryInterface(IID_IDirectSoundBuffer8 , (LPVOID*)&pSecondaryBuffer);
	if (FAILED(hr)){
		pTempBuffer->Release();
		*ConsoleOut::getInstance() << _T("Failed --- QueryInterface") << std::endl;
		return NULL;
	}

	hr = pTempBuffer->QueryInterface(IID_IDirectSoundNotify8 , (LPVOID*)&pSoundNotify);
	pTempBuffer->Release();
	if (FAILED(hr)){
		pSecondaryBuffer->Release();
		*ConsoleOut::getInstance() << _T("Failed --- QueryInterface") << std::endl;
		return NULL;
	}


	mof::StreamSoundBuffer* soundBuffer = new mof::StreamSoundBuffer(pDevice_, pSecondaryBuffer , pSoundNotify , soundFile);
	//pSecondaryBuffer->Release();
	pSoundNotify->Release();
	if(soundBuffer->initialize())return soundBuffer;
	delete soundBuffer;
	return NULL;

}
//}}}
}


namespace mof
{
namespace sound
{
//{{{ initialize
	bool SoundDevice::initialize(HWND hWnd, bool enable_output)
	{
		pDevice_ = std::make_shared<DirectSoundDevice>(hWnd);	
		if (enable_output) rec_.open("sound_output/rect.wav");// サウンドレコーダの初期化
		return true;
	}
//}}}
//{{{ finalize
	void SoundDevice::finalize()
	{
		pDevice_ = std::shared_ptr<DirectSoundDevice>();// NULLを設定
	}
//}}}
//{{{ create_static_sound
std::unique_ptr<mof::StaticSoundBuffer> SoundDevice::create_static_sound(const mof::tstring& filename)
{
	//---フォーマットを調べる
	// TODO 関数化
	tstring format;
	try {
		boost::regex regex("(.*)\\.(.*)");//name . format
		boost::smatch match;
		//漢字には対応していない
		if(!boost::regex_search(filename , match , regex)){
			*ConsoleOut::getInstance() << _T("FormatError --- ") << filename;
			throw std::invalid_argument("format error");
		}
		format = match.str(2);	
	}
	catch (std::exception &e) {
		exit(0);
	}

	StaticSoundBuffer* buffer = NULL;
	if(format == "ogg")buffer = loadStaticSound(filename , new OggVorbisFile());
	else if(format == "wav")buffer = loadStaticSound( filename , new WaveFile());
	
	if(buffer == NULL){
		*ConsoleOut::getInstance() << _T("UnKnownFileFormat ---") << format;
		throw std::invalid_argument("unknown file format");
	}
	return std::unique_ptr<StaticSoundBuffer>(buffer);
}
//}}}
//{{{ create_streaming_sound
std::unique_ptr<mof::StreamSoundBuffer> SoundDevice::create_streaming_sound(const mof::tstring& filename)
{
	//---フォーマットを調べる
	// TODO 関数化
	tstring format;
	try {
		boost::regex regex("(.*)\\.(.*)");//name . format
		boost::smatch match;
		//漢字には対応していない
		if(!boost::regex_search(filename , match , regex)){
			*ConsoleOut::getInstance() << _T("FormatError --- ") << filename;
			throw std::invalid_argument("format error");
		}
		format = match.str(2);	
	}
	catch (std::exception &e) {
		exit(0);
	}

	StreamSoundBuffer* buffer = NULL;
	
	if(format == "ogg")buffer = loadStreamSound(filename , new OggVorbisFile());
	else if(format == "wav")buffer = loadStreamSound( filename , new WaveFile());
	
	if(buffer == NULL){
		*ConsoleOut::getInstance() << _T("UnKnownFileFormat ---") << format;
		throw std::invalid_argument("unknown file format");
	}
	return std::unique_ptr<StreamSoundBuffer>(buffer);
}
//}}}
}
}

