/**
 * @file
 * @brief Sprite2D幚憰僋儔僗偺幚憰傪峴偆.
 * 
 * 
 * @author S.F.
 * @version $Id:
 *
 * Copyright (C) 2000-2002 Satoshi Fujiwara. All Rights Reserved.
 */
#pragma warning( disable : 4786 )	//STL偺寈崘奜偟

// 儊儌儕乕儕乕僋専弌梡
#include "sfdebug.h"

// SYSTEM INCLUDES
//
#include <stdio.h>
#include <queue>
#include "windows.h"
#include "windowsx.h"

#include "d3d11.h"
#include "d3dx11.h"
#include "dxerr8sf.h"


// PROJECT INCLUDES
//
#include "exception.h"
#include "sound.h"
#include "System.h"
#include "console.h"
#include "Obj.h"
#include "Obj2D.h"
#include "ObjRectangle.h"
#include "ObjQuadrangle.h"
#include "AbstractSprite.h"
#include "ConsoleImpl.h"


#include "Sprite2D.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

using namespace sf::system::console;
using namespace sf::system::console::sprite;

//////////////////////////////////////////////////////////////////////
// 峔抸/徚柵
//////////////////////////////////////////////////////////////////////
// 僐儞僗僩儔僋僞 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
Sprite2D::Sprite2D(LPDIRECT3DDEVICE9	pD3DDevice) : AbstractSprite()
{
	mpSpriteVtBuf = NULL;
	mpSpriteTexture = NULL;
	mCellCount = 0.0f;
	mCellHeight = mCellWidth = 0.0f;
	initialize(pD3DDevice);

}
// 僨僗僩儔僋僞 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
Sprite2D::~Sprite2D()
{
	uninitialize();
}

// 弶婜壔 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::initialize(void)
{
	uninitialize();
	mbUse = false;

}// initialize()

// 弶婜壔 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::initialize(LPDIRECT3DDEVICE9	pD3DDevice)
{
	initialize();

	mpD3DDevice = pD3DDevice;
}// initialize()

// 僗僾儔僀僩梡僀儞僞乕僼僃乕僗偺儕儕乕僗 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::uninitialize(void)
{
	if(mpSpriteVtBuf)
	{
		mpSpriteVtBuf->Release();
		mpSpriteVtBuf = NULL;
	}
//
//	if(mpSprite3DVtBuf)
//	{
//		mpSprite3DVtBuf->Release();
//		mpSprite3DVtBuf = NULL;
//	}

	if(mpSpriteTexture){
		mpSpriteTexture->Release();
		mpSpriteTexture = NULL;
	}
/*
    if( mpMeshMaterials != NULL ) 
        delete[] mpMeshMaterials;

    if( mpMeshTextures )
    {
        for( DWORD i = 0; i < mNumMaterials; i++ )
        {
            if( mpMeshTextures[i] )
                mpMeshTextures[i]->Release();
        }
        delete[] mpMeshTextures;
    }

	if( mpMesh != NULL )
        mpMesh->Release();
 */   
	mbUse = false;
}// release()

// 2D僗僾儔僀僩偺儘乕僪 -亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::load(const Info *pInfo,const D3DFORMAT textureFormat,const COLOR color)
{
	D3DXIMAGE_INFO img_info;

	HRESULT hr = D3DXCreateTextureFromFileExA(
		mpD3DDevice,
		pInfo->fileName,
		D3DX_DEFAULT,
		D3DX_DEFAULT,
		0,
		0,
		textureFormat,
		D3DPOOL_MANAGED,
		D3DX_FILTER_NONE,
		D3DX_FILTER_NONE,
		color,
		&img_info,
		NULL,
		&mpSpriteTexture);
	
	if(FAILED(hr))
	{
		std::string tmpErr("Sprite Load Error:");
		tmpErr += DXGetErrorString8(hr);
		throw FatalErrorException(tmpErr,__FILE__,__LINE__);
	}

	DWORD twidth = img_info.Width;
	DWORD theight = img_info.Height;

	mWidth = twidth;
	mHeight = theight;
	
	mCenterX = pInfo->centerX;
	mCenterY = pInfo->centerY;

	mCellWidth = pInfo->cellWidth;
	mCellHeight = pInfo->cellHeight;


/*	if(pInfo->cellWidth){
		twidth = mCellWidth = pInfo->cellWidth;
	}

	if(pInfo->cellHeight){
		theight = mCellHeight = pInfo->cellHeight;
	}
*/
	// 僶乕僥僢僋僗僶僢僼傽乮2D僗僾儔僀僩昞帵梡乯偺惗惉
	if(FAILED(hr = mpD3DDevice->CreateVertexBuffer(
		4 * sizeof(Vertex), 
		0 ,
		VertexFVF, 
		D3DPOOL_MANAGED, &mpSpriteVtBuf)))
	{
		std::string tmpErr("VertexBuffer Create Error:");
		tmpErr += DXGetErrorString8(hr);
		throw FatalErrorException(tmpErr,__FILE__,__LINE__);
	}
	
	use(true);
}//loadSprite2D()

