#include "Scene.h"
#include "vObject.h"
//#include "D3D11Effect.h"
#include "SurfManager.h"

ID3D11Buffer *HazeManager::VB = NULL;
ID3D11Buffer *HazeManager::IB = NULL;
DWORD HazeManager::nIdx = 0;
D3D11Config *HazeManager::cfg = NULL;
HVERTEX *HazeManager::_VB = NULL;//[HORIZON_NSEG*2];
float HazeManager::CosP[HORIZON_NSEG] = { 0.0f };
float HazeManager::SinP[HORIZON_NSEG] = { 0.0f };

HazeManager::HazeManager( vPlanet *_planet ) : D3D11Effect() {
	planet = _planet;
	obj = planet->obj;
	rad = oapiGetSize( obj );

	atmc = (ATMCONST*)oapiGetPlanetAtmConstants( obj );
	if( atmc )  {
		VECTOR3 tmp = *(VECTOR3*)oapiGetObjectParam( obj, OBJPRM_PLANET_HAZECOLOUR );
		basecol.r = (float)tmp.x;
		basecol.g = (float)tmp.y;
		basecol.b = (float)tmp.z;
		basecol.a = 1.0f;
		hralt = (float)(atmc->horizonalt/rad);
		dens0 = (float)( min( 1.0, atmc->horizonalt/64e3*(*(double*)oapiGetObjectParam( obj, OBJPRM_PLANET_HAZEDENSITY ) ) ) );
	}
	else {
		basecol = D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f );
		hralt = 0.0f;
		dens0 = 1.0f;
	}

	if( *(bool*)oapiGetObjectParam( obj, OBJPRM_PLANET_HASCLOUDS ) ) {
		hshift = *(double*)oapiGetObjectParam( obj, OBJPRM_PLANET_HAZESHIFT );
		cloudalt = *(double*)oapiGetObjectParam( obj, OBJPRM_PLANET_CLOUDALT );
	}
	else {
		hshift = 0.0;
		cloudalt = 0.0;
	}

	hscale = (float)(1.0 - *(double*)oapiGetObjectParam( obj, OBJPRM_PLANET_HAZEEXTENT ) );
}

HazeManager::~HazeManager() {
}

void HazeManager::GlobalInit( D3D11Config *_cfg ) {
	cfg = _cfg;
	InitHazeEffect();

	_VB = new HVERTEX [HORIZON_NSEG*2];

	nIdx = HORIZON_NSEG*2+2;
	WORD idata[HORIZON_NSEG*2+2];	

	DWORD j;
	double phi;
	for( j = 0; j < HORIZON_NSEG; j++ ) {
		idata[j*2] = j*2+1;
		idata[j*2+1] = j*2;
	}
	idata[j*2] = 1,	idata[j*2+1] = 0;

	for( j = 0; j < HORIZON_NSEG; j++ ) {
		//pos will be updated at first Render.
		//we don't need U coord...
		_VB[j*2].tex = 1.0f;
		_VB[j*2+1].tex = 0.0f;
		phi = (double)j/(double)HORIZON_NSEG*PI2;
		CosP[j] = (float)cos(phi),	SinP[j] = (float)sin(phi);
	}

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

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

	HR( Dev->CreateBuffer( &desc, &sdata, &VB ) );

	ZeroMemory( &desc, sizeof(desc) );
	desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	desc.ByteWidth = 2*(2*HORIZON_NSEG+2);
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;

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

	HR( Dev->CreateBuffer( &desc, &sdata, &IB ) );
}

void HazeManager::SaveParams() {
	oapiGetRotationMatrix( obj, &grot );
	oapiGetGlobalPos( obj, &gpos );
}

