//------------------------------------------------------------------------------
// 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
 */

#ifndef MATERIAL_H_
#define MATERIAL_H_

#include <Graphics/Scene/SceneObject.h>
#include <Graphics/System/GraphicsDeviceObjectHolder.h>
#include <Core/Container/ArrayList.h>

namespace Lamp{

class Mesh;
class Texture;
class DrawRequest;
class BasicMaterial;

//------------------------------------------------------------------------------
/**
 * マテリアル
 */
class Material : public SceneObject , public GraphicsDeviceObjectHolder{
friend class SceneObjectManagerTemplate<Material>;
friend class MaterialManager;
friend class Mesh;
friend class Renderer;
public:
	//--------------------------------------------------------------------------
	/**
	 * リファレンスカウントの取得
	 * @return リファレンスカウント
	 */
	virtual int getReferenceCount() const{ return parents_.getCount(); }

	//--------------------------------------------------------------------------
	/**
	 * コピー
	 * @param copyMask コピーマスク
	 * @return コピーされたマテリアル
	 */
	virtual Material* copy(u_int copyMask = 0) const = 0;

	/**
	 * 再帰的破棄
	 * @param material 破棄するマテリアル
	 * @return 破棄したオブジェクト数
	 */
	static int recursiveDestroy(Material* material);

	//--------------------------------------------------------------------------
	/**
	 * ステート変更フラグを立てる
	 */
	virtual void stateChanged(){ hasStateChanged_ = true; }

	/**
	 * ステート変更フラグの取得
	 * @return ステート変更フラグ
	 */
	virtual bool hasStateChanged() const{ return hasStateChanged_; }

	//--------------------------------------------------------------------------
	/**
	 * 親の数取得
	 * @return 親の数
	 */
	virtual int getParentCount() const{ return parents_.getCount(); }

	/**
	 * 親の取得
	 * @param index 親のインデックス
	 * @return 親
	 */
	virtual Mesh* getParent(int index) const{
		Assert(index >= 0);
		Assert(index < getParentCount());
		return parents_.get(index);
	}

	//--------------------------------------------------------------------------
	/**
	 * ライトを使用するか
	 * @return ライトを使用するならtrue
	 */
	virtual bool useLight() const{ return true; }

	//--------------------------------------------------------------------------
	/**
	 * 優先度の設定
	 * @param priority 優先度
	 */
	virtual void setPriority(int priority){
		priority_ = priority;
		stateChanged();
	}

	/**
	 * 優先度の取得
	 * @return 優先度
	 */
	virtual int getPriority() const{ return priority_; }

	//--------------------------------------------------------------------------
	// ブレンディング
	//--------------------------------------------------------------------------
	/// ブレンドモード
	enum BlendMode{
		blendModeDisable = 0,
		blendModeAdd,
		blendModeSubtract,
		blendModeInverseSubtract,
		blendModeMinimum,
		blendModeMaximum,
		blendModeMax,
	};

	/**
	 * ブレンドモードから文字列への変換
	 * @param blendMode ブレンドモード
	 * @return ブレンドモード文字列
	 */
	static const String& blendModeToString(BlendMode blendMode);

	/**
	 * 文字列からブレンドモードへの変換
	 * @param blendModeString ブレンドモード文字列
	 * @return ブレンドモード
	 */
	static BlendMode blendModeFromString(const String& blendModeString);

	//--------------------------------------------------------------------------
	/**
	 * ブレンドモードの設定
	 * @param blendMode ブレンドモード
	 */
	virtual void setBlendMode(BlendMode blendMode){
		blendMode_ = blendMode;
		stateChanged();
	}

	/**
	 * ブレンドモードの取得
	 * @return ブレンドモード
	 */
	virtual BlendMode getBlendMode() const{ return blendMode_; }

	/**
	 * ブレンドが有効か
	 * @return ブレンドが有効ならtrue
	 */
	virtual bool isBlendEnabled() const{
		return (blendMode_ != blendModeDisable);
	}

	//--------------------------------------------------------------------------
	/**
	 * アルファの設定
	 * @param alpha アルファ
	 */
	virtual void setAlpha(float alpha){
		alpha_ = alpha;
		stateChanged();
	}

	/**
	 * アルファの取得
	 * @return アルファ
	 */
	virtual float getAlpha() const{ return alpha_; }

	//--------------------------------------------------------------------------
	/// ブレンドステート
	enum BlendState{
		blendStateZero = 0,
		blendStateOne,
		blendStateSourceColor,
		blendStateInverseSourceColor,
		blendStateSourceAlpha,
		blendStateInverseSourceAlpha,
		blendStateSourceAlphaSaturate,
		blendStateDestinationColor,
		blendStateInverseDestinationColor,
		blendStateDestinationAlpha,
		blendStateInverseDestinationAlpha,
		blendStateMax,
	};

	/**
	 * ブレンドステートから文字列への変換
	 * @param blendState ブレンドステート
	 * @return ブレンドステート文字列
	 */
	static const String& blendStateToString(BlendState blendState);

