/*!
@auther d金魚
@since 2004/2/7
@version 1.02
@note
DxLibでサンプルを書いて見ました^^
<h3>見てね^^</h3>
このソースコードはBSD Licence的な考えで自由に使ってOKです^^
簡単に言うと、著作者への配慮を忘れなければ自由って事です。

例:以下のような事は止めてください。BSD Licence的な考えに反します。
これは私が作った!だからみんな使うな!って感じで主張する事のような事。
このソースつかったら被害が出た。賠償金払え!見たいな事。

<h3>謝辞</h3>
DxLibの製作者、山田 巧 氏に感謝します。
http://homepage2.nifty.com/natupaji/DxLib/
*/

#include "DxLib.h"
#include "BMLDLL.h"
//**********************************************************
///ベンチマーク用設定でコンパイルする
#define BML_BENCHMARK 1


//**********************************************************
//ユーティリティ関数

inline bool CheckCollision(const RECT &a,const RECT &g){
	return !(a.right<g.left || a.left>g.right ||
		a.top>g.bottom || a.bottom<g.top);	
}


//**********************************************************
///超簡易プレイヤークラス
class TestPlayer{
public:
	
	TestPlayer(){
		mx = 0;
		my = 0;
	}
	~TestPlayer(){}
	typedef int Coordinates;

	Coordinates mx,my;
	Coordinates getX(){return mx;}
	Coordinates getY(){return my;}
	void setX(Coordinates x){mx = x;}
	void setY(Coordinates y){my = y;}
	void setXY(Coordinates x,Coordinates y){
		setX(x);
		setY(y);
	}
	void addX(Coordinates x){	mx += x;}
	void addY(Coordinates y){	my += y;}

	virtual void Move(){
		
		int angle=-1;	//とりあえず角度を-1にしておく
		//[38]↑	[40]↓	[37]←	[39]→
		bool right = false,left = false,up = false,down = false;
		//::ProcessMessage();
		if(::CheckHitKey(KEY_INPUT_RIGHT)){
			right = true;
		}
		if(::CheckHitKey(KEY_INPUT_LEFT)){
			left = true;
		}
		if(::CheckHitKey(KEY_INPUT_UP)){
			up = true;
		}
		if(::CheckHitKey(KEY_INPUT_DOWN)){
			down = true;
		}

		if(up && right)	//右上
			angle=45;
		else
		if(left && up)	//左上
			angle=135;
		else
		if(left && down)	//左下
			angle=225;
		else
		if(down && right)	//右下
			angle=315;
		else
		if(up)	//↑
			angle=90;
		else
		if(down)	//↓
			angle=270;
		else
		if(left)	//←
			angle=180;
		else
		if(right)	//→
			angle=0;
	
		//angleの値が変わっていたらキャラクタの座標を変更する
		if(angle!=-1){
			int mv = 3;//3の速さ
			addX(BML_HELPER_AngleToCos(angle)*mv);
			addY(-BML_HELPER_AngleToSin(angle)*mv);
			//cx+=fcos[angle]*mv;
			//cy-=fsin[angle]*mv;
		}
		return;
			
	}
	virtual void Draw(int color){
		DrawBox(getX(),getY(),getX() + 3,getY() + 3,color,TRUE);
		return;
	}

};

///ここでテストプレイヤーを宣言
TestPlayer gPlayer;


//**********************************************************
//簡易FPSマネージメント


class	FPSManager{
	///時間を保存しておくもの
	DWORD mReserveTime;
	///次のFPS更新するときの時間の差分を導くための時間保存
	DWORD mFPSReserveTime;
	///今のフレームカウント
	DWORD m_CountFrame;
	///2回Beginを呼び出さないようにする。カウンタ保存変数
	DWORD m_NowFrame;
	///1FPSあたりの時間
	DWORD mFPSSec;
	///FPSのカウント
	DWORD mFPSCount;
	///今のFPS
	DWORD mNowFPS;

	bool mFPSManagementEnable;

	/*DWORD FrameInit(){ 
		m_NowFrame=0;
		return	m_CountFrame = 0;
	}*/
protected:	
	DWORD IncCountFrame(){ return m_CountFrame++;}
public:
	DWORD NowCountFrame(){ return m_CountFrame;}
	FPSManager(){
		mReserveTime = 0;
		m_CountFrame = 0;
		mFPSSec = 0;
		m_NowFrame = 0;
		mFPSCount = 0;
		mFPSReserveTime = 0;
		SetFPS(60);//defaultは60
		//FPS管理は行う。
		mFPSManagementEnable = true;
	}
	void ClearCountFrame(){
		m_NowFrame = 0;
		m_CountFrame = 0;
	}
	
	virtual ~FPSManager(){}
	///1フレームに1回呼び出してほしいもの(ループの最初に呼び出すこと
	void Begin(){
		if( NowCountFrame() == m_NowFrame) return;
		
		//フレームカウントを代入
		m_NowFrame = NowCountFrame();

			//次へのタイムを保存
		DWORD getTime = ::timeGetTime();
		mReserveTime = getTime;
		//FPSの更新処理
		if (getTime - mFPSReserveTime > 1000) 
		{
			mNowFPS = mFPSCount;//結果を入れるなり
			mFPSCount = 0; //カウンタをゼロクリア
			mFPSReserveTime = getTime;//ここで次への時間の差分をゲット!
		}
	
	}
	void SetFPS(int seq){
		seq *= 2;//なぜかFPSが合わないのでこういう風にするしかない^^;
		//だれかどこが間違っているか教えて^^;;;
		mFPSSec = 1000/seq;
	}
	///@param flag[in] FALSEだとFPS管理を行わない。
	void Enable(bool flag){
		mFPSManagementEnable = flag;
	}
	