void HazeManager::Render( D3DXMATRIX *wmat, bool dual ) {
	VECTOR3 psun;
	D3DXMATRIX imat, transm;
	float dr, dh, cost, sint, cosp, sinp;
	float alpha;
	double phi, h1, h2, r1, r2, colofs;

	D3DMAT_MatrixInvert( &imat, wmat );
	VECTOR3 rpos = { imat._41, imat._42, imat._43 };
	double cdist = length( rpos );

	alpha = (float)(dens0*min( 1.0, (cdist - 1.0)*200.0 ));
	if( !dual )
		alpha = 1.0 - alpha;
	if( alpha <= 0.0 )
		return;		//nothing to do

	if( dual && cdist < 1.001 )
		return;

	VECTOR3 cpos = { 0.0, cdist, 0.0 };
	double id = 1.0/max( cdist, 1.001 );
	double id2 = 1.0/cdist;
	double visrad = acos(id);	// aperture of visibility sector
	double sinv = sin(visrad);

	h1 = (float)id;
	h2 = h1 + (float)(hralt*id);
	r1 = (float)sinv,	r2 = (1.0f + hralt)*r1;

	if( hshift && (cdist - 1.0) > cloudalt/rad ) {
		dr = (float)(hshift*sinv);
		dh = (float)(hshift*id);
		h1 += dh,	h2 += dh;
		r1 += dr,	r2 += dr;
	}

	float dens = (float)max( 1.0, 1.4 - 0.3/hralt*(cdist - 1.0) );	// saturate haze colour at low altitudes
	if( dual )
		dens *= (float)(0.5 + 0.5/cdist);							// scale down intensity at large distances

	normalise( rpos );
	cost = (float)rpos.y,	sint = (float)sqrt(1.0 - cost*cost);
	phi = atan2( rpos.z, rpos.x ),	cosp = (float)cos(phi),		sinp = (float)sin(phi);

	D3DXMATRIX rmat = D3DXMATRIX(	cost*cosp, -sint, cost*sinp, 0,
									sint*cosp,  cost, sint*sinp, 0,
									-sinp,      0,    cosp,      0,
									0,          0,    0,         1	);

	D3DXMatrixMultiply( &transm, &rmat, wmat );

	MATRIX3 rrmat = {	cost*cosp, -sint, cost*sinp,
						sint*cosp,  cost, sint*sinp,
						-sinp,      0,    cosp			};

//	MATRIX3 grot;
//	VECTOR3 gpos;
//	oapiGetRotationMatrix( obj, &grot );
//	oapiGetGlobalPos( obj, &gpos );

	psun = tmul( grot, -gpos );		// sun in planet coords
	psun = mul( rrmat, psun );		// sun in camera-relative horizon coords
	VECTOR3 cs = psun - cpos;
	normalise( cs );				// camera->sun
	normalise( psun );
	float psunx = (float)psun.x,	psuny = (float)psun.y,		psunz = (float)psun.z;
	colofs = ( dual ? 0.4 : 0.3 );

	float intr, intg, intb;
	double csun, colsh, maxred, maxgreen, maxblue, minred, mingreen, minblue;
	UINT32 i, j;
	VECTOR3 hp, cp;
	D3DXCOLOR col;
	for( i = j = 0; i < HORIZON_NSEG; i++ ) {
		hp.x = _VB[j].pos.x = r1*CosP[i];
		hp.y = _VB[j].pos.y = h1;
		hp.z = _VB[j].pos.z = r1*SinP[i];

		csun = dotp( hp, psun );

		cp = hp - cpos;
		normalise( cp );
		colsh = 0.5*(dotp( cp, cs ) + 1.0);

		maxred = colofs - 0.18*colsh;
		minred = maxred - 0.4;

		maxgreen = colofs - 0.1*colsh;
		mingreen = maxgreen - 0.4;

		maxblue = colofs;
		minblue = maxblue - 0.4;

		if( csun > maxred )			intr = 1.0f;
		else if( csun < minred )	intr = 0.0f;
		else						intr = (float)((csun - minred)*2.5);

		if( csun > maxgreen )		intg = 1.0f;
		else if( csun < mingreen )	intg = 0.0f;
		else						intg = (float)((csun - mingreen)*2.5);

		if( csun > maxblue )		intb = 1.0f;
		else if( csun < minblue )	intb = 0.0f;
		else						intb = (float)((csun - minblue)*2.5);

		col.r = intr*min( 1.0f, dens*basecol.r );
		col.g = intg*min( 1.0f, dens*basecol.g );
		col.b = intb*min( 1.0f, dens*basecol.b );
		col.a = alpha;

		_VB[j].col = col;
		j++;
		_VB[j].pos.x = r2*CosP[i];
		_VB[j].pos.y = h2;
		_VB[j].pos.z = r2*SinP[i];
		_VB[j].col = col;
		j++;
	}

	D3D11_MAPPED_SUBRESOURCE Buf;
	UINT HVertexStride = 32, VBOffset = 0;
	float BlendFactors[4] = { 1.0f };

	ZeroMemory( &Buf, sizeof(Buf) );
	dCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &Buf );
	memcpy( Buf.pData, _VB, sizeof(HVERTEX)*HORIZON_NSEG*2 );
//	Buf.pData = _VB;
	dCtx->Unmap( VB, 0 );

	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
	dCtx->IASetInputLayout( IL_HazeVertex );
	dCtx->VSSetShader( VS_Haze, NULL, NULL );
	dCtx->PSSetShader( PS_Haze, NULL, NULL );
	dCtx->OMSetBlendState( BS_SrcAlpha_Blend_0, BlendFactors, 0xFFFFFFFF );//??
	dCtx->OMSetDepthStencilState( DSS_Write0_Test0, 0 );
	dCtx->RSSetState( RS_None_Solid );

	D3DXMATRIX M = transm * *SC->GetVP();
	dCtx->UpdateSubresource( cb_VS_Haze_0, 0, NULL, &M, 0, 0 );
	dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_Haze_0 );

	dCtx->IASetVertexBuffers( 0, 1, &VB, &HVertexStride, &VBOffset );
	dCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

	dCtx->DrawIndexed( nIdx, 0, 0 );

//	DWORD sz = sizeof( HVERTEX );
//	sz = sz;

	if( dual ) {
		h2 = h1;
		r2 = hscale*r1*r1;

		for( i = j = 0; i < HORIZON_NSEG; i++ ) {
			j++;
			_VB[j].pos.x = r2*CosP[i];
			_VB[j].pos.y = h2;
			_VB[j].pos.z = r2*SinP[i];
			j++;
		}

		ZeroMemory( &Buf, sizeof(Buf) );
		dCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &Buf );
		memcpy( Buf.pData, _VB, sizeof(HVERTEX)*HORIZON_NSEG*2 );
	//	Buf.pData = _VB;
		dCtx->Unmap( VB, 0 );

		dCtx->IASetVertexBuffers( 0, 1, &VB, &HVertexStride, &VBOffset );

		dCtx->DrawIndexed( nIdx, 0, 0 );
	}
}

void HazeManager::GlobalExit() {
	VB->Release();
	IB->Release();
	delete [ ] _VB;
}