//------------------------------------------------------------------------------
// 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 "Graphics/Enumeration/GraphicsDeviceComboInformation.h"
#include "Graphics/Enumeration/GraphicsDeviceEnumeration.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Primitive/GraphicsBufferFormat.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
GraphicsDeviceComboInformation::GraphicsDeviceComboInformation(
	D3DFORMAT adapterFormat, D3DFORMAT backBufferFormat, bool isWindowed) :
	adapterFormat_(adapterFormat), backBufferFormat_(backBufferFormat),
	isWindowed_(isWindowed){
}
//------------------------------------------------------------------------------
// デストラクタ
GraphicsDeviceComboInformation::~GraphicsDeviceComboInformation(){
}
//------------------------------------------------------------------------------
// 列挙
bool GraphicsDeviceComboInformation::enumerate(
	GraphicsDeviceEnumeration* enumeration,
	GraphicsDeviceInformation* deviceInformation){
	// メンバの設定
	adapterOrdinal_ = deviceInformation->getAdapterOrdinal();
	deviceType_ = deviceInformation->getDeviceType();

	// 深度、ステンシルフォーマットの構築
	if(enumeration->getUsesDepthStencilBuffer()){
		u_int minimumDepthBits = enumeration->getMinimumDepthBits();
		u_int minimumStencilBits = enumeration->getMinimumStencilBits();
		buildDepthStencilFormats(minimumDepthBits, minimumStencilBits);
		if(getDepthStencilFormatCount() == 0){ return false; }
	}

	// マルチサンプルタイプの構築
	buildMultiSampleType();
	if(getMultiSampleTypeCount() == 0){ return false; }
	// マルチサンプルコンフリクトの構築
	buildMultiSampleConflict();

	// 頂点プロセスタイプの構築
	buildVertexProcessingType(
		deviceInformation->getDeviceCapability(),
		enumeration->getUsesMixedVertexProcessing(),
		enumeration->getConfirmGraphicsDevice());
	if(getVertexProcessingTypeCount() == 0){ return false; }

	// プレゼンテーション間隔の構築
	buildPresentationInterval(deviceInformation->getDeviceCapability());
	return true;
}
//------------------------------------------------------------------------------
// 深度、ステンシルフォーマットの構築
void GraphicsDeviceComboInformation::buildDepthStencilFormats(
	u_int minimumDepthBits, u_int minimumStencilBits){
	Direct3D* direct3D = LampGraphics::getDirect3D();
	// 全深度、ステンシルバッファフォーマット
	const D3DFORMAT depthStencilFormatArray[] = {
		D3DFMT_D16,
		D3DFMT_D15S1,
		D3DFMT_D24X8,
		D3DFMT_D24S8,
		D3DFMT_D24X4S4,
		D3DFMT_D32};
	const u_int depthStencilFormatArrayCount =
		sizeof(depthStencilFormatArray) / sizeof(depthStencilFormatArray[0]);
	for(u_int i = 0; i < depthStencilFormatArrayCount; i++){
		D3DFORMAT depthStencilFormat = depthStencilFormatArray[i];
		GraphicsBufferFormat bufferFormat(depthStencilFormat);
		// 深度ビットチェック
		if(bufferFormat.getDepthBits() < minimumDepthBits){ continue; }
		// ステンシルビットチェック
		if(bufferFormat.getStencilBits() < minimumStencilBits){ continue; }
		// デバイスフォーマットのチェック
		if(DirectXSucceeded(direct3D->CheckDeviceFormat(
			adapterOrdinal_, deviceType_, adapterFormat_,
			D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, depthStencilFormat))){
			// バックバッファとの整合性チェック
			if(DirectXSucceeded(direct3D->CheckDepthStencilMatch(
				adapterOrdinal_, deviceType_, adapterFormat_,
				backBufferFormat_, depthStencilFormat))){
				depthStencilFormats_.add(depthStencilFormat);
			}
		}
	}
}
//------------------------------------------------------------------------------
// マルチサンプルタイプの構築
void GraphicsDeviceComboInformation::buildMultiSampleType(){
	Direct3D* direct3D = LampGraphics::getDirect3D();
	// 全マルチサンプル配列
	const D3DMULTISAMPLE_TYPE multiSampleTypeArray[] = {
		D3DMULTISAMPLE_NONE,
		D3DMULTISAMPLE_NONMASKABLE,
		D3DMULTISAMPLE_2_SAMPLES,
		D3DMULTISAMPLE_3_SAMPLES,
		D3DMULTISAMPLE_4_SAMPLES,
		D3DMULTISAMPLE_5_SAMPLES,
		D3DMULTISAMPLE_6_SAMPLES,
		D3DMULTISAMPLE_7_SAMPLES,
		D3DMULTISAMPLE_8_SAMPLES,
		D3DMULTISAMPLE_9_SAMPLES,
		D3DMULTISAMPLE_10_SAMPLES,
		D3DMULTISAMPLE_11_SAMPLES,
		D3DMULTISAMPLE_12_SAMPLES,
		D3DMULTISAMPLE_13_SAMPLES,
		D3DMULTISAMPLE_14_SAMPLES,
		D3DMULTISAMPLE_15_SAMPLES,
		D3DMULTISAMPLE_16_SAMPLES};
	const u_int multiSampleTypeArrayCount =
		sizeof(multiSampleTypeArray) / sizeof(multiSampleTypeArray[0]);
	for(int i = 0; i < multiSampleTypeArrayCount; i++){
		D3DMULTISAMPLE_TYPE multiSampleType = multiSampleTypeArray[i];
		unsigned long multiSampleQuality;
		// マルチサンプルチェック
		if(DirectXSucceeded(direct3D->CheckDeviceMultiSampleType(
			adapterOrdinal_, deviceType_, backBufferFormat_, isWindowed_,
			multiSampleType, &multiSampleQuality))){
			multiSampleTypes_.add(multiSampleType);
			multiSampleTypeQualities_.add((u_int)multiSampleQuality);
		}
	}
}
//------------------------------------------------------------------------------
// マルチサンプルコンフリクトの構築
void GraphicsDeviceComboInformation::buildMultiSampleConflict(){
	Direct3D* direct3D = LampGraphics::getDirect3D();
	// 深度、ステンシルフォーマットループ
	u_int depthStencilFormatCount = getDepthStencilFormatCount();
	for(u_int i = 0; i < depthStencilFormatCount; i++){
		D3DFORMAT depthStencilFormat = getDepthStencilFormat(i);
		// マルチサンプルタイプループ
		u_int multiSampleTypeCount = getMultiSampleTypeCount();
		for(u_int j = 0; j < multiSampleTypeCount; j++){
			D3DMULTISAMPLE_TYPE multiSampleType = getMultiSampleType(j);
			if(DirectXFailed(direct3D->CheckDeviceMultiSampleType(
				adapterOrdinal_, deviceType_, depthStencilFormat, isWindowed_,
				multiSampleType, NULL))){
				multiSampleConflictFormats_.add(depthStencilFormat);
				multiSampleConflictTypes_.add(multiSampleType);
			}
		}
	}
}
//------------------------------------------------------------------------------
// 頂点プロセスタイプの構築
void GraphicsDeviceComboInformation::buildVertexProcessingType(
	const D3DCapacity& deviceCapability, bool usesMixedVertexProcessing,
	ConfirmGraphicsDevice* confirmDevice){
	// Pure、Hardware、Mixed、Softwareの優先順位で使用される。
	if((deviceCapability.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0){
		// ピュアデバイス能力があるか
		if((deviceCapability.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0){
			if(confirmDevice->confirmGraphicsDevice(
				deviceCapability,
				(D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE),
				adapterFormat_, backBufferFormat_)){
				vertexProcessingTypes_.add(VertexProcessingType::pureHardware);
			}
		}
		// ハードウェア頂点プロセス
		if(confirmDevice->confirmGraphicsDevice(
			deviceCapability,
			D3DCREATE_HARDWARE_VERTEXPROCESSING,
			adapterFormat_, backBufferFormat_)){
			vertexProcessingTypes_.add(VertexProcessingType::hardware);
		}
		// 混合頂点プロセス
		if(usesMixedVertexProcessing &&
			confirmDevice->confirmGraphicsDevice(
				deviceCapability,
				D3DCREATE_MIXED_VERTEXPROCESSING,
				adapterFormat_, backBufferFormat_)){
				vertexProcessingTypes_.add(VertexProcessingType::mixed);
		}
	}
	// ソフトウェア頂点プロセス
	if(confirmDevice->confirmGraphicsDevice(
		deviceCapability,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		adapterFormat_, backBufferFormat_)){
		vertexProcessingTypes_.add(VertexProcessingType::software);
	}

}
//------------------------------------------------------------------------------
// プレゼンテーション間隔の構築
void GraphicsDeviceComboInformation::buildPresentationInterval(
	const D3DCapacity& deviceCapability){
	const u_int presentationIntervalArray[] = {
		D3DPRESENT_INTERVAL_DEFAULT,
		D3DPRESENT_INTERVAL_IMMEDIATE,
		D3DPRESENT_INTERVAL_ONE,
		D3DPRESENT_INTERVAL_TWO,
		D3DPRESENT_INTERVAL_THREE,
		D3DPRESENT_INTERVAL_FOUR,
	};
	const u_int presentationIntervalArrayCount =
		sizeof(presentationIntervalArray) / sizeof(presentationIntervalArray[0]);
	for(u_int i = 0; i < presentationIntervalArrayCount; i++){
		u_int presentationInterval = presentationIntervalArray[i];
		// ウィンドウモードの時は複数のインターバルを待つモードは使用できない。
		if(isWindowed_){
			if((presentationInterval == D3DPRESENT_INTERVAL_TWO) ||
				(presentationInterval == D3DPRESENT_INTERVAL_THREE) ||
				(presentationInterval == D3DPRESENT_INTERVAL_FOUR)){
				continue;
			}
		}
		// デフォルトは無条件で使用できる。それ以外は能力チェック
		if((presentationInterval == D3DPRESENT_INTERVAL_DEFAULT) ||
			(deviceCapability.PresentationIntervals & presentationInterval)){
			presentationInterval_.add(presentationInterval);
		}
	}

	
}
//------------------------------------------------------------------------------
// 文字列への変換
String GraphicsDeviceComboInformation::toString(){
	String result;
	// ウィンドウモード
	if(isWindowed_){ result += "Window "; }
	else{ result += "Fullscreen "; }
	// バッファフォーマット
	GraphicsBufferFormat adapter(adapterFormat_);
	GraphicsBufferFormat backBuffer(backBufferFormat_);
	String formatString;
	formatString.format("adapter %s backBuffer %s ",
		adapter.getName().getBytes(), backBuffer.getName().getBytes());
	result += formatString;
	return result;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
