#include "D3D11Client.h"

//====================================================================
//						Global vars
//====================================================================
PerformanceCounter *PCounter		= NULL;
D3D11Config	*cfg					= NULL;
//		D3D11 devices, buffers and states
//
IDXGIFactory1 *Factory				= NULL;
IDXGISwapChain *SwapChain			= NULL;
ID3D11Device *Dev					= NULL;
ID3D11DeviceContext *iCtx			= NULL;

ID3D11InputLayout
	*IL_D3DXVECTOR2					= NULL,
	*IL_D3DXVECTOR3					= NULL;

ID3D11SamplerState
	*SS_Point_Wrap					= NULL,
	*SS_Linear_Wrap					= NULL,
	*SS_Linear_Clamp				= NULL;

ID3D11Buffer
	*cb_D3DXMATRIX_x1				= NULL,
	*cb_D3DXMATRIX_x2				= NULL,
	*cb_D3DXVECTOR4					= NULL;

ID3D11RasterizerState
	*RS_CullNone_Solid				= NULL,
	*RS_CullNone_Wire				= NULL,
	*RS_CullFront_Solid				= NULL,
	*RS_CullFront_Wire				= NULL,
	*RS_CullBack_Solid				= NULL,
	*RS_CullBack_Wire				= NULL;

ID3D11BlendState
	*BS_SrcAlpha					= NULL,
	*BS_InvSrcAlpha					= NULL,
	*BS_NoBlend						= NULL;

ID3D11DepthStencilState
	*DSS_NoDepth_NoStencil			= NULL,
	*DSS_TestDepthOnly_NoStencil	= NULL;

ID3D11Texture2D *Tex_2x2			= NULL;

ID3D11RenderTargetView *RTV_Tex_2x2	= NULL;

UINT ShaderCompileFlag				= 0;

char
	vs_ver[32] = { 0 },
	gs_ver[32] = { 0 },
	ps_ver[32] = { 0 };

//		Threads
//
bool bRender = true, bBBRender = false;
HANDLE RenderThread					= NULL;
HANDLE UpdateReady					= NULL;
HANDLE FrameReady					= NULL;
HANDLE ResourceRequest				= NULL;
HANDLE ResourceReleased				= NULL;
CRITICAL_SECTION Resources;

float fAmbient;

//MT:
void CheckResourceRequest() {
	if( WaitForSingleObject( ResourceRequest, 0 ) == WAIT_TIMEOUT )
		return;
	else
		ProcessResourceRequest();
}

void ProcessResourceRequest() {
	ResetEvent( ResourceRequest );
	LeaveCriticalSection( &Resources );
	WaitForSingleObject( ResourceReleased, INFINITE );
	EnterCriticalSection( &Resources );
	ResetEvent( ResourceReleased );
}

//====================================================================
//						Performance counter
//====================================================================

PerformanceCounter::PerformanceCounter() {
}

PerformanceCounter::~PerformanceCounter() {
}

void PerformanceCounter::Start() {
	static LARGE_INTEGER out;

	QueryPerformanceCounter( &out );
	start = (__int64)out.QuadPart;

	QueryPerformanceFrequency( &out );
	freq = (double)out.QuadPart;
	freq /= 1e6;
};

void PerformanceCounter::End() {
	static LARGE_INTEGER out;
	double result;

	QueryPerformanceCounter( &out );
	result = (double)(out.QuadPart - start)/freq;
	sprintf( line, "%lf mcrs", result );
	oapiWriteLog( line );
}

void PerformanceCounter::End( char *comment ) {
	static LARGE_INTEGER out;
	double result;

	QueryPerformanceCounter( &out );
	result = (double)(out.QuadPart - start)/freq;
	sprintf( line, "%lf mcrs", result );
	strcat( string, comment );
	strcat( string, line );
	oapiWriteLog( string );
}

char *PerformanceCounter::GetLine() {
	return string;
}

