#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <d3d9.h>
#include <d3dx9math.h>
#include <assert.h>

#include "PajaTypes.h"
#include "BBox2C.h"
#include "ColorC.h"
#include "Vector2C.h"
#include "DeviceInterfaceI.h"
#include "GUIDrawInterfaceI.h"
#include "DX9ViewportC.h"
#include "DX9GUIDrawInterfaceC.h"

using namespace PajaTypes;
using namespace PajaSystem;
using namespace PluginClass;
using namespace Edit;


static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}


#define FVF_XYZCOLOR_VERTEX			(D3DFVF_XYZ | D3DFVF_DIFFUSE)
#define FVF_XYZWCOLOR_VERTEX			(D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

// Helper function to stuff a FLOAT into a DWORD argument
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }

#define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }


DX9GUIDrawInterfaceC::DX9GUIDrawInterfaceC() :
	m_pDevice( 0 ),
	m_dwCurColor( 0xffffffff ),
	m_pFont( 0 ),
	m_pVertBuffer( 0 ),
	m_nVerts( 0 )
{
}

DX9GUIDrawInterfaceC::DX9GUIDrawInterfaceC( DX9DeviceC* pDevice ) :
	m_pDevice( pDevice ),
	m_dwCurColor( 0xffffffff ),
	m_pFont( 0 ),
	m_pVertBuffer( 0 ),
	m_nVerts( 0 )
{
}

DX9GUIDrawInterfaceC::~DX9GUIDrawInterfaceC()
{
//	if( m_pFont )
//		m_pFont->DeleteDeviceObjects();
//	delete m_pFont;

	delete [] m_pVertBuffer;
}


DataBlockI*
DX9GUIDrawInterfaceC::create()
{
	return new DX9GUIDrawInterfaceC;
}

DX9GUIDrawInterfaceC*
DX9GUIDrawInterfaceC::create_new( DX9DeviceC* pDevice )
{
	return new DX9GUIDrawInterfaceC( pDevice );
}

ClassIdC
DX9GUIDrawInterfaceC::get_class_id() const
{
	return CLASS_DX9_GUIINTERFACE;
}

const char*
DX9GUIDrawInterfaceC::get_class_name()
{
	return "DirectX 9 GUIDI";
}

void
DX9GUIDrawInterfaceC::draw_text( const PajaTypes::Vector2C& rPos, const char* szStr )
{
	if( m_pFont )
	{
		DX9ViewportC*	pIface = (DX9ViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
		Vector2C	rClientPos = pIface->layout_to_client( rPos );
		m_pFont->DrawText( rClientPos[0], (float32)pIface->get_height() - rClientPos[1], m_dwCurColor, szStr );
	}
}

void
DX9GUIDrawInterfaceC::use_font( HFONT hFont )
{
/*	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;
	delete m_pFont;
	m_pFont = new CD3DFont( hFont );
	m_pFont->InitDeviceObjects( pD3DDevice );
	m_pFont->RestoreDeviceObjects();*/
}

void
DX9GUIDrawInterfaceC::draw_marker( const Vector2C& rPos, float32 f32Size )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	XYZCOLOR_VERTEX	rLoop[17];

	// Draw circle
	float32	f32Angle = 0;
	float32	f32DeltaAngle = (2.0f * (float32)M_PI) / 16.0f;

	for( uint32 i = 0; i < 16; i++ )
	{
		rLoop[i].x = rPos[0] + (float32)sin( f32Angle ) * f32Size;
		rLoop[i].y = rPos[1] + (float32)cos( f32Angle ) * f32Size;
		rLoop[i].z = 0;
		rLoop[i].color = m_dwCurColor;

		f32Angle += f32DeltaAngle;
	}
	rLoop[16] = rLoop[0];

	// Draw cross
	rLoop[0] = rLoop[2];
	rLoop[1] = rLoop[10];

	rLoop[2] = rLoop[6];
	rLoop[3] = rLoop[14];

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, 2, rLoop, sizeof( XYZCOLOR_VERTEX ) );
}

