#include "D3D11Client.h"
#include "CelSphere.h"

CelSphere::CelSphere() : D3D11Effect() {
	Stars = NULL;
	ConstLines = LatGrid = LngGrid = NULL;
	sphere_r = 1e6f;
	
	InitCelSphereEffect();

	LoadStars();
	LoadConstLines();
	CreateGrid();
}

CelSphere::~CelSphere() {
	for( DWORD j = 0; j < nbuf; j++ )
		REL( Stars[j] );
	REL( ConstLines );
	REL( LatGrid );
	REL( LngGrid );

	REL( cb_VS_Star_Line );
	REL( cb_PS_Line );
	REL( IL_CVertex );
	REL( VS_Star_Line );
	REL( PS_Star );
	REL( PS_Line );
}

void CelSphere::LoadStars() {
	const DWORD bsize = 8;
	double a, b, xz;

	StarRenderPrm *prm = (StarRenderPrm*)gc->GetConfigParam( CFGPRM_STARRENDERPRM );

	if( prm->mag_lo > prm->mag_hi ) {
		if( prm->map_log )
			a = -log( prm->brt_min )/(prm->mag_lo - prm->mag_hi);
		else {
			a = (1.0 - prm->brt_min)/(prm->mag_hi - prm->mag_lo);
			b = prm->brt_min - prm->mag_lo*a;
		}
	}
	else
		oapiWriteLog( "Inconsistent magnitude limits for background stars" );

//	float c;
	int lvl, plvl = 256;
	StarRec *data = new StarRec [MAXSTARVERTEX];
	STARVERTEX *idata = new STARVERTEX [MAXSTARVERTEX];
	FILE *file;
	DWORD j, k, nvtx, idx = 0;
	D3D11_BUFFER_DESC desc;
	D3D11_SUBRESOURCE_DATA sdata;

	nbuf = 0;
	Stars = NULL;
//	Stars[0] = NULL;

	if( !(file = fopen( "Star.bin", "rb" )) )
		return;

	nsvtx = 0;
	while( nvtx = fread( data, sizeof(StarRec), MAXSTARVERTEX, file ) ) {
		for( j = 0; j < nvtx; j++ )
			if( data[j].mag > prm->mag_lo ) {
				nvtx = j;
				break;
			}
		if( nvtx ) {
			ID3D11Buffer **tmp = new ID3D11Buffer *[nbuf+1];
			if( Stars ) {
				memcpy( tmp, Stars, sizeof(ID3D11Buffer*)*nbuf );
				delete [ ] Stars;
			}
			Stars = tmp;
			nbuf++;
			Stars[nbuf-1] = NULL;

			for( j = 0; j < nvtx; j++ ) {
				StarRec &rec = data[j];
				STARVERTEX &v = idata[j];

				xz = sphere_r*cos(rec.lat);
				v.pos.x = (float)(xz*cos(rec.lng));
				v.pos.y = (float)(xz*sin(rec.lng));
				v.pos.z = (float)(sphere_r*sin(rec.lat));

				if( prm->map_log )	v.col = (float)min( 1.0, max( prm->brt_min, exp( -(rec.mag - prm->mag_hi)*a ) ) );
				else				v.col = (float)min( 1.0, max( prm->brt_min, a*rec.mag + b ) );

				lvl = (int)(v.col*256.0*0.5);
				lvl = (lvl > 255 ? 255 : lvl);

				for( k = lvl; k < (DWORD)plvl; k++ )
					lvlid[k] = idx;
				plvl = lvl;
				idx++;
			}

			ZeroMemory( &desc, sizeof(desc) );
			desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
			desc.ByteWidth = 16*nvtx;
			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, &Stars[nbuf-1] ) );

			nsvtx += nvtx;
		}
		if( nvtx < MAXSTARVERTEX )
			break;
	}
	fclose( file );

	delete [ ] data;
	delete [ ] idata;

	for( j = 0; j < (DWORD)plvl; j++ )
		lvlid[j] = idx;
}