void PerformanceCounter::ShowLine() {
	oapiWriteLog( string );
}

//====================================================================
//						D3D11Config
//====================================================================

D3D11Config::D3D11Config() {

	fAmbient = (float)(*(DWORD*)gc->GetConfigParam( CFGPRM_AMBIENTLEVEL ))/256.0f;

	Width					= 0;
	Height					= 0;
	cWidth					= 0;
	cHeight					= 0;
	SketchPadMode			= 1;
	TextureMipMapCount		= 0;
	TileLoadingFrequency	= 50;

	FullScreenWindow	= false;
	RThread				= false;
	NormalMaps			= false;
	BumpMaps			= false;
	bPreloadTiles		= false;

	Aspect				= 0.0f;
	ShadowAlpha			= 0.5f;
	MFDTransparency		= 0.9f;

	FILTER_MeshTexture = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	FILTER_PlanetTexture = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	LF_MFDTexture = 1;

	AF_FACTOR_MeshTexture = 1;
	AF_FACTOR_PlanetTexture = 1;

	AAModes = NULL;

	RTFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
	CurrentAAMode.Count = 1;
	CurrentAAMode.Quality = 0;
	
	CfgFILE = NULL;
	cfg = this;
}

bool D3D11Config::LoadConfig() {

	FILE *file = NULL;
	if( !(file = fopen( "D3D11Client.cfg", "rb" )) ) {
		SetDefault();
		SaveConfig();
		delete [ ] AAModes;
	}
	else
		fclose( file );
	
	CfgFILE = oapiOpenFile( "D3D11Client.cfg", FILE_IN, ROOT );

	//GENERAL:
	oapiReadItem_int( CfgFILE, "Supported AA modes", Cfg.AA_count );
	oapiReadItem_int( CfgFILE, "Selected AA mode", Cfg.AA_mode );

	Cfg.AA_count = Cfg.AA_count > 0 ? ( Cfg.AA_count < 10 ? Cfg.AA_count : -1 ) : -1;
	if( Cfg.AA_count != -1 )
		AAModes = new AA_MODE_DESC [Cfg.AA_count];
	else {
		oapiCloseFile( CfgFILE, FILE_IN );
		SetDefault();
		SaveConfig();
		delete [ ] AAModes;
		CfgFILE = oapiOpenFile( "D3D11Client.cfg", FILE_IN, ROOT );
		oapiReadItem_int( CfgFILE, "Supported AA modes", Cfg.AA_count );
		oapiReadItem_int( CfgFILE, "Selected AA mode", Cfg.AA_mode );
		Cfg.AA_count = Cfg.AA_count > 0 ? ( Cfg.AA_count < 10 ? Cfg.AA_count : -1 ) : -1;
		if( Cfg.AA_count == -1 )
			oapiWriteLog( "Unable to load config" );
	}	
	
	int tmp;
	WORD j;
	char line[32], tag[64];
	strcpy( line, "AA_mode" );
	for( j = 0; j < Cfg.AA_count; j++ ) {
		sprintf( tag, "%s_%d", line, j );
		oapiReadItem_string( CfgFILE, tag, AAModes[j].desc );

		sprintf( tag, "%s_%d_count", line, j );
		oapiReadItem_int( CfgFILE, tag, tmp );
		AAModes[j].SDesc.Count = tmp;

		sprintf( tag, "%s_%d_quality", line, j );
		oapiReadItem_int( CfgFILE, tag, tmp );
		AAModes[j].SDesc.Quality = tmp;
	}

	oapiReadItem_int( CfgFILE, "Threading", Cfg.Thread_mode );
	oapiReadItem_int( CfgFILE, "Sketchpad mode", Cfg.Sketchpad_mode );
	oapiReadItem_int( CfgFILE, "MFD filter", Cfg.MFD_filter );
	oapiReadItem_int( CfgFILE, "MFD transparency", Cfg.MFD_transparency );

	//VESSELS:
	oapiReadItem_int( CfgFILE, "Mesh mip maps", Cfg.Mesh_Texture_Mip_maps );
	oapiReadItem_int( CfgFILE, "Mesh texture filter", Cfg.Mesh_Texture_Filter );
	oapiReadItem_int( CfgFILE, "Enable mesh normal maps", Cfg.Mesh_Normal_maps );
	oapiReadItem_int( CfgFILE, "Enable mesh bump maps", Cfg.Mesh_Bump_maps );
	oapiReadItem_int( CfgFILE, "Enable mesh specular maps", Cfg.Mesh_Specular_maps );
	oapiReadItem_int( CfgFILE, "Enable mesh emissive maps", Cfg.Mesh_Emissive_maps );

//	oapiReadItem_int( CfgFILE, "Enable PS local lighting", Cfg.PStream_LLights );
//	oapiReadItem_int( CfgFILE, "Enable base local lighting", Cfg.Base_LLights );

	//PLANETS:
	oapiReadItem_int( CfgFILE, "Planet texture filter", Cfg.Planet_Texture_filter );
	oapiReadItem_int( CfgFILE, "Planet tile loading frequency", Cfg.Planet_Tile_loading_Freq );
	oapiCloseFile( CfgFILE, FILE_IN );

	//check values:
	Cfg.Thread_mode = Cfg.Thread_mode > 1 ? 0 : (Cfg.Thread_mode < 0 ? 0 : Cfg.Thread_mode);
	Cfg.Sketchpad_mode = Cfg.Sketchpad_mode > 2 ? 0 : (Cfg.Sketchpad_mode < 0 ? 0 : Cfg.Sketchpad_mode);
	Cfg.Mesh_Texture_Mip_maps = Cfg.Mesh_Texture_Mip_maps > 2 ? 0 : (Cfg.Mesh_Texture_Mip_maps < 0 ? 0 : Cfg.Mesh_Texture_Mip_maps);
	Cfg.Mesh_Texture_Filter = Cfg.Mesh_Texture_Filter > 4 ? 0 : (Cfg.Mesh_Texture_Filter < 0 ? 0 : Cfg.Mesh_Texture_Filter);
	Cfg.Planet_Texture_filter = Cfg.Planet_Texture_filter > 4 ? 0 : (Cfg.Planet_Texture_filter < 0 ? 0 : Cfg.Planet_Texture_filter);
	Cfg.Planet_Tile_loading_Freq = Cfg.Planet_Tile_loading_Freq > 200 ? 50 : (Cfg.Planet_Tile_loading_Freq < 0 ? 50 : Cfg.Planet_Tile_loading_Freq);
	return true;
}

