// ------------------------------------------------------------------------
/*
	author: Jaap Suter

	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#include <stddef.h>
#include <gdiplus.h>

#include "gdi_renderer.h"

// -------------------------------------------------------------------------------
LRESULT WINAPI MainWindowProcedure( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{    
	switch( msg )
	{
	case WM_DESTROY:
		PostQuitMessage( 0 );
		return 0;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam );
	}
}

// -------------------------------------------------------------------------------
GdiRenderer::GdiRenderer( HINSTANCE hInstance, int showCmd ) 
	: 
mHwnd( 0 ), 
mWidth(),
mHeight(),
mGdiplusToken( 0 ),
mBackbuffer(),
mBackgroundImage()
{
	WNDCLASS windowClass;
 		windowClass.lpszClassName     = "Reveal Test";
		windowClass.lpfnWndProc       = MainWindowProcedure;
		windowClass.style             = CS_VREDRAW | CS_HREDRAW;
		windowClass.hInstance         = hInstance;
		windowClass.hIcon             = LoadIcon( NULL, IDI_APPLICATION );
		windowClass.hCursor           = LoadCursor( NULL, IDC_ARROW );
		windowClass.hbrBackground     = (HBRUSH)( COLOR_WINDOW+1 );
		windowClass.lpszMenuName      = NULL;
		windowClass.cbClsExtra        = 0;
		windowClass.cbWndExtra        = 0;
	
	RegisterClass( &windowClass );

	mHwnd = CreateWindow( "Reveal Test", "Reveal Test",
						  WS_OVERLAPPEDWINDOW,
						  0, 0, 800, 600,
					      NULL, NULL, hInstance, NULL );

	if ( ! mHwnd ) 
	{
		MessageBox( NULL, "Unable to create window", "Fatal Error", MB_OK );
		throw UnableToCreateWindow();
	}

	ShowWindow( mHwnd, showCmd );
	UpdateWindow( mHwnd );

	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::Status status = Gdiplus::GdiplusStartup( &mGdiplusToken, &gdiplusStartupInput, 0 );
	if ( status != Gdiplus::Ok )
	{
		MessageBox( NULL, "Unable to initialize GDI+", "Fatal Error", MB_OK );
		throw UnableToInitGdiplus();
	}

	mBackgroundImage.Reset( new Gdiplus::Image( L"../doc/bg.jpg" ) ); 

	FixBackBuffer();	
}

// -------------------------------------------------------------------------------
GdiRenderer::~GdiRenderer()
{
	mBackbuffer.Reset();
	mBackgroundImage.Reset();
    Gdiplus::GdiplusShutdown( mGdiplusToken );
}

// -------------------------------------------------------------------------------
void GdiRenderer::FixBackBuffer()
{
    RECT r;
    GetClientRect( mHwnd, &r );
    int width = r.right - r.left;
    int height = r.bottom - r.top;
    if ( (width != mWidth) || (height != mHeight) )
    {
	    mWidth = width;
	    mHeight = height;
	    mBackbuffer.Reset( new Gdiplus::Bitmap( mWidth, mHeight ) );
    }
}

// -------------------------------------------------------------------------------
void GdiRenderer::FlipAndClear()
{	
	HDC hdc = GetDC( mHwnd );
		Gdiplus::Graphics frontBuffer( hdc );
		frontBuffer.DrawImage( mBackbuffer.Get(), 0, 0 );			
	ReleaseDC( mHwnd, hdc );

	DoBar( 0, 0, mWidth, mHeight, 0xFF000000 );

	Reveal::SmartPtr::ScopedPtr< Gdiplus::Graphics > graphics( Gdiplus::Graphics::FromImage( mBackbuffer.Get() ) );
	Gdiplus::RectF rect( 0.0f, 0.0f, static_cast< float >( mWidth ), static_cast< float >( mHeight ) );
	graphics->DrawImage( mBackgroundImage.Get(), rect );
	
	RedrawWindow( mHwnd, 0, 0, 0 );
}

// -------------------------------------------------------------------------------
void GdiRenderer::DoLine( int x1, int y1, int x2, int y2, Reveal::uint32_t color ) const
{
	Reveal::SmartPtr::ScopedPtr< Gdiplus::Graphics > graphics( Gdiplus::Graphics::FromImage( mBackbuffer.Get() ) );
	Gdiplus::Color lineColor( (BYTE)((color & 0xFF000000) >> 24), (BYTE)((color & 0xFF0000) >> 16), (BYTE)((color & 0xFF00) >> 8), (BYTE)(color & 0xFF) );
	Gdiplus::Pen pen( lineColor );
	graphics->DrawLine( &pen, x1, y1, x2, y2 );
}

// -------------------------------------------------------------------------------
void GdiRenderer::DoBar( int x1, int y1, int width, int height, Reveal::uint32_t color ) const
{
	Reveal::SmartPtr::ScopedPtr< Gdiplus::Graphics > graphics( Gdiplus::Graphics::FromImage( mBackbuffer.Get() ) );
	Gdiplus::Color rectColor( (BYTE)((color & 0xFF000000) >> 24), (BYTE)((color & 0xFF0000) >> 16), (BYTE)((color & 0xFF00) >> 8), (BYTE)(color & 0xFF) );
	Gdiplus::SolidBrush brush( rectColor );
	graphics->FillRectangle( &brush, x1, y1, width, height );
}

// -------------------------------------------------------------------------------
void GdiRenderer::DoString( int x1, int y1, const char* str, Reveal::uint32_t color ) const
{
	Reveal::SmartPtr::ScopedPtr< Gdiplus::Graphics > graphics( Gdiplus::Graphics::FromImage( mBackbuffer.Get() ) );
	Gdiplus::Color fontColor( (BYTE)((color & 0xFF000000) >> 24), (BYTE)((color & 0xFF0000) >> 16), (BYTE)((color & 0xFF00) >> 8), (BYTE)(color & 0xFF) );

	Gdiplus::FontFamily  fontFamily( L"Arial" );
	Gdiplus::Font        font( &fontFamily, 20, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel );
	Gdiplus::PointF      pos( static_cast< float >( x1 ), static_cast< float >( y1 ) );
	Gdiplus::SolidBrush  solidBrush( fontColor );

	const size_t maxLen = 1024;
	wchar_t buf[ maxLen ];
	if ( mbstowcs( buf, str, maxLen ) > 0 )
	{
		graphics->DrawString( buf, -1, &font, pos, &solidBrush );
	}
}

// -------------------------------------------------------------------------------
void GdiRenderer::DoTriangle( int x1, int y1, int x2, int y2, int x3, int y3, Reveal::uint32_t color ) const
{
	Reveal::SmartPtr::ScopedPtr< Gdiplus::Graphics > graphics( Gdiplus::Graphics::FromImage( mBackbuffer.Get() ) );
	Gdiplus::Color triColor( (BYTE)((color & 0xFF000000) >> 24), (BYTE)((color & 0xFF0000) >> 16), (BYTE)((color & 0xFF00) >> 8), (BYTE)(color & 0xFF) );
	Gdiplus::SolidBrush brush( triColor );
	Gdiplus::Point p[ 3 ];
	p[ 0 ].X = x1;
	p[ 0 ].Y = y1;
	p[ 1 ].X = x2;
	p[ 1 ].Y = y2;
	p[ 2 ].X = x3;
	p[ 2 ].Y = y3;
	
	graphics->FillPolygon( &brush, p, 3, Gdiplus::FillModeAlternate );
}

// -------------------------------------------------------------------------------
bool GdiRenderer::DoScreenSpaceToWorldSpace( int screenX, int  screenY, 
											float&  worldspaceX, float&  worldspaceY, float& worldspaceZ,
											float&  rayX, float&  rayY, float& rayZ ) const 
{ 
	worldspaceX = static_cast< float >( screenX );
	worldspaceY = static_cast< float >( screenY );
	worldspaceZ = 0;
	rayX = 0.0f;
	rayY = 0.0f;
	rayZ = 1.0f;

    return true; 
}
