/*************************************************************************************************/
/*!
   	@file		main.h
	@author 	Fanzo
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	<windows.h>
#include	<math.h>
#include	"../../Project/win/AudioDevice/resource.h"
#include	"AudioSettingDlg.h"

#pragma pack( push , 8 )		//set align

using namespace icubic;
using namespace icubic_audio;
using namespace shared;
///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define

///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"GenTone" class 
**************************************************************************************************/
class GenTone
{
// variable member
private:
	double		m_current_time;
	float		m_amplitude;
	float		m_frequency;		
	
// private functions
private:
// public functions
public:
//=================================================================================================
GenTone
		(
		float		amplitude	= 0.3f , 
		float		frequency	= 500.0f
		) : m_current_time( 0.0f ) , m_amplitude( amplitude ) , m_frequency( frequency )
{
}
//=================================================================================================
void Reset()
{
	m_current_time	= 0.0f;
}
//=================================================================================================
void Gen
		(
		uint32		samplespersec , 
		uint32		samplenum , 
		float*		sample
		)
{
	double	dtime	= 1.0 / (double)samplespersec;
	uint32	sampleoff;
	for( sampleoff = 0 ; sampleoff < samplenum ; sampleoff++ )
	{
		sample[sampleoff]	= m_amplitude * ( float )sin( m_frequency * 2.0 * PI_d * m_current_time );
		m_current_time += dtime;
	}
}
};
/**************************************************************************************************
"MainWindow" class 
**************************************************************************************************/
class MainWindow : 
	public Window , 
	IAudioDeviceStream
{
	msg_win_map_begin()
	msg_win_hook( WM_CREATE			, OnCreate );
	msg_win_hook( WM_DESTROY		, OnDestroy );
	msg_win_hook( WM_INITMENUPOPUP	, OnInitMenuPopup );
	msg_win_cmd_hook( ID_AUDIO_SETTING	, 0 , OnAudioSetting );
	msg_win_cmd_hook( ID_GENTONE		, 0 , OnGenTone );
	msg_win_map_end()
	
// variable member
private:
	instance<DeviceSettingDlg>		m_setting;
	
	// audio	
	iAudioDevice					m_device;
	instance<EnumAudioDriverInOut>	m_enums;
	iAudioDeviceId					m_id;
	IAudioDeviceAsio::Format		m_format;

	// audio thread
	long							m_gentone_f;
	GenTone							m_gen;
	
// "IAudioDeviceStream" interface functions
public:
//=================================================================================================
void cb_call AudioDeviceStream
		(
		int32				ctrlid , 
		const Format&		format , 
		float const*const*	capture , 
		float**				render
		)
{
	if( format.m_channel[1] > 0 )
	{
		int32	f = atomic_get(&m_gentone_f);
		if( f != 0 )
			m_gen.Gen( format.m_samplespersec , format.m_buffersamples , render[0] );
		else
			MemoryZero( render[0] , format.m_buffersamples * sizeof( float ) );
		uint32	ch;
		for( ch = 1 ; ch < format.m_channel[1] ; ch++ )
			MemoryCopy( render[ch] , render[0] , format.m_buffersamples * sizeof( float ) );
	}

	uint32	ch , chnum = min( format.m_channel[0] , format.m_channel[1] );
	for( ch = 0 ; ch < chnum ; ch++ )
	{
		add_ary( render[ch] , capture[ch] , format.m_buffersamples );
	}
}

// private functions
private:
//=================================================================================================
void SaveState()
{
	wstring	path = GetModuleFolder( m_hinst ) + L"state.bin";
	instance<FileWriter>	file;
	if( false == file->Open( path.c_str() ) )
		return;
	
	if( false == file->Write( &m_gentone_f , 1 , sizeof( m_gentone_f ) , Little_EndianType ) )
		return;
	if( false == file->Write( &m_format , 1 , sizeof( m_format ) , Little_EndianType ) )
		return;
	int		size	= 0;
	if( m_id == true )
		size = m_id->GetDataSize();
	if( false == file->Write( &size , sizeof( size ) , 1 , Little_EndianType ) )
		return;
	if( size != 0 && false == file->Write( m_id->GetDataPtr() , 1 , size , Little_EndianType ) )
		return;
}
//=================================================================================================
void LoadState()
{
	wstring	path = GetModuleFolder( m_hinst ) + L"state.bin";
	instance<FileReader>	file;
	if( false == file->Open( path.c_str() ) )
		return;

	uint32						tone_f;
	if( false == file->Read( &tone_f , 1 , sizeof( tone_f ) , Little_EndianType ) )
		return;
	IAudioDeviceAsio::Format	fmt;	
	if( false == file->Read( &fmt , 1 , sizeof( fmt ) , Little_EndianType ) )
		return;
	int		size	= 0;
	if( false == file->Read( &size , sizeof( size ) , 1 , Little_EndianType ) )
		return;

	Array<uint8>	buf( size );
	if( size != 0 )
	{
		if( false == file->Read( buf.GetPtr() , 1 , size , Little_EndianType ) )
			return;
	}
	instance<AudioDeviceId>		id;
	id->SetData( buf.GetConstPtr() , buf.GetDatanum() );
	
	atomic_exchange( &m_gentone_f , tone_f );
	m_format	= fmt;
	m_id		= (iAudioDeviceId)id;
}
//=================================================================================================
bool Play()
{
	int32	drvoff , devoff;
	if( m_id == false )
		return false;
	if( false == m_enums->SearchDevice( &drvoff , &devoff , m_id ) )
	{
		::MessageBox( m_hwnd , L"デバイスが見つかりません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
		return false;
	}
	m_device	= m_enums->GetEnumAudioDevice( drvoff )->CreateDevice( devoff , m_hwnd );
	if( m_device == false )
	{
		::MessageBox( m_hwnd , L"デバイスを作成できません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
		return false;
	}
	iAudioDeviceAsio	asio = (iAudioDeviceAsio)m_device;
	if( asio == true )
	{
		if( false == asio->Create( m_format ) )
		{
			::MessageBox( m_hwnd , L"デバイスを作成できません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
			return false;
		}
	}
	else
	{
		if( false == m_device->Create( m_format ) )
		{
			::MessageBox( m_hwnd , L"デバイスを作成できません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
			return false;
		}
	}
	if( false == m_device->SetAudioDeviceStream( 0 , this ) )
	{
		::MessageBox( m_hwnd , L"コールバックを設定できません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
		return false;
	}
	if( false == m_device->Play() )
	{
		::MessageBox( m_hwnd , L"デバイスを再生できません" , L"エラー" , MB_OK | MB_ICONINFORMATION );
		return false;
	}
	return true;
}
// windows message handler
private:
//=================================================================================================
void OnCreate
		(
		WPARAM	wparam,
		LPARAM	lparam
		)
{
	CREATESTRUCT*	cs	= ( CREATESTRUCT* )lparam;
	
	LoadState();
	m_enums->Enum();
	if( false == Play() )
		OnAudioSetting();
}
//=================================================================================================
void OnDestroy
		(
		WPARAM	wparam,
		LPARAM	lparam
		)
{
	SaveState();
	if( m_device == true )
		m_device->Destroy();
	PostQuitMessage(0);
}
//=================================================================================================
void OnInitMenuPopup
		(
		WPARAM	wparam,
		LPARAM	lparam
		)
{
	int32	f = atomic_get( &m_gentone_f );
	if( f == 0 )
		CheckMenuItem( (HMENU)wparam , ID_GENTONE , MF_BYCOMMAND | MF_UNCHECKED );
	else
		CheckMenuItem( (HMENU)wparam , ID_GENTONE , MF_BYCOMMAND | MF_CHECKED );
}
//=================================================================================================
void OnGenTone()
{
	atomic_exchange( &m_gentone_f , atomic_get( &m_gentone_f ) == 0 ? 1 : 0 );
}
//=================================================================================================
void OnAudioSetting()
{
	m_device.release();
	if( true == m_setting->DoModal( m_hinst , m_hwnd , (iEnumAudioDriver)m_enums , &m_id , &m_format ) )
		SaveState();
	Play();
}
// public functions
public:
//=================================================================================================
MainWindow() : m_gentone_f( 1 )
{
	m_format.m_channel[0]		= 2;
	m_format.m_channel[1]		= 2;
	m_format.m_samplespersec	= 44100;
	m_format.m_buffersamples	= 0;	
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

///////////////////////////////////////////////////////////////////////////////////////////////////
// global functions define


//using namespace icubic;		

#pragma pack( pop )			//release align