void
DX9GUIDrawInterfaceC::draw_box( const Vector2C& rMin, const Vector2C& rMax )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	XYZCOLOR_VERTEX	rLoop[5];
	rLoop[0].x = rMin[0];
	rLoop[0].y = rMin[1];
	rLoop[0].z = 0;
	rLoop[0].color = m_dwCurColor;

	rLoop[1].x = rMax[0];
	rLoop[1].y = rMin[1];
	rLoop[1].z = 0;
	rLoop[1].color = m_dwCurColor;

	rLoop[2].x = rMax[0];
	rLoop[2].y = rMax[1];
	rLoop[2].z = 0;
	rLoop[2].color = m_dwCurColor;

	rLoop[3].x = rMin[0];
	rLoop[3].y = rMax[1];
	rLoop[3].z = 0;
	rLoop[3].color = m_dwCurColor;

	rLoop[4] = rLoop[0];

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 4, rLoop, sizeof( XYZCOLOR_VERTEX ) );
}

void
DX9GUIDrawInterfaceC::begin_layout()
{
	DX9ViewportC*	pIface = (DX9ViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	BBox2C	rViewport = pIface->get_viewport();
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
	pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	pD3DDevice->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE );

	D3DXMATRIX	Identity;
	D3DXMatrixIdentity( &Identity );

	D3DXMATRIX	Proj;
	D3DXMatrixOrthoOffCenterLH( &Proj, rViewport[0][0], rViewport[1][0], rViewport[0][1], rViewport[1][1], -1.0f, 1.0f );

	pD3DDevice->SetTransform( D3DTS_PROJECTION, &Proj );
	pD3DDevice->SetTransform( D3DTS_VIEW, &Identity );
	pD3DDevice->SetTransform( D3DTS_WORLD, &Identity );

	pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
}

void
DX9GUIDrawInterfaceC::end_layout()
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
}

void
DX9GUIDrawInterfaceC::draw_layout( const ColorC& rLayoutCol )
{
	DX9ViewportC*	pIface = (DX9ViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );

	BBox2C	rLayout = pIface->get_layout();

	BBox2C	rViewport = pIface->get_viewport();
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	DWORD	dwCol = D3DCOLOR_ARGB( (int32)(rLayoutCol[3] * 255), (int32)(rLayoutCol[0] * 255), (int32)(rLayoutCol[1] * 255), (int32)(rLayoutCol[2] * 255) );

	XYZCOLOR_VERTEX	rQuad[4];
	rQuad[0].x = rLayout[0][0];
	rQuad[0].y = rLayout[0][1];
	rQuad[0].z = 0;
	rQuad[0].color = dwCol;

	rQuad[1].x = rLayout[1][0];
	rQuad[1].y = rLayout[0][1];
	rQuad[1].z = 0;
	rQuad[1].color = dwCol;

	rQuad[2].x = rLayout[1][0];
	rQuad[2].y = rLayout[1][1];
	rQuad[2].z = 0;
	rQuad[2].color = dwCol;

	rQuad[3].x = rLayout[0][0];
	rQuad[3].y = rLayout[1][1];
	rQuad[3].z = 0;
	rQuad[3].color = dwCol;

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, rQuad, sizeof( XYZCOLOR_VERTEX ) );
}

void
DX9GUIDrawInterfaceC::set_color( const ColorC& rColor )
{
	m_dwCurColor = D3DCOLOR_ARGB( (int32)(rColor[3] * 255), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
}

void
DX9GUIDrawInterfaceC::set_point_size( float32 f32Size )
{
	m_f32PointSize = f32Size;
}

void
DX9GUIDrawInterfaceC::draw_line( const Vector2C& rFrom, const Vector2C& rTo )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	XYZCOLOR_VERTEX	rLine[2];
	rLine[0].x = rFrom[0];
	rLine[0].y = rFrom[1];
	rLine[0].z = 0;
	rLine[0].color = m_dwCurColor;

	rLine[1].x = rTo[0];
	rLine[1].y = rTo[1];
	rLine[1].z = 0;
	rLine[1].color = m_dwCurColor;

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, rLine, sizeof( XYZCOLOR_VERTEX ) );
}