void CelSphere::LoadConstLines() {
	const DWORD maxline = 1000;	// plenty for default data base, but check with custom data bases!

	oapi::GraphicsClient::ConstRec *cline = new oapi::GraphicsClient::ConstRec [maxline];
	ncline = gc->LoadConstellationLines( maxline, cline );
	if( ncline ) {
		D3DXVECTOR4 *data = new D3DXVECTOR4 [ncline*2];
		DWORD j;
		double xz;

		for( j = 0; j < ncline; j++ ) {
			oapi::GraphicsClient::ConstRec *rec = cline+j;
			xz = sphere_r*cos( rec->lat1 );
			data[j*2].x = (float)(xz*cos( rec->lng1 ));
			data[j*2].z = (float)(xz*sin( rec->lng1 ));
			data[j*2].y = (float)(sphere_r*sin( rec->lat1 ));
			data[j*2].w = 1.0f;
			xz = sphere_r*cos( rec->lat2 );
			data[j*2+1].x = (float)(xz*cos( rec->lng2 ));
			data[j*2+1].z = (float)(xz*sin( rec->lng2 ));
			data[j*2+1].y = (float)(sphere_r*sin( rec->lat2 ));
			data[j*2+1].w = 1.0f;
		}

		D3D11_BUFFER_DESC desc;
		ZeroMemory( &desc, sizeof(desc) );
		desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		desc.ByteWidth = ncline*2*16;
		desc.CPUAccessFlags = 0;
		desc.MiscFlags = 0;
		desc.StructureByteStride = 0;
		desc.Usage = D3D11_USAGE_IMMUTABLE;

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

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

		delete [ ] data;
	}
	delete [ ] cline;
}

void CelSphere::CreateGrid() {
	DWORD j, i, idx;
	double lat, lng, xz, y;
	D3DXVECTOR4 *buf = new D3DXVECTOR4 [12*(NSEG+1)];
	D3D11_BUFFER_DESC desc;
	D3D11_SUBRESOURCE_DATA sdata;

	for( j = idx = 0; j <= 10; j++ ) {
		lat = (j-5)*15.0*RAD;
		xz = sphere_r*cos( lat );
		y = sphere_r*sin( lat );
		for( i = 0; i <= NSEG; i++ ) {
			lng = PI2*(double)i/(double)NSEG;
			buf[idx].x = (float)(xz*cos( lng ));
			buf[idx].z = (float)(xz*sin( lng ));
			buf[idx].y = (float)y;
			buf[idx].w = 1.0f;
			idx++;
		}
	}

	ZeroMemory( &desc, sizeof(desc) );
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc.ByteWidth = 16*(NSEG+1)*11;//idx;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;
	
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = buf;

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

	for( j = idx = 0; j < 12; j++ ) {
		lng = j*15.0*RAD;
		for( i = 0; i <= NSEG; i++ ) {
			lat = PI2*(double)i/(double)NSEG;
			xz = sphere_r*cos( lat );
			y = sphere_r*sin( lat );
			buf[idx].x = (float)(xz*cos( lng ));
			buf[idx].z = (float)(xz*sin( lng ));
			buf[idx].y = (float)y;
			buf[idx].w = 1.0f;
			idx++;
		}
	}

	ZeroMemory( &desc, sizeof(desc) );
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc.ByteWidth = 16*(NSEG+1)*12;//idx;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;
	
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = buf;

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

	delete [ ] buf;
}