void D3D11Config::SetDefault() {
	Cfg.Thread_mode = 0;
	FindSupportedAAModes();
	Cfg.Sketchpad_mode = 1;
	Cfg.MFD_filter = 1;
	Cfg.MFD_transparency = 90;

	Cfg.Mesh_Texture_Mip_maps = 2;
	Cfg.Mesh_Texture_Filter = 4;
	Cfg.Mesh_Normal_maps = 1;
	Cfg.Mesh_Bump_maps = 0;
	Cfg.Mesh_Specular_maps = 0;
	Cfg.Mesh_Emissive_maps = 0;
	Cfg.PStream_LLights = 0;
	Cfg.Base_LLights = 0;

	Cfg.Planet_Texture_filter = 1;
	Cfg.Planet_Tile_loading_Freq = 50;
}

void D3D11Config::SaveConfig() {

	FILE *file = NULL;
	file = fopen( "D3D11Client.cfg", "wb" );
	fclose( file );

	CfgFILE = NULL;
	CfgFILE = oapiOpenFile( "D3D11Client.cfg", FILE_OUT, ROOT );

/*	if( !CfgFILE ) {
		FILE *file = fopen( "D3D11Client.cfg", "wb" );
		fclose( file );
		CfgFILE = oapiOpenFile( "D3D11Client.cfg", FILE_OUT, ROOT );
		if( !CfgFILE )
			oapiWriteLog( "Unable to create config file" );
	}*/

	//GENERAL:
	oapiWriteItem_int( CfgFILE, "Threading", Cfg.Thread_mode );
	oapiWriteItem_int( CfgFILE, "Supported AA modes", Cfg.AA_count );
	oapiWriteItem_int( CfgFILE, "Selected AA mode", Cfg.AA_mode );

	char line[32], tag[64];
	strcpy( line, "AA_mode" );
	for( WORD j = 0; j < Cfg.AA_count; j++ ) {
		sprintf( tag, "%s_%d", line, j );
		oapiWriteItem_string( CfgFILE, tag, AAModes[j].desc );

		sprintf( tag, "%s_%d_count", line, j );
		oapiWriteItem_int( CfgFILE, tag, AAModes[j].SDesc.Count );

		sprintf( tag, "%s_%d_quality", line, j );
		oapiWriteItem_int( CfgFILE, tag, AAModes[j].SDesc.Quality );
	}

	oapiWriteItem_int( CfgFILE, "Sketchpad mode", Cfg.Sketchpad_mode );
	oapiWriteItem_int( CfgFILE, "MFD filter", Cfg.MFD_filter );
	oapiWriteItem_int( CfgFILE, "MFD transparency", Cfg.MFD_transparency );

	//VESSELS:
	oapiWriteItem_int( CfgFILE, "Mesh mip maps", Cfg.Mesh_Texture_Mip_maps );
	oapiWriteItem_int( CfgFILE, "Mesh texture filter", Cfg.Mesh_Texture_Filter );
	oapiWriteItem_int( CfgFILE, "Enable mesh normal maps", Cfg.Mesh_Normal_maps );
	oapiWriteItem_int( CfgFILE, "Enable mesh bump maps", Cfg.Mesh_Bump_maps );
	oapiWriteItem_int( CfgFILE, "Enable mesh specular maps", Cfg.Mesh_Specular_maps );
	oapiWriteItem_int( CfgFILE, "Enable mesh emissive maps", Cfg.Mesh_Emissive_maps );
//	oapiWriteItem_int( CfgFILE, "Enable PS local lighting", Cfg.PStream_LLights );
//	oapiWriteItem_int( CfgFILE, "Enable base local lighting", Cfg.Base_LLights );

	//PLANETS:
	oapiWriteItem_int( CfgFILE, "Planet texture filter", Cfg.Planet_Texture_filter );
	oapiWriteItem_int( CfgFILE, "Planet tile loading frequency", Cfg.Planet_Tile_loading_Freq );
	oapiCloseFile( CfgFILE, FILE_OUT );
}

