/*********************************************************************//**
*	Snow fall system.
*	Modul implementuje tridu, ktera se stara o efekt padani snehu.
*	Umoznuje nastavovat hustotu jednotlivych vlocek a prostor,
*	kde se maji vlocky generovat.
*
*	author: Michal Jirous
*	date: 09.04.2009
*	file: snow_system.cpp
**********************************************************************/

#include "snow_system.h"

SnowSystem snowSystem;

SnowSystem::SnowSystem()
{
	m_iDensity = SNOW_DENSITY_MEDIUM;
	m_fTimeValue = 0;
	m_iuTotalAreas = 0;
	m_iNumPoints = 0;
}


void SnowSystem::addArea( BoundingBox &bounds )
{
	m_SnowAreas.push_back( bounds );
	m_fMinimumZ = std::min( m_fMinimumZ, bounds.m_fBounds[MIN_Z] );
	m_fStartZ = std::max( m_fStartZ, bounds.m_fBounds[MAX_Z] );
	m_iuTotalAreas = m_SnowAreas.size();
}

void SnowSystem::reset()
{
	m_iNumPoints = 0;
	m_SnowAreas.clear();
	m_iuTotalAreas = 0;
	m_Winds.clear();
	m_fStartZ = MIN_FLOAT;
}

void SnowSystem::generatePosition( int i )
{
	int area = rand() % m_iuTotalAreas;
	BoundingBox &box = m_SnowAreas[area];
	m_Points[i].x = rand() % (int)(box.m_fBounds[MAX_X] - box.m_fBounds[MIN_X]) + box.m_fBounds[MIN_X];
	m_Points[i].y = rand() % (int)(box.m_fBounds[MAX_Y] - box.m_fBounds[MIN_Y]) + box.m_fBounds[MIN_Y];
	m_Points[i].z = m_fStartZ;
}

#include "physics.h"
#include "level_collision.h"
void SnowSystem::updatePoints()
{
	Vector force( 0, 0, -9.85f );	//gravity

	for( std::list<EnvWind*>::iterator iter = m_Winds.begin(); iter != m_Winds.end(); ++iter )
		force += (*iter)->getWindForce();

	force = force * 0.03f * POINTS_PER_METER * 0.03f;

//#pragma omp parallel for	//neni to rychlejsi :P
	for( int i = 0; i < m_iNumPoints; ++i )
	{
		m_Points[i] += force;

		if( m_Points[i].z < m_fMinimumZ || collisionSystem.clippingHullCollision( m_Points[i] ) != CONTENTS_EMPTY )
			generatePosition( i );
	}
}

void SnowSystem::render( RenderData &renderData )
{
	if( !m_iNumPoints )
		return;

	m_fTimeValue += renderData.frameTimes.elapsed_seconds;

	while( m_fTimeValue > UPDATE_TIME )
	{	
		m_fTimeValue -= UPDATE_TIME;
		
		updatePoints();

	}
	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );

	glDisable( GL_TEXTURE_2D );
	glColor3f( 1,1,1 );

	glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
	glEnableClientState( GL_VERTEX_ARRAY );
	glVertexPointer(3, GL_FLOAT, 0, (float*)&m_Points);
	glPointSize( 2.0f );
	glDrawArrays( GL_POINTS, 0, m_iNumPoints );
	glDisableClientState(GL_VERTEX_ARRAY);
	glPopClientAttrib();
	glPopAttrib();
}
void SnowSystem::setDensity( int density )
{
	m_iDensity = getFromRange<int>( density, SNOW_DENSITY_LOW, SNOW_DENSITY_TOO_MANY );
}

void SnowSystem::addWind( EnvWind *wind )
{
	if( wind )
		m_Winds.push_back( wind );
}

void SnowSystem::update( float seconds )
{
	if( !m_iuTotalAreas )
		return;

	m_fGenerationTime += seconds;

	if( m_fGenerationTime > SNOW_GENERATION_TIME )
	{
		m_fGenerationTime -= SNOW_GENERATION_TIME;
		
		if( m_iNumPoints < SNOW_DENSITY_POINTS_COUNT[ m_iDensity ] )
		{
			generatePosition( m_iNumPoints );
			m_iNumPoints++;
		}
	}
}
