//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Sound/3D/SoundListener.h"
#include "Sound/System/SoundDefinition.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
SoundListener::SoundListener(DirectSoundPrimaryBuffer* primaryBuffer) :
	listener_(NULL), distanceFactor_(0.f){
	// リスナの取得
	if(DirectXFailed(primaryBuffer->QueryInterface(
		DirectSound3DListenerInterfaceID, (void**)&listener_))){
		ErrorOut("SoundListener::SoundListener() リスナの取得に失敗しました。");
	}
	// 初期データの取得
	DS3DLISTENER param;
	param.dwSize = sizeof(DS3DLISTENER);
	if(DirectXFailed(listener_->GetAllParameters(&param))){
		ErrorOut("SoundListener::SoundListener() "
			"パラメータの取得に失敗しました。");
	}
	position_.set(param.vPosition.x, param.vPosition.x, param.vPosition.z);
	velocity_.set(param.vVelocity.x, param.vVelocity.x, param.vVelocity.z);
	frontDirection_.set(param.vOrientFront.x,
		param.vOrientFront.y, param.vOrientFront.z);
	upDirection_.set(param.vOrientTop.x,
		param.vOrientTop.y, param.vOrientTop.z);
	rolloffFactor_ = param.flRolloffFactor;
	dopplerFactor_ = param.flDopplerFactor;
	// 距離係数の初期化
	Assert(distanceFactor_ != SoundDefinition::distanceFactor);
	setDistanceFactor(SoundDefinition::distanceFactor);
	apply3DSettings();
}
//------------------------------------------------------------------------------
// デストラクタ
SoundListener::~SoundListener(){
	SafeRelease(listener_);
}
//------------------------------------------------------------------------------
// 位置の設定
void SoundListener::setPosition(const Vector3& position){
	if(position_ == position){ return; }
	if(DirectXFailed(listener_->SetPosition(
		-position.x, position.y, position.z, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setPosition() 位置の設定に失敗しました。");
	}
	position_ = position;
}
//------------------------------------------------------------------------------
// 速度の設定
void SoundListener::setVelocity(const Vector3& velocity){
	if(velocity_ == velocity){ return; }
	if(DirectXFailed(listener_->SetVelocity(
		-velocity.x, velocity.y, velocity.z, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setVelocity() 速度の設定に失敗しました。");
	}
	velocity_ = velocity;
}
//------------------------------------------------------------------------------
// 位置と速度の設定
void SoundListener::setPositionAndVelocity(
	const Vector3& position, float millisecond){
	Assert(millisecond >= 0.f);
	if(millisecond < Math::epsilon){
		setVelocity(Vector3::zero);
	}else{
		Vector3 velocity = position - position_;
		velocity *= (1000.f / millisecond);
		setVelocity(velocity);
	}
	setPosition(position);
}
//------------------------------------------------------------------------------
// 方向の設定
void SoundListener::setDirection(
	const Vector3& frontDirection, const Vector3& upDirection){
	if((frontDirection_ == frontDirection) &&
		(upDirection_ == upDirection)){ return; }
	Assert(!frontDirection.isZero());
	Assert(!upDirection.isZero());
	if(DirectXFailed(listener_->SetOrientation(
		-frontDirection.x, frontDirection.y, frontDirection.z,
		-upDirection.x, upDirection.y, upDirection.z, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setDirection() 方向の設定に失敗しました。");
	}
	frontDirection_ = frontDirection;
	upDirection_ = upDirection;
}
//------------------------------------------------------------------------------
// 距離係数の設定
void SoundListener::setDistanceFactor(float distanceFactor){
	Assert(distanceFactor > 0.f);
	if(distanceFactor_ == distanceFactor){ return; }
	if(DirectXFailed(listener_->SetDistanceFactor(
		distanceFactor, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setDistanceFactor() "
			"距離係数の設定に失敗しました。");
	}
	distanceFactor_ = distanceFactor;
}
//------------------------------------------------------------------------------
// ロールオフ係数の設定
void SoundListener::setRolloffFactor(float rolloffFactor){
	Assert(DS3D_MAXROLLOFFFACTOR >= rolloffFactor);
	Assert(DS3D_MINROLLOFFFACTOR <= rolloffFactor);
	if(rolloffFactor_ == rolloffFactor){ return; }
	if(DirectXFailed(listener_->SetRolloffFactor(
		rolloffFactor, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setRolloffFactor() "
			"ロールオフ係数の設定に失敗しました。");
	}
	rolloffFactor_ = rolloffFactor;
}
//------------------------------------------------------------------------------
// ドップラー係数の設定
void SoundListener::setDopplerFactor(float dopplerFactor){
	Assert(DS3D_MAXDOPPLERFACTOR >= dopplerFactor);
	Assert(DS3D_MINDOPPLERFACTOR <= dopplerFactor);
	if(dopplerFactor_ == dopplerFactor){ return; }
	if(DirectXFailed(listener_->SetDopplerFactor(
		dopplerFactor, DS3D_DEFERRED))){
		ErrorOut("SoundListener::setDopplerFactor() "
			"ドップラー係数の設定に失敗しました。");
	}
	dopplerFactor_ = dopplerFactor;
}
//------------------------------------------------------------------------------
// 文字列への変換
String SoundListener::toString() const{
	String result, temp;
	temp.format("Position ( %.1f, %.1f, %.1f )\n",
		position_.x, position_.y, position_.z);
	result += temp;
	temp.format("Velocity ( %.1f, %.1f, %.1f )\n",
		velocity_.x, velocity_.y, velocity_.z);
	result += temp;
	temp.format("Direction Front( %.2f, %.2f, %.2f ) Up( %.2f, %.2f, %.2f )\n",
		frontDirection_.x, frontDirection_.y, frontDirection_.z,
		upDirection_.x, upDirection_.y, upDirection_.z);
	result += temp;
	temp.format("Distance %.2f  Rolloff %.2f  Doppler %.2f\n",
		distanceFactor_, rolloffFactor_, dopplerFactor_);
	result += temp;
	return result;
}
//------------------------------------------------------------------------------
// 3D設定の適用
void SoundListener::apply3DSettings(){
	if(DirectXFailed(listener_->CommitDeferredSettings())){
		ErrorOut("SoundListener::apply3DSettings() "
			"パラメータの適用に失敗しました。");
	}
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