void D3D11Config::FindSupportedAAModes() {
	AA_MODE_DESC *tmp = new AA_MODE_DESC [9];
	bool flag[9];
	flag[0] = true;

	strcpy( tmp[0].desc, "No AA" );
	tmp[0].SDesc.Count = 1;
	tmp[0].SDesc.Quality = 0;

	strcpy( tmp[1].desc, "MSAA x2" );
	tmp[1].SDesc.Count = 2;
	tmp[1].SDesc.Quality = 0;

	strcpy( tmp[2].desc, "MSAA x4" );
	tmp[2].SDesc.Count = 4;
	tmp[2].SDesc.Quality = 0;

	strcpy( tmp[3].desc, "MSAA x6" );
	tmp[3].SDesc.Count = 6;
	tmp[3].SDesc.Quality = 0;

	strcpy( tmp[4].desc, "MSAA x8" );
	tmp[4].SDesc.Count = 8;
	tmp[4].SDesc.Quality = 0;

	strcpy( tmp[5].desc, "CSAA x8" );
	tmp[5].SDesc.Count = 4;
	tmp[5].SDesc.Quality = 8;

	strcpy( tmp[6].desc, "CSAA x8Q" );
	tmp[6].SDesc.Count = 8;
	tmp[6].SDesc.Quality = 8;

	strcpy( tmp[7].desc, "CSAA x16" );
	tmp[7].SDesc.Count = 4;
	tmp[7].SDesc.Quality = 16;

	strcpy( tmp[8].desc, "CSAA x16Q" );
	tmp[8].SDesc.Count = 8;
	tmp[8].SDesc.Quality = 16;

	UINT j, ctr = 0, num = 0;
	Dev->CheckMultisampleQualityLevels( RTFormat, 2, &num );
	flag[1] = num ? true : false;

	Dev->CheckMultisampleQualityLevels( RTFormat, 4, &num );
	flag[2] = num ? true : false;
	flag[5] = num >= 9 ? true : false;
	flag[7] = num >= 17 ? true : false;

	Dev->CheckMultisampleQualityLevels( RTFormat, 6, &num );
	flag[3] = num ? true : false;

	Dev->CheckMultisampleQualityLevels( RTFormat, 8, &num );
	flag[4] = num ? true : false;
	flag[6] = num >= 9 ? true : false;
	flag[8] = num >= 17 ? true : false;

	Cfg.AA_mode = 0;
	Cfg.AA_count = 0;
	for( j = 0; j < 9; j++ )
		if( flag[j] )
			Cfg.AA_count++;

	AAModes = new AA_MODE_DESC [Cfg.AA_count];
	for( j = 0; j < 9; j++ )
		if( flag[j] ) {
			AAModes[ctr] = tmp[j];
			ctr++;
		}

	delete [ ] tmp;
}

