//------------------------------------------------------------------------------
// 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/Material/Material.h"
#include "Graphics/Material/MaterialManager.h"
#include "Graphics/Texture/Texture.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/Renderer/DrawRequest.h"

namespace Lamp{

// ブレンドモード文字列テーブル
const String Material::blendModeStringTable[] = {
	"Disable",
	"Add",
	"Subtract",
	"InverseSubtract",
	"Minimum",
	"Maximum",
};

// ブレンドステート文字列テーブル
const String Material::blendStateStringTable[] = {
	"Zero",
	"One",
	"SourceColor",
	"InverseSourceColor",
	"SourceAlpha",
	"InverseSourceAlpha",
	"SourceAlphaSaturate",
	"DestinationColor",
	"InverseDestinationColor",
	"DestinationAlpha",
	"InverseDestinationAlpha",
};

// フォグオプション文字列テーブル
const String Material::fogOptionStringTable[] = {
	"None",
	"Disable",
	"Black",
};

//------------------------------------------------------------------------------
// コンストラクタ
Material::Material(const String& name, Scene* scene) :
	SceneObject(name, scene), startStateBlock_(NULL), endStateBlock_(NULL),
	priority_(0), blendMode_(blendModeDisable),
	blendSource_(blendStateSourceAlpha),
	blendDestination_(blendStateInverseSourceAlpha), alpha_(1.f),
	fogOption_(fogOptionNone), lightMask_(1), pipelineMode_(pipelineModeNone),
	alphaBlend_(false), zWrite_(true), zTest_(true), hasStateChanged_(true){
}
//------------------------------------------------------------------------------
// デストラクタ
Material::~Material(){
	releaseStateBlock();
}
//------------------------------------------------------------------------------
// マテリアルの値コピー
void Material::copyMaterialValue(Material* destination) const{
	destination->setPriority(priority_);
	destination->setBlendMode(blendMode_);
	destination->setBlendSource(blendSource_);
	destination->setBlendDestination(blendDestination_);
	destination->setAlpha(alpha_);
	destination->setFogOption(fogOption_);
	destination->setLightMask(lightMask_);
	destination->setZWrite(zWrite_);
	destination->setZTest(zTest_);
}
//------------------------------------------------------------------------------
// 再帰的破棄
int Material::recursiveDestroy(Material* material){
	Assert(material != NULL);
	int result = 0;
	// 子の破棄
	if(material->getReferenceCount() == 0){
		result += material->destroyChildren();
	}
	// 引数の破棄
	MaterialManager* manager = material->getScene()->getMaterialManager();
	if(manager->destroy(material) == 0){
		result++;
	}
	return result;
}
//------------------------------------------------------------------------------
// 描画の開始
void Material::drawStart(){
	// ステートが変更されていればステートブロックを再構築する
	if(hasStateChanged_){
		releaseStateBlock();
		buildStateBlock(&startStateBlock_, &endStateBlock_);
		hasStateChanged_ = false;
	}
	// 開始ステートブロックを適用する
	if(startStateBlock_ != NULL){
		RenderingDevice::getInstance()->applyStateBlock(startStateBlock_);
	}
}
//------------------------------------------------------------------------------
// 描画の終了
void Material::drawEnd(){
	// ステートが変更されていればステートブロックを再構築する
	if(hasStateChanged_){
		releaseStateBlock();
		buildStateBlock(&startStateBlock_, &endStateBlock_);
		hasStateChanged_ = false;
	}
	// 終了ステートブロックを適用する
	if(endStateBlock_ != NULL){
		RenderingDevice::getInstance()->applyStateBlock(endStateBlock_);
	}
}
//------------------------------------------------------------------------------
// 描画のセットアップ
void Material::drawSetup(DrawRequest* request){
	// 同じマテリアルなら変更しない
	if(request->isMaterialChanged()){
		// 前回の描画を終了する
		Material* preMaterial = request->getPreMaterial();
		if(preMaterial != NULL){ preMaterial->drawEnd(); }
		// 描画を開始する
		drawStart();
	}
}
//------------------------------------------------------------------------------
// ブレンディング
//------------------------------------------------------------------------------
// ブレンドモードから文字列への変換
const String& Material::blendModeToString(BlendMode blendMode){
	Assert(blendMode >= 0);
	Assert(blendMode < blendModeMax);
	return blendModeStringTable[blendMode];
}
//------------------------------------------------------------------------------
// 文字列からブレンドモードへの変換
Material::BlendMode Material::blendModeFromString(
	const String& blendModeString){
	for(int i = 0; i < blendModeMax; i++){
		if(blendModeStringTable[i].equals(blendModeString)){
			return BlendMode(i);
		}
	}
	ErrorOut("Material::blendModeFromString() " + blendModeString);
	return blendModeMax;
}
//------------------------------------------------------------------------------
// ブレンドステートから文字列への変換
const String& Material::blendStateToString(BlendState blendState){
	Assert(blendState >= 0);
	Assert(blendState < blendStateMax);
	return blendStateStringTable[blendState];
}
//------------------------------------------------------------------------------
// 文字列からブレンドステートへの変換
Material::BlendState Material::blendStateFromString(
	const String& blendStateString){
	for(int i = 0; i < blendStateMax; i++){
		if(blendStateStringTable[i].equals(blendStateString)){
			return BlendState(i);
		}
	}
	ErrorOut("Material::blendStateFromString() " + blendStateString);
	return blendStateMax;
}
//------------------------------------------------------------------------------
// フォグオプションから文字列への変換
const String& Material::fogOptionToString(FogOption fogOption){
	Assert(fogOption >= 0);
	Assert(fogOption < fogOptionMax);
	return fogOptionStringTable[fogOption];
}
//------------------------------------------------------------------------------
// 文字列からフォグオプションへの変換
Material::FogOption Material::fogOptionFromString(
	const String& fogOptionString){
	for(int i = 0; i < fogOptionMax; i++){
		if(fogOptionStringTable[i].equals(fogOptionString)){
			return FogOption(i);
		}
	}
	ErrorOut("Material::fogOptionFromString() " + fogOptionString);
	return fogOptionMax;
}
//------------------------------------------------------------------------------
// テクスチャリファレンス
//------------------------------------------------------------------------------
// テクスチャリファレンスの設定
Texture* Material::setTextureReferense(
	Texture* nowTexture, Texture* newTexture){
	if(nowTexture != NULL){ nowTexture->removeReference(this); }
	if(newTexture != NULL){ newTexture->addReference(this); }
	stateChanged();
	return newTexture;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
