///////////////////////////////////////////////////////////////////////////////
/// @file deDriver_base.cpp
///
/// @brief base class for drivers
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date May 2004
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <cstdio>
#include <cstdarg>
#include "deDriver_priv.hpp"


deDriver::deDriver()
{
	//global variables
	memset(&m_CurrentDisplay, 0, sizeof(m_CurrentDisplay));
	memset(&m_CurrentViewport, 0, sizeof(m_CurrentViewport));
	memset(&m_ClientRect, 0, sizeof(m_ClientRect));
	memset(&m_SourceRect, 0, sizeof(m_SourceRect));
	memset(m_QualityFlags, 0, QUALITY_FLAG_COUNT * sizeof(QualityFlag));
	m_CurWindowHandle = NULL;

	m_UniqueID = -1;

	ResetDriverStats();
	return;
}

deDriver::~deDriver()
{
	return;
}

long deDriver::GetCount()
{
	//just return the number of drivers we are aware of
	return DRIVER_COUNT;
}

const IdeDriver::deDisplay* deDriver::GetDisplay() const
{
	return &m_CurrentDisplay;
}

long deDriver::BitDepth(BPP BPPType) const
{
	return IdeDriver_BPPBitDepth(BPPType);
}

long deDriver::BitDepth(ZPP ZPPType) const
{
	switch (ZPPType)
	{
	case IdeDriver::ZPP_D16:
	case IdeDriver::ZPP_D15S1:
		return 16;
		
	case IdeDriver::ZPP_D24:
	case IdeDriver::ZPP_D24S4:
	case IdeDriver::ZPP_D24S8:
	case IdeDriver::ZPP_D32:
		return 32;
		
	default:
		return 0;
	}
}

long deDriver::StencilDepth(ZPP ZPPType) const
{
	switch (ZPPType)
	{
	case IdeDriver::ZPP_D16:
	case IdeDriver::ZPP_D24:
	case IdeDriver::ZPP_D32:
		return 0;
		
	case IdeDriver::ZPP_D15S1:
		return 1;
	case IdeDriver::ZPP_D24S4:
		return 4;
	case IdeDriver::ZPP_D24S8:
		return 8;
		
	default:
		return 0;
	}
}

deBoolean deDriver::HasStencil(ZPP ZPPType) const
{
	switch (ZPPType)
	{
	case IdeDriver::ZPP_D16:
	case IdeDriver::ZPP_D24:
	case IdeDriver::ZPP_D32:
		return deFALSE;
		
	case IdeDriver::ZPP_D15S1:
	case IdeDriver::ZPP_D24S4:
	case IdeDriver::ZPP_D24S8:
		return deTRUE;
		
	default:
		return deFALSE;
	}
}

IdeDriver::BPP deDriver::DownSampleFormat(BPP OriginalFormat) const
{
	switch (OriginalFormat)
	{
	default:
		return BPP_UNKNOWN;

	case BPP_R8G8B8:		// "drop" to 32-bit from 24-bit
		return BPP_X8R8G8B8;

	case BPP_A2B10G10R10:	// drop to a lower-color-precision 32-bit format
		return BPP_A8R8G8B8;	// common fallback

	case BPP_A8R8G8B8:		// keep all channels equal precision
		return BPP_A4R4G4B4;

	case BPP_A4R4G4B4:		// drop the 4 alpha for "better" color
		return BPP_A1R5G5B5;

	case BPP_X8R8G8B8:		// drop from effective 24-bit to 16-bit
	case BPP_A1R5G5B5:		// drop the alpha altogether
	case BPP_X1R5G5B5:		// "drop" to higher green precision
	case BPP_X4R4G4B4:		// "drop" to higher precision
		return BPP_R5G6B5;		// common fallback

	case BPP_DXT1:
		return BPP_A1R5G5B5;	// not so much alpha here, but still some chance
	case BPP_DXT2:
	case BPP_DXT3:
	case BPP_DXT4:
	case BPP_DXT5:
		return BPP_A4R4G4B4;	// has alpha formats, so need more bits for it
	}
}

unsigned long deDriver::GetQualityFlag(QualityFlag Flag)
{
	if (Flag <= QUALITY_UNKNOWN || Flag >= QUALITY_FLAG_COUNT)
		DEBUGRETURN(0, "GetQualityFlag: Invalid Flag Identifier");

	return m_QualityFlags[Flag];
}

deBoolean deDriver::SetQualityFlag(QualityFlag Flag, unsigned long Value)
{
	if (Flag <= QUALITY_UNKNOWN || Flag >= QUALITY_FLAG_COUNT)
		DEBUGRETURN(deFALSE, "GetQualityFlag: Invalid Flag Identifier");

	m_QualityFlags[Flag] = Value;
	return deTRUE;
}