void D3D11Config::ApplyConfig() {
	CurrentAAMode = AAModes[Cfg.AA_mode].SDesc;
	RThread = Cfg.Thread_mode == 1 ? true : false;
	SketchPadMode = Cfg.Sketchpad_mode;
	LF_MFDTexture = Cfg.Mesh_Texture_Filter;
	MFDTransparency = (float)Cfg.MFD_transparency/100.0f;

	switch( Cfg.Mesh_Texture_Mip_maps ) {
	case 0:
		TextureMipMapCount = 1;
		break;
	case 1:
		TextureMipMapCount = 4;
		break;
	case 2:
		TextureMipMapCount = 0;
		break;
	}

	FILTER_MeshTexture = SetFilter( Cfg.Mesh_Texture_Filter, AF_FACTOR_MeshTexture );

	NormalMaps = Cfg.Mesh_Normal_maps ? true : false;
	BumpMaps = Cfg.Mesh_Bump_maps ? true : false;
	SpecularMaps = Cfg.Mesh_Specular_maps ? true : false;
	EmissiveMaps = Cfg.Mesh_Emissive_maps ? true : false;
	
	FILTER_PlanetTexture = SetFilter( Cfg.Planet_Texture_filter, AF_FACTOR_PlanetTexture );

	TileLoadingFrequency = Cfg.Planet_Tile_loading_Freq;
	bPreloadTiles = false;
//	bPreloadTiles = CFG.P_preload_tiles ? true : false;
}

D3D11_FILTER D3D11Config::SetFilter( int value, DWORD &afactor ) {
	if( value < 0 || value > 4 )
		return D3D11_FILTER_MIN_MAG_MIP_LINEAR;

	D3D11_FILTER ret;
	switch( value ) {
	case 0:
		ret = D3D11_FILTER_MIN_MAG_MIP_POINT;
		afactor = 1;
		break;
	case 1:
		ret = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		afactor = 1;
		break;
	case 2:
		ret = D3D11_FILTER_ANISOTROPIC;
		afactor = 4;
		break;
	case 3:
		ret = D3D11_FILTER_ANISOTROPIC;
		afactor = 8;
		break;
	case 4:
		ret = D3D11_FILTER_ANISOTROPIC;
		afactor = 16;
		break;
	}

	return ret;
}

