#include "Overlay.h"

#include <Windowsx.h>

Overlay *OV = NULL;

ID3D11Buffer
	*Overlay::VB		= NULL,
	*Overlay::IB		= NULL,
	*Overlay::QuadVB	= NULL,
	*Overlay::QuadIB	= NULL;

const UINT
	Overlay::NTVertexStride = 32,
	Overlay::VBOffset = 0;

Overlay::Overlay() {
//projection matrix for GDI buffer render:
	D3DXMatrixIdentity( &M );
	M._11 = 2.0f;
	M._22 = -2.0f;
	M._33 = 0.5f;
	M._14 = -1.0f;
	M._24 = 1.0f;
	M._34 = 0.5f;

//shaders:
	ID3DBlob *SBlob, *EBlob;
	D3D11_INPUT_ELEMENT_DESC IEDesc[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};
	HR( D3DX11CompileFromFile( "Modules\\D3D11Shaders\\Overlay.fx", NULL, NULL, "VS_Overlay", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, NULL ) );
	HR( Dev->CreateInputLayout( IEDesc, 3, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_NTVertex ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_2DOverlay ) );
	REL( SBlob );
	REL( EBlob );

	HR( D3DX11CompileFromFile( "Modules\\D3D11Shaders\\Overlay.fx", NULL, NULL, "PS_Overlay", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, NULL ) );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_2DOverlay ) );
	REL( SBlob );
	REL( EBlob );

	HR( D3DX11CompileFromFile( "Modules\\D3D11Shaders\\Overlay.fx", NULL, NULL, "PS_GDI", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, NULL ) );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_PBuffer ) );
	REL( SBlob );
	REL( EBlob );

	VB = IB = NULL;
	nVB = nIB = 0;
	ExtendVertexBuffer( 512 );
	ExtendIndexBuffer( 1024 );

//quad:
	float quad[32] = {
		0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
		1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
	};

	WORD idx[6] = { 0, 1, 2, 0, 2, 3 };

	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = 4*32;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sdata;
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = quad;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &QuadVB ) );	

	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = 12;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = idx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &QuadIB ) );

//	OV = this;
}

Overlay::~Overlay() {
	REL( VB );
	REL( IB );
	REL( QuadVB );
	REL( QuadIB );
	REL( IL_NTVertex );
	REL( VS_2DOverlay );
	REL( PS_2DOverlay );
}

void Overlay::ExtendVertexBuffer( UINT nVtx ) {
	REL( VB );

	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = nVtx*32;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VB ) );
	nVB = nVtx;
}

void Overlay::ExtendIndexBuffer( UINT nIdx ) {
	REL( IB );

	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = nIdx*2;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &IB ) );
	nIB = nIdx;
}