	/**
	 * 文字列からブレンドステートへの変換
	 * @param blendStateString ブレンドステート文字列
	 * @return ブレンドステート
	 */
	static BlendState blendStateFromString(const String& blendStateString);

	//--------------------------------------------------------------------------
	/**
	 * ブレンドソースの設定
	 * @param blendSource ブレンドソース
	 */
	virtual void setBlendSource(BlendState blendSource){
		Assert(blendSource >= 0);
		Assert(blendSource < blendStateMax);
		blendSource_ = blendSource;
		stateChanged();
	}

	/**
	 * ブレンドソースの取得
	 * @return ブレンドソース
	 */
	virtual BlendState getBlendSource() const{ return blendSource_; }

	//--------------------------------------------------------------------------
	/**
	 * ブレンドデスティネーションの設定
	 * @param blendDestination ブレンドデスティネーション
	 */
	virtual void setBlendDestination(BlendState blendDestination){
		Assert(blendDestination_ >= 0);
		Assert(blendDestination_ < blendStateMax);
		blendDestination_ = blendDestination;
		stateChanged();
	}

	/**
	 * ブレンドデスティネーションの取得
	 * @return ブレンドデスティネーション
	 */
	virtual BlendState getBlendDestination() const{
		return blendDestination_;
	}

	//--------------------------------------------------------------------------
	// Zテスト
	//--------------------------------------------------------------------------
	/**
	 * Z書き込みの設定
	 * @param zWrite Z書き込みを行うならtrue
	 */
	virtual void setZWrite(bool zWrite){
		zWrite_ = zWrite;
		stateChanged();
	}

	/**
	 * Z書き込みの取得
	 * @return Z書き込みを行うならtrue
	 */
	virtual bool useZWrite() const{ return zWrite_; }

	//--------------------------------------------------------------------------
	/**
	 * Zテストの設定
	 * @param zTest Zテストを行うならtrue
	 */
	virtual void setZTest(bool zTest){
		zTest_ = zTest;
		stateChanged();
	}

	/**
	 * Zテストの取得
	 * @return Zテストを行うならtrue
	 */
	virtual bool useZTest() const{ return zTest_; }

	//--------------------------------------------------------------------------
	// フォグオプション
	//--------------------------------------------------------------------------
	/// フォグオプション
	enum FogOption{
		fogOptionNone = 0,
		fogOptionDisable,
		fogOptionBlack,
		fogOptionMax,
	};

	/**
	 * フォグオプションから文字列への変換
	 * @param fogOption フォグオプション
	 * @return フォグオプション文字列
	 */
	static const String& fogOptionToString(FogOption fogOption);

	/**
	 * 文字列からフォグオプションへの変換
	 * @param fogOptionString フォグオプション文字列
	 * @return フォグオプション
	 */
	static FogOption fogOptionFromString(const String& fogOptionString);

	//--------------------------------------------------------------------------
	/**
	 * フォグオプションの設定
	 * @param fogOption フォグオプション
	 */
	virtual void setFogOption(FogOption fogOption){
		Assert(fogOption >= 0);
		Assert(fogOption < fogOptionMax);
		fogOption_ = fogOption;
		stateChanged();
	}

	/**
	 * フォグオプションの取得
	 * @return フォグオプション
	 */
	virtual FogOption getFogOption() const{ return fogOption_; }

	//--------------------------------------------------------------------------
	// ライトマスク
	//--------------------------------------------------------------------------
	/**
	 * ライトマスクの設定
	 * @param lightMask ライトマスク
	 */
	virtual void setLightMask(u_int lightMask){ lightMask_ = lightMask; }

	/**
	 * ライトマスクの取得
	 * @return ライトマスク
	 */
	virtual u_int getLightMask() const{ return lightMask_; }

	//--------------------------------------------------------------------------
	// パイプラインモード
	//--------------------------------------------------------------------------
	/// パイプラインモード
	enum PipelineMode{
		pipelineModeNone = 0,
		pipelineModeFixed,
		pipelineModeProgrammableShader2,
	};

	/**
	 * パイプラインモードの取得
	 * @return パイプラインモード
	 */
	virtual PipelineMode getPipelineMode() const{
		Assert(pipelineMode_ != pipelineModeNone);
		return pipelineMode_;
	}

	//--------------------------------------------------------------------------
	// デバイスオブジェクト管理
	//--------------------------------------------------------------------------
	/**
	 * デバイスオブジェクトの初期化
	 * @return 成功したらtrueを返す
	 */
	virtual bool initializeGraphicsDeviceObjects(){ return true; }

	/**
	 * デバイスオブジェクトの削除
	 */
	virtual void deleteGraphicsDeviceObjects(){}

	/**
	 * デバイスオブジェクトのリストア
	 * @return 成功したらtrueを返す
	 */
	virtual bool restoreGraphicsDeviceObjects(){
		buildStateBlock(&startStateBlock_, &endStateBlock_);
		return true;
	}