void CelSphere::RenderCelestialSphere( D3DXMATRIX *VP, float skybrt, const D3DXVECTOR3 *bgcol ) {
/*
	Renders stars, constellations, labels.
*/
//	static const D3DXCOLOR EGColor = D3DXCOLOR( 0.0f, 0.0f, 0.4f, 1.0f ), EColor = D3DXCOLOR( 0.0f, 0.0f, 0.8f, 1.0f );

	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP );
	dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_Star_Line );
	dCtx->IASetInputLayout( IL_CVertex );
	dCtx->VSSetShader( VS_Star_Line, NULL, NULL );
	
	DWORD PMode = SC->GetPlanetariumFlag();
	if( PMode & PLN_ENABLE ) {
		float linebrt = 1.0f - skybrt;

		dCtx->PSSetShader( PS_Line, NULL, NULL );
		dCtx->PSSetConstantBuffers( 1, 1, &cb_PS_Line );
		dCtx->UpdateSubresource( cb_VS_Star_Line, 0, NULL, SC->GetVP(), 0, 0 );
		//ecliptic grid
		if( PMode & PLN_EGRID ) {
			D3DXVECTOR4 vColor( 0.0f, 0.0f, 0.4f*linebrt, 1.0f );
			dCtx->UpdateSubresource( cb_PS_Line, 0, NULL, &vColor, 0, 0 );
			RenderGrid( !(PMode & PLN_ECL) );
		}
		if( PMode & PLN_ECL ) {
			D3DXVECTOR4 vColor( 0.0f, 0.0f, 0.8f*linebrt, 1.0f );
			dCtx->UpdateSubresource( cb_PS_Line, 0, NULL, &vColor, 0, 0 );
			RenderGreatCircle();
		}
		//celestial grid
		if( PMode & (PLN_CGRID | PLN_EQU) ) {
			static const double obliquity = 0.4092797095927;
			double coso = cos(obliquity),	sino = sin(obliquity);
			D3DXMATRIX rot(		1.0f, 0.0f, 0.0f, 0.0f,
								0.0f, (float)coso, (float)sino, 0.0f,
								0.0f, -(float)sino, (float)coso, 0.0f,
								0.0f, 0.0f, 0.0f, 1.0f );

			D3DXMatrixMultiply( &rot, &rot, SC->GetVP() );
			dCtx->UpdateSubresource( cb_VS_Star_Line, 0, NULL, &rot, 0, 0 );

			if( PMode & PLN_CGRID ) {
				D3DXVECTOR4 vColor( 0.35f*linebrt, 0.0f, 0.35f*linebrt, 1.0f );
				dCtx->UpdateSubresource( cb_PS_Line, 0, NULL, &vColor, 0, 0 );
				RenderGrid( !(PMode & PLN_EQU) );
			}
			if( PMode & PLN_EQU ) {
				D3DXVECTOR4 vColor( 0.7f*linebrt, 0.0f, 0.7f*linebrt, 1.0f );
				dCtx->UpdateSubresource( cb_PS_Line, 0, NULL, &vColor, 0, 0 );
				RenderGreatCircle();
			}
		}
		//constellation lines
		dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINELIST );
		if( PMode & PLN_CONST ) {
			D3DXVECTOR4 vColor( 0.4f*linebrt, 0.3f*linebrt, 0.2f*linebrt, 1.0f );
			dCtx->UpdateSubresource( cb_PS_Line, 0, NULL, &vColor, 0, 0 );
			RenderConstellations();
		}
	}
	//stars
	dCtx->UpdateSubresource( cb_VS_Star_Line, 0, NULL, SC->GetVP(), 0, 0 );
	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
	dCtx->PSSetShader( PS_Star, NULL, NULL );
	RenderStars( bgcol );

	const oapi::GraphicsClient::LABELLIST *cmlist;
	DWORD x = gc->GetCelestialMarkers( &cmlist );
	x = x;
	RenderCelestialMarkers( VP );
}

void CelSphere::RenderCelestialMarkers( D3DXMATRIX *VP ) {
/*	todo	*/
}

void CelSphere::RenderStars( const D3DXVECTOR3 *bgcol ) {
	//nmax = -1;
	DWORD j, i, nmax = nsvtx;
	const UINT CVertexStride = 16, Offset = 0;
	
	if( bgcol ) {
		int bglvl = min( 255, (int)((min( bgcol->x, 1.0f ) + min( bgcol->y, 1.0f ) + min( bgcol->z, 1.0f ))*128.0f));
		nmax = min( nmax, (DWORD)lvlid[bglvl] );
	}

	for( i = j = 0; i < nmax; i += MAXSTARVERTEX, j++ ) {
		dCtx->IASetVertexBuffers( 0, 1, &Stars[j], &CVertexStride, &Offset );
		dCtx->Draw( min( nmax-i, MAXSTARVERTEX ), 0 );
	}
}

void CelSphere::RenderConstellations() {
	const UINT CVertexStride = 16, VBOffset = 0;

	dCtx->IASetVertexBuffers( 0, 1, &ConstLines, &CVertexStride, &VBOffset );
	dCtx->Draw( ncline*2, 0 );
}

void CelSphere::RenderGrid( bool eqline ) {
	const UINT CVertexStride = 16, VBOffset = 0;
	DWORD j;

	dCtx->IASetVertexBuffers( 0, 1, &LngGrid, &CVertexStride, &VBOffset );
	for( j = 0; j <= 10; j++ )
		if( eqline || j != 5 )
			dCtx->Draw( NSEG+1, j*(NSEG+1) );

	dCtx->IASetVertexBuffers( 0, 1, &LatGrid, &CVertexStride, &VBOffset );
	for( j = 0; j < 12; j++ )
		dCtx->Draw( NSEG+1, j*(NSEG+1) );
}

void CelSphere::RenderGreatCircle() {
	const UINT CVertexStride = 16, VBOffset = 0;

	dCtx->IASetVertexBuffers( 0, 1, &LngGrid, &CVertexStride, &VBOffset );
	dCtx->Draw( (NSEG+1), 5*(NSEG+1) );
}