D3D11Config::~D3D11Config() {
	delete [ ] Modes;
	AdapterOutputDesc.clear();
	Adapters.clear();
	Outputs.clear();
}

void D3D11Config::EnumerateAll( oapi::GraphicsClient::VIDEODATA *vdata ) {
	IDXGIAdapter1 *Adapter;
	IDXGIOutput *Output;
	DXGI_ADAPTER_DESC ADesc;
	DXGI_OUTPUT_DESC ODesc;
	PHYSICAL_MONITOR Monitor;
	UINT AC = 0, OC = 0;
	char *line, cbuf[128];

	HR( CreateDXGIFactory1( __uuidof(IDXGIFactory1), (void**)(&Factory) ) );
	while( Factory->EnumAdapters1( AC, &Adapter ) == S_OK ) {
		Adapter->GetDesc( &ADesc );	
		while( Adapter->EnumOutputs( OC, &Output ) == S_OK ) {
			Output->GetDesc( &ODesc );

			GetPhysicalMonitorsFromHMONITOR( ODesc.Monitor, 1, &Monitor );
			wcstombs( cbuf, Monitor.szPhysicalMonitorDescription, 128 );
			DestroyPhysicalMonitors( 1, &Monitor );

			line = new char [128];
			AdapterOutputDesc.push_back( line );
			wcstombs( AdapterOutputDesc[OC], ADesc.Description, 128 );
			strcat( AdapterOutputDesc[OC], cbuf );

			Adapters.push_back( Adapter );
			Outputs.push_back( Output );
			OC++;
		}
		AC++;
	}
	NumOutputs = OC;

	if( vdata->deviceidx < 0 || (UINT)vdata->deviceidx > OC )		vdata->deviceidx = 0;
	if( vdata->modeidx < 0 || (UINT)vdata->modeidx > OC )		vdata->modeidx = 0;

	UINT NumModes;
	Outputs[vdata->deviceidx]->GetDisplayModeList( DXGI_FORMAT_B8G8R8A8_UNORM, 0, &NumModes, NULL );
	Modes = new DXGI_MODE_DESC [NumModes];
	Outputs[vdata->deviceidx]->GetDisplayModeList( DXGI_FORMAT_B8G8R8A8_UNORM, 0, &NumModes, Modes );
	
	D3D11Config::Adapter = Adapters[vdata->deviceidx];
	Output = Outputs[vdata->deviceidx];
	Mode = Modes[vdata->modeidx];

	FullScreenWindow = vdata->pageflip;
	DriverType = vdata->forceenum ? D3D_DRIVER_TYPE_REFERENCE : D3D_DRIVER_TYPE_UNKNOWN;
	FullScreenWindow = vdata->fullscreen;
	Width = vdata->winw;
	Height = vdata->winh;

	D3D_FEATURE_LEVEL FeatureLevels[ ] = {
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1
	};

	UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef _DEBUG
	flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	HR( D3D11CreateDevice(
		cfg->Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags,
		FeatureLevels, 3, D3D11_SDK_VERSION, &Dev, &cfg->SucceededFL, &iCtx ) );

	if( !Dev )	MessageBoxA( gc->hWindow, "Device creation failed.\n", "Critical error.", MB_OK );
}

bool tRender = false;


HRESULT CompileFromFile(LPCSTR pSrcFile,CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude,
        LPCSTR pFunctionName, LPCSTR pProfile, UINT Flags1, UINT Flags2, ID3DX11ThreadPump* pPump, ID3D10Blob** ppShader, ID3D10Blob** ppErrorMsgs, HRESULT* pHResult)
{
	Flags1 |= D3D10_SHADER_ENABLE_STRICTNESS;
#ifdef _DEBUG
	Flags1 |= D3D10_SHADER_DEBUG;
#else
	Flags1 |= D3D10_SHADER_OPTIMIZATION_LEVEL3;
#endif
	return D3DX11CompileFromFileA(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags1, Flags2, pPump, ppShader, ppErrorMsgs, pHResult);
}