	/**
	 * デバイスオブジェクトの無効化
	 */
	virtual void invalidateGraphicsDeviceObjects(){ releaseStateBlock(); }

	//--------------------------------------------------------------------------
	// RTTI
	//--------------------------------------------------------------------------
	/**
	 * マテリアルかどうか
	 * @return マテリアルならtrue
	 */
	virtual bool isMaterial() const{ return true; }

	//--------------------------------------------------------------------------
	/**
	 * 基本マテリアルかどうか
	 * @return 基本マテリアルならtrue
	 */
	virtual bool isBasicMaterial() const{ return false; }

	/**
	 * 基本マテリアルへのキャスト
	 * @return 基本マテリアル。型が違えばNULLを返す。
	 */
	virtual BasicMaterial* castBasicMaterial() const{
		if(isBasicMaterial()){ return (BasicMaterial*)this; }
		return NULL;
	}

	//--------------------------------------------------------------------------
protected:
	/**
	 * コンストラクタ
	 * @param name 名前
	 * @param scene シーン
	 */
	Material(const String& name, Scene* scene);

	/**
	 * デストラクタ
	 */
	virtual ~Material();

	/**
	 * マテリアルの値コピー
	 * @param destination コピー先マテリアル
	 */
	virtual void copyMaterialValue(Material* destination) const;

	/**
	 * 子の破棄
	 * @return 破棄したオブジェクト数
	 */
	virtual int destroyChildren() = 0;

	//--------------------------------------------------------------------------
	// 描画
	//--------------------------------------------------------------------------
	/**
	 * 描画のセットアップ
	 * @param request 描画リクエスト
	 */
	virtual void drawSetup(DrawRequest* request);

	/**
	 * 描画
	 * @param request 描画リクエスト
	 */
	virtual void draw(DrawRequest* request) = 0;

	//--------------------------------------------------------------------------
	/**
	 * ステートブロックの構築
	 * @param startBlock [out]開始ステートブロック
	 * @param endBlock [out]終了ステートブロック
	 */
	virtual void buildStateBlock(
		Direct3DStateBlock** startBlock, Direct3DStateBlock** endBlock) = 0;

	/**
	 * ステートブロックの解放
	 */
	virtual void releaseStateBlock(){
		SafeRelease(endStateBlock_);
		SafeRelease(startStateBlock_);
	}

	/**
	 * パイプラインモードの設定
	 * @param pipelineMode パイプラインモード
	 */
	virtual void setPipelineMode(PipelineMode pipelineMode){
		Assert(pipelineMode != pipelineModeNone);
		pipelineMode_ = pipelineMode;
	}

	//--------------------------------------------------------------------------
	/**
	 * 参照の追加
	 * @param parent 親
	 * @return 参照カウント
	 */
	virtual int addReference(Mesh* parent){
		parents_.add(parent);
		return getParentCount();
	}

	/**
	 * 参照の削除
	 * @param parent 親
	 * @return 参照カウント
	 */
	virtual int removeReference(Mesh* parent){
		parents_.removeByValue(parent);
		return getParentCount();
	}

	//--------------------------------------------------------------------------
	// テクスチャリファレンス
	//--------------------------------------------------------------------------
	/**
	 * テクスチャリファレンスの設定
	 * @param nowTexture 現在のテクスチャ
	 * @param newTexture 新しいテクスチャ
	 * @return 新しいテクスチャ
	 */
	virtual Texture* setTextureReferense(
		Texture* nowTexture, Texture* newTexture);

private:
	//--------------------------------------------------------------------------
	// 描画
	//--------------------------------------------------------------------------
	/**
	 * 描画の開始
	 */
	virtual void drawStart();

	/**
	 * 描画の終了
	 */
	virtual void drawEnd();

	//--------------------------------------------------------------------------
	// 親配列
	ArrayList<Mesh*> parents_;
	// 開始ステートブロック
	Direct3DStateBlock* startStateBlock_;
	// 終了ステートブロック
	Direct3DStateBlock* endStateBlock_;
	// 優先度
	int priority_;
	// ブレンドモード
	BlendMode blendMode_;
	// ブレンドソース
	BlendState blendSource_;
	// ブレンドデスティネーション
	BlendState blendDestination_;
	// アルファ
	float alpha_;
	// フォグオプション
	FogOption fogOption_;
	// ライトマスク
	u_int lightMask_;
	// パイプラインモード
	PipelineMode pipelineMode_;
	// アルファブレンド
	bool alphaBlend_;
	// Z書き込み
	bool zWrite_;
	// Zテスト
	bool zTest_;
	// ステートステート変更フラグ
	bool hasStateChanged_;

	// ブレンドモード文字列テーブル
	static const String blendModeStringTable[];
	// ブレンドステート文字列テーブル
	static const String blendStateStringTable[];
	// フォグオプション文字列テーブル
	static const String fogOptionStringTable[];

};

//------------------------------------------------------------------------------
} // End of namespace Lamp
#endif // End of MATERIAL_H_
//------------------------------------------------------------------------------