	void FPS_Management(){


	}
	void End()
	{
		/*do{
			:Sleep(CPU_SLEEP_TIME);
		}while(::timeGetTime() < wait_time + FPSSec);
		*/
		if(true==mFPSManagementEnable)
		{
			DWORD t = ::timeGetTime();
			if(t < mReserveTime + mFPSSec){
				DWORD tmp = (mReserveTime + mFPSSec - t);
				tmp = (tmp > 20) ? tmp - 20 : tmp;
				Sleep(tmp);
			}
			t = ::timeGetTime();
			while(t < mReserveTime + mFPSSec){
				DWORD tmp = (mReserveTime + mFPSSec - t);
				tmp = (tmp > 1) ? tmp - 1 : tmp;
				Sleep(tmp);
				t = ::timeGetTime();
			}
		}
		mFPSCount ++;//FPSカウントをUPするなり!
		
		//フレームをインクリメント
		IncCountFrame();
	}
	DWORD GetFPS()const{return mNowFPS;}
	inline int DrawFPS(int x,int y,int color){
		return ::DrawFormatString(x,y,color,"FPS = %d",GetFPS());
	}
};


//**********************************************************
//コールバック関数郡

BOOL CALLBACK GetPlayerCollision(BML_PLAYER_CALLBACK_DATA *data){
	int x,y;
	x = gPlayer.getX();
	y = gPlayer.getY();
	data->collision.left = x;
	data->collision.top = y;
	data->collision.right = x + 1;
	data->collision.bottom = y + 1;
//	data->result = TRUE;
	return TRUE;
}

///画面から消えたらTRUE
BOOL CALLBACK DisplayCheckCollisionCallback(const RECT *myrect,BML_BULLET_DATA *data){
	RECT b={data->x,data->y,data->x + 1,data->y + 1};
	return (!CheckCollision(*myrect,b));
}


//**********************************************************
//メイン関数

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
				 LPSTR lpCmdLine, int nCmdShow )
{
	char buff[MAX_PATH];//XMLへのパス
	//フォルダオープンダイアログを表示
	if(edk_Not_Selected == BML_HELPER_OpenBMLSelectDialog(buff,sizeof(buff),NULL))
	{
		return 1;
	}
	
	ChangeWindowMode(TRUE);//Window Mode
	
	//DxLibを初期化
	if(0 != DxLib_Init()){return -1;}
	
	SetDrawScreen(DX_SCREEN_BACK);//back buffer write mode


	//BMLDLLを初期化 10000個の弾領域、スピードは1、200個の空き領域保存キュー
	if(DKUTIL_FAILED(BML_Init(10000,1,200)))
	{//error
		return -1;
	}


	int SimpleColor = ::GetColor(255,0,0);//赤色
	int ActionColor = ::GetColor(0,255,255);//水色
	int PlayerColor = ::GetColor(255,255,255);//白


	//BulletMLが定義されたXMLを読み込む。
	int bml = BML_LoadXMLFile(buff);
	if(bml==-1) return -1;

	char dummywork[1];
	//フレームをクリア
	BML_ClearFrame();

	//プレイヤーの座標をセット
	gPlayer.setX(320);
	gPlayer.setY(240);


	FPSManager fps;
	//FPSのウェイトをTRUEにする。
	fps.Enable(TRUE);
	while(ProcessMessage() != -1){
		//FPS計測開始
		fps.Begin();

		if(CheckHitKey(KEY_INPUT_ESCAPE)==1)
		{//ESCキーが押されたらこのループを抜ける。
			break;
		}
		//画面をクリア
		ClsDrawScreen();

#	ifdef BML_BENCHMARK
		if(BML_GetFrame() % 10 == 0 || BML_GetFrame()==0)
#	else
		if(BML_GetFrame() % 60 == 0 || BML_GetFrame()==0)
#	endif
		{//60フレームに1回生成。
			BML_CreateBullet(bml,
				320,//生成する座標
				240,
				dummywork,//タスクワーク
				sizeof(dummywork),
				GetPlayerCollision//プレイヤーへの座標を得るコールバック関数
			);
		}
		//移動処理
		
		//弾の移動 ここでフレームをひとつインクリメント
		BML_Move();
		//プレイヤーの移動
		gPlayer.Move();

		//この範囲を出たら弾を削除するようにする。
		RECT disp={0 - 5,0 - 5,640 + 5,480 + 5};
		//コールバック関数を指定して、コリジョンチェック
		BML_HELPER_HitDelete(
			&disp,//この矩形をDisplayCheckCollisionCallbackの第一引数に渡す
			NULL,//NULLだと内部で用意してくれる
			DisplayCheckCollisionCallback//チェック関数
		);

		//libBulletMLの処理
		BML_Update();
		

		//描画処理 

		//弾の描画はヘルパ関数に任せる。
		BML_HELPER_DrawBullet(SimpleColor,ActionColor,DrawBox);
		//プレイヤーの描画
		gPlayer.Draw(PlayerColor);

		//弾数を表示
		::DrawFormatString(0,0,PlayerColor,
			"弾数 = %d , PlayerX=%d PlayerY=%d",
			BML_GetBulletNum(),gPlayer.getX(),gPlayer.getY()
		);
		//FPS計測終了
		fps.End();
		//FPS描画
		fps.DrawFPS(0,16,PlayerColor);

		ScreenCopy();
		Sleep(1);
	}

	BML_End();
	
	DxLib_End();
	return 0;
}