void
DX9GUIDrawInterfaceC::draw_point( const Vector2C& rPos )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	DX9ViewportC*	pIface = (DX9ViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	Vector2C	rConvPos = pIface->layout_to_client( rPos );
	rConvPos[1] = (float)pIface->get_height() - rConvPos[1];


	float32	f32PtSize = m_f32PointSize / 2.0f;

	XYZWCOLOR_VERTEX	rQuad[4];

	rQuad[0].x = rConvPos[0] - f32PtSize;
	rQuad[0].y = rConvPos[1] - f32PtSize;
	rQuad[0].z = 0;
	rQuad[0].w = 1.0f;
	rQuad[0].color = m_dwCurColor;

	rQuad[1].x = rConvPos[0] + f32PtSize;
	rQuad[1].y = rConvPos[1] - f32PtSize;
	rQuad[1].z = 0;
	rQuad[1].w = 1.0f;
	rQuad[1].color = m_dwCurColor;

	rQuad[2].x = rConvPos[0] + f32PtSize;
	rQuad[2].y = rConvPos[1] + f32PtSize;
	rQuad[2].z = 0;
	rQuad[2].w = 1.0f;
	rQuad[2].color = m_dwCurColor;

	rQuad[3].x = rConvPos[0] - f32PtSize;
	rQuad[3].y = rConvPos[1] + f32PtSize;
	rQuad[3].z = 0;
	rQuad[3].w = 1.0f;
	rQuad[3].color = m_dwCurColor;

	pD3DDevice->SetFVF( FVF_XYZWCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, rQuad, sizeof( XYZWCOLOR_VERTEX ) );
}

void
DX9GUIDrawInterfaceC::draw_grid( float32 f32Width, float32 f32Height, float32 f32GridSize )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	pD3DDevice->SetRenderState( D3DRS_LOCALVIEWER, FALSE );

	pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
	pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

	pD3DDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
	pD3DDevice->SetRenderState( D3DRS_POINTSCALEENABLE, FALSE );
	pD3DDevice->SetRenderState( D3DRS_POINTSIZE, FtoDW( 1 ) );

	int32	i32GSizeX = (int32)(f32Width / f32GridSize) - 1;
	int32	i32GSizeY = (int32)(f32Height / f32GridSize) - 1;

	if( m_nVerts < i32GSizeX * i32GSizeY )
	{
		delete [] m_pVertBuffer;
		m_pVertBuffer = new XYZCOLOR_VERTEX[i32GSizeX * i32GSizeY];
		m_nVerts = i32GSizeX * i32GSizeY;
	}

	XYZCOLOR_VERTEX*	pVerts = m_pVertBuffer;

	float32	f32Y = f32GridSize;
	int32	i, j;

	//--------------------------------------------------------------------
	// copy particle data to vertex buffer
	//--------------------------------------------------------------------

	for( i = 0; i < i32GSizeY; i++ )
	{
		float32	f32X = f32GridSize;
		for( j = 0; j < i32GSizeX; j++ )
		{

			XYZCOLOR_VERTEX*	pVert = &pVerts[i * i32GSizeX + j];
			pVert->x = f32X;
			pVert->y = f32Y;
			pVert->z = 0;
			pVert->color = m_dwCurColor;

			f32X += f32GridSize;
		}
		f32Y += f32GridSize;
	}

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_POINTLIST, i32GSizeX * i32GSizeY, pVerts, sizeof( XYZCOLOR_VERTEX ) );

//	delete [] pVerts;

	pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
	pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
}

void
DX9GUIDrawInterfaceC::draw_selection_box( const Vector2C& rStartPos, const Vector2C& rEndPos )
{
	LPDIRECT3DDEVICE9	pD3DDevice = m_pDevice->get_d3ddevice();
	if( !pD3DDevice )
		return;

	pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR );
	pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
	pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );


	XYZCOLOR_VERTEX	rLoop[5];
	rLoop[0].x = rStartPos[0];
	rLoop[0].y = rStartPos[1];
	rLoop[0].z = 0;
	rLoop[0].color = 0xffffffff;

	rLoop[1].x = rEndPos[0];
	rLoop[1].y = rStartPos[1];
	rLoop[1].z = 0;
	rLoop[1].color = 0xffffffff;

	rLoop[2].x = rEndPos[0];
	rLoop[2].y = rEndPos[1];
	rLoop[2].z = 0;
	rLoop[2].color = 0xffffffff;

	rLoop[3].x = rStartPos[0];
	rLoop[3].y = rEndPos[1];
	rLoop[3].z = 0;
	rLoop[3].color = 0xffffffff;

	rLoop[4] = rLoop[0];

	pD3DDevice->SetFVF( FVF_XYZCOLOR_VERTEX );
	pD3DDevice->SetPixelShader( NULL );
	pD3DDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 4, rLoop, sizeof( XYZCOLOR_VERTEX ) );

	pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
	pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
}

void
DX9GUIDrawInterfaceC::init_invalidate()
{
	if( m_pFont )
		m_pFont->InvalidateDeviceObjects();
}

void
DX9GUIDrawInterfaceC::init_validate()
{
	if( m_pFont )
		m_pFont->RestoreDeviceObjects();
}