// 俀俢僗僾儔僀僩偺昤夋乮巐妏宍乯亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::draw(const ObjRectangle * const pObj)
{

	HRESULT hr;
	
	float width;
	float height;
	float left,right,top,bottom;
	float cos_,sin_;
	float tu1,tu2,tv1,tv2;
	
	if(!pObj->visibility() || !isUsed())
		return;

	D3DCOLOR diffuse = pObj->color();
	D3DCOLOR specular = pObj->specular();

	calcUVPos(pObj,tu1,tu2,tv1,tv2);

	Vertex* tlv;
	
	if(!mCellHeight)
		height = mHeight * pObj->scalingY();
	else
		height = mCellHeight * pObj->scalingY();

	if(!mCellWidth)
		width = mWidth * pObj->scalingX();
	else
		width = mCellWidth * pObj->scalingX(); 
		
	left = - (width / 2.0f) + mCenterX;
	right = width / 2.0f + mCenterX;
	top = - (height / 2.0f) + mCenterY;
	bottom = (height / 2.0f) + mCenterY;
		
	if(pObj->angle() != 0.0f)
	{
		cos_ = cosf(pObj->angle());
		sin_ = sinf(pObj->angle());


		float left_cos = left * cos_;
		float left_sin = left * sin_;
	
		float right_cos = right * cos_;
		float right_sin = right * sin_;

		float top_cos = top * cos_;
		float top_sin = top * sin_;

		float bottom_cos = bottom * cos_;
		float bottom_sin = bottom * sin_;

		mpSpriteVtBuf->Lock(0,0,(BYTE **)&tlv,0);
	
		tlv[0].x = pObj->x() + left_cos - top_sin;
		tlv[0].y = pObj->y() + left_sin + top_cos; 
		tlv[0].z = pObj->z();
		tlv[0].diffuse = diffuse;
		tlv[0].specular = specular;
		tlv[0].tu = tu1;
		tlv[0].tv = tv1;
		tlv[0].rhw = 1.0f;
		
		tlv[1].x = pObj->x() + right_cos - top_sin;
		tlv[1].y = pObj->y() + right_sin + top_cos; 
		tlv[1].z = pObj->z();
		tlv[1].diffuse = diffuse;
		tlv[1].specular = specular;
		tlv[1].tu = tu2;
		tlv[1].tv = tv1;
		tlv[1].rhw = 1.0f;

		tlv[2].x = pObj->x() + left_cos - bottom_sin;
		tlv[2].y = pObj->y() + left_sin + bottom_cos; 
		tlv[2].z = pObj->z();
		tlv[2].diffuse = diffuse;
		tlv[2].specular = specular;
		tlv[2].tu = tu1;
		tlv[2].tv = tv2;
		tlv[2].rhw = 1.0f;
		
		
		tlv[3].x = pObj->x() + right_cos - bottom_sin;
		tlv[3].y = pObj->y() + right_sin + bottom_cos; 
		tlv[3].z = pObj->z();
		tlv[3].diffuse = diffuse;
		tlv[3].specular = specular;
		tlv[3].tu = tu2;
		tlv[3].tv = tv2;
		tlv[3].rhw = 1.0f;
	} else {
		mpSpriteVtBuf->Lock(0,0,(BYTE **)&tlv,0);
	
		tlv[0].x = pObj->x() + left;
		tlv[0].y = pObj->y() + top; 
		tlv[0].z = pObj->z();
		tlv[0].diffuse = diffuse;
		tlv[0].specular = specular;
		tlv[0].tu = tu1;
		tlv[0].tv = tv1;
		tlv[0].rhw = 1.0f;
		
		tlv[1].x = pObj->x() + right;
		tlv[1].y = pObj->y() + top; 
		tlv[1].z = pObj->z();
		tlv[1].diffuse = diffuse;
		tlv[1].specular = specular;
		tlv[1].tu = tu2;
		tlv[1].tv = tv1;
		tlv[1].rhw = 1.0f;
		
		tlv[2].x = pObj->x() + left;
		tlv[2].y = pObj->y() + bottom; 
		tlv[2].z = pObj->z();
		tlv[2].diffuse = diffuse;
		tlv[2].specular = specular;
		tlv[2].tu = tu1;
		tlv[2].tv = tv2;
		tlv[2].rhw = 1.0f;
		
		
		tlv[3].x = pObj->x() + right;
		tlv[3].y = pObj->y() + bottom; 
		tlv[3].z = pObj->z();
		tlv[3].diffuse = diffuse;
		tlv[3].specular = specular;
		tlv[3].tu = tu2;
		tlv[3].tv = tv2;
		tlv[3].rhw = 1.0f;

	}
	
	mpSpriteVtBuf->Unlock();

	mpD3DDevice->SetRenderState(D3DRS_LIGHTING,		  FALSE);

	mpD3DDevice->SetTexture(0,mpSpriteTexture);
	
	mpD3DDevice->SetVertexShader(VertexFVF);
	mpD3DDevice->SetStreamSource(0,mpSpriteVtBuf, sizeof(Vertex));
	
	setRenderState(pObj->drawMode());
	
	hr = mpD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
	mpD3DDevice->SetTexture(0,NULL);

}//  draw()