void deDriver::LogInfo(char *Data, ...)
{
	va_list			VAData;
	char			NewData[1024];
	char			LogFile[100];
	deDriverInfo *	DriverInfo;
	unsigned long	BytesWritten;

	//combine the string data together
	va_start(VAData, Data);
	vsprintf(NewData, Data, VAData);
	va_end(VAData);

	//get the driver info
	DriverInfo = GetInfo();
	if(!DriverInfo)
		return;

	//create the logfile name
	sprintf(LogFile, "%s.log", DriverInfo->Name);
#if 0
	HANDLE			FileHandle;
	//open the log file
	FileHandle = CreateFile(LogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
	if((long)FileHandle == -1)
		return;

	//go to the end of the file
	SetFilePointer(FileHandle, 0, NULL, SEEK_END);

	//write it and close it
	WriteFile(FileHandle, NewData, strlen(NewData), &BytesWritten, 0);
	CloseHandle(FileHandle);
#else
	FILE*			FileHandle;
	FileHandle = fopen(LogFile, "a");
	if (FileHandle == NULL)	
		return;

	BytesWritten = fwrite(NewData, 1, strlen(NewData), FileHandle);
	fclose(FileHandle);
#endif

	//return
	return;
}

deRect deDriver::UpdateWindowRect()
{
	//update the client rect if we are doing windowed mode

	deRect RetRect;

	//if not windowed, exit
	if(!m_CurrentDisplay.Windowed)
	{
		const deDisplay * d = GetDisplay();
		m_SourceRect.Top = 0;
		m_SourceRect.Left = 0;
		m_SourceRect.Right = d->Width;
		m_SourceRect.Bottom = d->Height;
		RetRect = m_SourceRect;
		return RetRect;
	}

	//grab the window size and figure out where to render to
	GetClientRect(m_CurWindowHandle, (RECT*)&m_ClientRect);

	//realign the source drawing area
	m_SourceRect.Top = 0;
	m_SourceRect.Left = 0;
	m_SourceRect.Right = m_ClientRect.Right - m_ClientRect.Left;
	m_SourceRect.Bottom = m_ClientRect.Bottom - m_ClientRect.Top;
	RetRect = m_SourceRect;
	
	return RetRect;
}

deRect deDriver::GetDisplayRect() const
{
	return m_SourceRect;
}

void deDriver::SetDefaultRenderstates()
{
	float	FogVal;

	//set the default renderstates

	InvalidateRenderStates(deFALSE);

	//now set our values
	SetRenderState(RENDER_ZENABLE, deTRUE);
	SetRenderState(RENDER_ZWRITESENABLE, deTRUE);
	SetRenderState(RENDER_DEPTHFUNCTION, COMPARE_LESSEQUAL);
	SetRenderState(RENDER_DEPTHBIAS, 0);
	SetRenderState(RENDER_FILLMODE, FILL_SOLID);
	SetRenderState(RENDER_SHADEMODE, SHADE_GOURAUD);
	SetRenderState(RENDER_COLORMASK, 0xffffffff);
	SetRenderState(RENDER_FSAAENABLE, deTRUE);
	SetRenderState(RENDER_FSAAMASK, 0xffffffff);

	SetRenderState(RENDER_ALPHATESTENABLE, 0);
	SetRenderState(RENDER_ALPHAREF, 256);
	SetRenderState(RENDER_ALPHAFUNCTION, COMPARE_ALWAYS);
	SetRenderState(RENDER_ALPHABLENDENABLE, deFALSE);
	SetRenderState(RENDER_SOURCEBLEND, BLEND_ONE);
	SetRenderState(RENDER_DESTBLEND, BLEND_ZERO);
	SetRenderState(RENDER_CULLMODE, CULL_CCW);

	SetRenderState(RENDER_FOGENABLE, 0);
	FogVal = 0.0f;
	SetRenderState(RENDER_FOGSTART, *(long *)(&FogVal));
	FogVal = 1.0f;
	SetRenderState(RENDER_FOGEND, *(long *)(&FogVal));
	SetRenderState(RENDER_FOGCOLOR, 0x00ffffffff);
	SetRenderState(RENDER_CLIPPINGENABLE, deTRUE);
	SetRenderState(RENDER_HWLIGHTINGENABLE, deTRUE);
	SetRenderState(RENDER_AMBIENTLIGHT, 0x00000000);
	SetRenderState(RENDER_DITHERENABLE, deFALSE);

	SetRenderState(RENDER_TEXTUREFACTOR, 0xffffffff);
	SetRenderState(RENDER_SPECULARENABLE, deFALSE);
	SetRenderState(RENDER_SPECULARPERSPECTIVE, deTRUE);
	SetRenderState(RENDER_PERVERTEXCOLOR, deTRUE);
	SetRenderState(RENDER_STENCILENABLE, deFALSE);
	SetRenderState(RENDER_STENCILREF, 0x00000000);
	SetRenderState(RENDER_STENCILMASK, 0xffffffff);
	SetRenderState(RENDER_STENCILWRITEMASK, 0xffffffff);
	SetRenderState(RENDER_STENCILFUNCTION, COMPARE_ALWAYS);
	SetRenderState(RENDER_STENCILFAIL, STENCIL_KEEP);
	SetRenderState(RENDER_STENCILZFAIL, STENCIL_KEEP);
	SetRenderState(RENDER_STENCILPASS, STENCIL_KEEP);

	InvalidateRenderStates(deTRUE);
}

IdeDriver::deDriverStats* deDriver::GetDriverStats()
{
	return &m_Stats;
}

void deDriver::ResetDriverStats()
{
	m_Stats.VBufferUploads = 0;
	m_Stats.TextureUploads = 0;
}