void Overlay::Render2DOverlay( SURFHANDLE *Surf, MESHHANDLE Mesh, MATRIX3 *T, bool transparent ) {
	const float BlendFactors[4] = { 1.0f };

	D3D11_VIEWPORT VP;
    VP.Width = (FLOAT)cfg->cWidth;
    VP.Height = (FLOAT)cfg->cHeight;
    VP.MinDepth = 0.0f;
    VP.MaxDepth = 1.0f;
    VP.TopLeftX = 0;
    VP.TopLeftY = 0;
	iCtx->RSSetViewports( 1, &VP );

	iCtx->IASetInputLayout( IL_NTVertex );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->VSSetShader( VS_2DOverlay, NULL, NULL );
	iCtx->PSSetShader( PS_2DOverlay, NULL, NULL );
	iCtx->OMSetBlendState( BS_SrcAlpha, BlendFactors, 0xFFFFFFFF );
	iCtx->RSSetState( RS_CullBack_Solid );
	iCtx->OMSetRenderTargets( 1, &SC->RTarget_RTV, NULL );

//render 2D panels and HUD:
	float	
		l = (0.0f - (float)(T->m13))*(1.0f/(float)(T->m11)),
		r =	((float)cfg->cWidth - (float)(T->m13))*(1.0f/(float)(T->m11)),
		t = (0.0f - (float)(T->m23))*(1.0f/(float)(T->m22)),
		b = ((float)cfg->cHeight - (float)(T->m23))*(1.0f/(float)(T->m22)),
		zn = -100.0f,
		zf = 100.0f;

	D3DXMatrixIdentity( &Orth );
	Orth._11 = 2/(r-l);
	Orth._22 = 2/(t-b);
	Orth._14 = (l+r)/(l-r);
	Orth._24 = (t+b)/(b-t);
	Orth._33 = 1/(zn-zf);
	Orth._43 = zn/(zn-zf);

	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &Orth, 0, 0 );
	iCtx->VSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );

	Texture *tex;
	D3D11_MAPPED_SUBRESOURCE SRes;
	MESHGROUP *MG;
	WORD ngrp = oapiMeshGroupCount( Mesh );
	for( WORD j = 0; j < ngrp; j++ ) {
		MG = oapiMeshGroup( Mesh, j );
		if( MG->UsrFlag & 2 )
			continue;
		if( MG->TexIdx >= TEXIDX_MFD0 ) {
			int mfd_idx = MG->TexIdx - TEXIDX_MFD0;
			SURFHANDLE surf = gc->GetMFDSurface( mfd_idx );
			if( !surf )
				continue;
			tex = (Texture*)surf;
			PSCB.mode = 1;
			iCtx->PSSetSamplers( 0, 1, &( cfg->LF_MFDTexture ? SS_Linear_Wrap : SS_Point_Wrap) );
			MG->Vtx[1].tu = MG->Vtx[2].tv = MG->Vtx[3].tu = MG->Vtx[3].tv = 1.002f;
		}
		else {
			PSCB.mode = 0;
			tex = (Texture*)Surf[MG->TexIdx];
			iCtx->PSSetSamplers( 0, 1, &SS_Linear_Wrap );
		}

		iCtx->PSSetShaderResources( 0, 1, tex->GetSRV() );
		PSCB.r = cfg->MFDTransparency;
		iCtx->UpdateSubresource( cb_D3DXVECTOR4,0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 1, 1, &cb_D3DXVECTOR4 );

		if( MG->nVtx > nVB )
			ExtendVertexBuffer( MG->nVtx );
		memset( &SRes, 0, sizeof(SRes) );
		iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, MG->Vtx, 32*MG->nVtx );
		iCtx->Unmap( VB, 0 );

		if( MG->nIdx > nIB )
			ExtendVertexBuffer( MG->nIdx );
		memset( &SRes, 0, sizeof(SRes) );
		iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, MG->Idx, 2*MG->nIdx );
		iCtx->Unmap( IB, 0 );

		iCtx->IASetVertexBuffers( 0, 1, &VB, &NTVertexStride, &VBOffset );
		iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

		iCtx->DrawIndexed( MG->nIdx, 0, 0 );
	}	
}

void Overlay::RenderPBuffer() {
	if( !SC->BBDrawCount )
		return;

	iCtx->IASetInputLayout( IL_NTVertex );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->VSSetShader( VS_2DOverlay, NULL, NULL );
	iCtx->OMSetRenderTargets( 1, &SC->RTarget_RTV, NULL );
	iCtx->PSSetShader( PS_PBuffer, NULL, NULL );
	iCtx->PSSetSamplers( 0, 1, &SS_Point_Wrap );
	iCtx->PSSetShaderResources( 0, 1, &SC->PBuffer_SRV );
	iCtx->RSSetState( RS_CullFront_Solid );
	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &M, 0, 0 );
	iCtx->VSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );

	iCtx->IASetVertexBuffers( 0, 1, &QuadVB, &NTVertexStride, &VBOffset );
	iCtx->IASetIndexBuffer( QuadIB, DXGI_FORMAT_R16_UINT, 0 );

	iCtx->DrawIndexed( 6, 0, 0 );
	iCtx->ClearRenderTargetView( SC->PBuffer_RTV, D3DXCOLOR( 0, 0, 0, 1 ) );
}