// 僗僾儔僀僩偺昤夋 ------------------------------------------------------------
void Sprite2D::draw(const ObjQuadrangle * const pObj)
{
	if(!pObj->visibility() || !isUsed() )
		return;

	HRESULT hr;
	
	float cos_,sin_;
	
	const Vertex* vertex;
	Vertex* ptlv;

	float rot_x = mCenterX;
	float rot_y = mCenterY;

	float scaling_x = pObj->scalingX();
	float scaling_y = pObj->scalingY();

	float u1,u2,v1,v2;
	
	calcUVPos(pObj,u1,u2,v1,v2);

	mpSpriteVtBuf->Lock(0,0,(BYTE **)&ptlv,0);
	
	ptlv[0].tu = u1;
	ptlv[0].tv = v1;
	ptlv[1].tu = u2;
	ptlv[1].tv = v1;
	ptlv[2].tu = u1;
	ptlv[2].tv = v2;
	ptlv[3].tu = u2;
	ptlv[3].tv = v2;

	if(pObj->angle() != 0.0f)
	{
		cos_ = cosf(pObj->angle());
		sin_ = sinf(pObj->angle());


		for(int i = 0;i < 4;i++)
		{
			vertex = pObj->vertex(i);

			ptlv[i].x = pObj->x() 
				+ ( vertex->x - rot_x) * cos_
				- ( vertex->y - rot_y) * sin_;
			ptlv[i].y = pObj->y() 
				+ ( vertex->x - rot_x) * sin_
				+ ( vertex->y - rot_y) * cos_;

			ptlv[i].diffuse = vertex->diffuse;
			ptlv[i].specular = vertex->specular;
//			ptlv[i].tu = vertex->tu;
//			ptlv[i].tv = vertex->tv;
			ptlv[i].rhw = 1.0f;
		}
		
	} else {

		for(int i = 0;i < 4;i++)
		{
			vertex = pObj->vertex(i);

			ptlv[i].x = pObj->x() + vertex->x * scaling_x;
			ptlv[i].y = pObj->y() + vertex->y * scaling_y;

			ptlv[i].diffuse = vertex->diffuse;
			ptlv[i].specular = vertex->specular;
//			ptlv[i].tu = vertex->tu;
//			ptlv[i].tv = vertex->tv;
			ptlv[i].rhw = 1.0f;
		}

	}
	
	mpSpriteVtBuf->Unlock();
	
	mpD3DDevice->SetTexture(0,mpSpriteTexture);
	mpD3DDevice->SetVertexShader(VertexFVF);

	mpD3DDevice->SetStreamSource(0,mpSpriteVtBuf,sizeof(Vertex));

	setRenderState(pObj->drawMode());
	mpD3DDevice->SetRenderState(D3DRS_LIGHTING,		  FALSE);
	
	hr = mpD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
	mpD3DDevice->SetTexture(0,NULL);
		
}// draw

// 僥僋僗僠儍偺埵抲傪媮傔傞 亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅亅
void Sprite2D::calcUVPos(const Obj2D * const pObj,float& u1,float& u2,float& v1,float& v2)
{
	float cu1,cu2,cv1,cv2;
	
	if(mCellWidth){
		int cell_ix = pObj->index() % (int)(mWidth / mCellWidth);
		cu1 = (float)cell_ix * mCellWidth / mWidth;
		cu2 = (float)(cell_ix + 1) * (mCellWidth - 0.008f) / mWidth;
	} else {
		cu1 = 0.0f;
		cu2 = 1.0f;
	}

	if(mCellHeight){
		int cell_iy = pObj->index() / (mWidth / mCellHeight);
		cv1 = (float)cell_iy * mCellHeight / mHeight;
		cv2 = (float)(cell_iy + 1) * (mCellHeight - 0.008f) / mHeight;
	} else {
		cv1 = 0.0f;
		cv2 = 1.0f;
	}

	// X斀揮
	if(pObj->reverseX()){
		u1 = cu2;
		u2 = cu1;
	} else {
		u1 = cu1;
		u2 = cu2;
	}

	// Y斀揮
	if(pObj->reverseY()){
		v1 = cv2;
		v2 = cv1;
	} else {
		v1 = cv1;
		v2 = cv2;
	}

};
