/*********************************************************************//**
*	Vitr
*	Trida Wind zde implementuje vitr, kde pomoci parametru lze
*	vytvorit silu, ktera simuluje vitr. Zakladni smer 
*	vetru je vektor(1,0,0). Smer se nahodne meni a rychlost
*	vysledneho siloveho vektoru v zavislosti na nastaveni.
*	Vitr pouziva spring system a efekt padani snehu.
*
*	author: Michal Jirous
*	date: 20.12.2008
*	file: wind.cpp
**********************************************************************/

#include "wind.h"

using namespace std;

Wind::Wind()
{
	memset( this, 0, sizeof( Wind ) );
	m_vecFinalForce.x = 1.0f;
	m_fSpeedOfChange = WIND_CHANGE_SPEED_DEFAULT;
}

/** @param angle Uhel ve stupnich. */
void Wind::setAngleX( float angle )
{
	m_fAngleXAxis = getFromRange( angle, -90.0f, 90.0f );
}

/** @param angle Uhel ve stupnich. */
void Wind::setAngleZ( float angle )
{
	m_fAngleZAxis = angle;
}

/** @param range Uhel ve stupnich. */
void Wind::setAngleXRange( float range )
{
	m_fAngleXRange = getFromRange( range, 0.0f, 180.0f );
}

/** @param range Uhel ve stupnich. */
void Wind::setAngleZRange( float range )
{
	m_fAngleZRange = getFromRange( range, 0.0f, 360.0f );
}

/** @param mag Velikost sily vetru. */
void Wind::setBasicMagnitude( float mag )
{
	m_fBaseMagnitude = getFromRange( mag, 0.0f, WIND_MAX_MAGNITUDE );
}

/** @param range Velikost sily vetru. */
void Wind::setMagnitudeRange( float range )
{
	m_fMagnitudeChangeRange = getFromRange( range, 0.0f, WIND_MAX_MAGNITUDE_RANGE );
}


/** @return Vysledna sila vetru. */
Vector Wind::getForce()
{
	return m_vecFinalForce;
}

void Wind::update()
{
	if( !m_bIsRunning )
	{
		m_vecFinalForce.clear();
		return;
	}

	float treshold = 0.0f;
	if( m_iChangeRate != WIND_CHANGE_NEVER )
	{
		switch( m_iChangeRate )
		{
			case WIND_CHANGE_OCCASIONALLY:
				treshold = WIND_OCCASIONALLY_PROBABILITY;
				break;
			case WIND_CHANGE_OFTEN:
				treshold = WIND_OCCASIONALLY_OFTEN;
				break;
			case WIND_CHANGE_ALWAYS:
				treshold = 1.0f;
				break;	
		}
	}


	if( ((float)(rand() % 100)) / 100.0f < treshold )
	{
		if( m_fMagnitudeChangeRange > 0.0f )
			m_fTargetMagnitude = max( 0.0f, m_fBaseMagnitude + (rand() % ((int)(m_fMagnitudeChangeRange*1000.0f)))/1000.0f - m_fMagnitudeChangeRange/2.0f  );
		if( compareFloats( m_fAngleXRange, 0, FLOAT_COMPARE_ACCURACY_PRECISION ) )
			m_fTargetAngleX = m_fAngleXAxis + rand() % ((int)m_fAngleXRange) - m_fAngleXRange/2.0f;
		if( compareFloats( m_fAngleZRange, 0, FLOAT_COMPARE_ACCURACY_PRECISION ) )
			m_fTargetAngleZ = m_fAngleZAxis + rand() % ((int)m_fAngleZRange) - m_fAngleZRange/2.0f;
	}


	if( m_fTargetAngleX > m_fCurrentAngleX )
		m_fCurrentAngleX += min( m_fTargetAngleX-m_fCurrentAngleX, m_fAngleXRange/m_fSpeedOfChange );
	else 
		m_fCurrentAngleX -= min( m_fCurrentAngleX-m_fTargetAngleX, m_fAngleXRange/m_fSpeedOfChange );

	if( m_fTargetAngleZ > m_fCurrentAngleZ )
		m_fCurrentAngleZ += min( m_fTargetAngleZ-m_fCurrentAngleZ, m_fAngleZRange/m_fSpeedOfChange );
	else 
		m_fCurrentAngleZ -= min( m_fCurrentAngleZ-m_fTargetAngleZ, m_fAngleZRange/m_fSpeedOfChange );

	if( m_fTargetMagnitude > m_fCurrentMagnitude )
		m_fCurrentMagnitude += min( m_fTargetMagnitude-m_fCurrentMagnitude, m_fMagnitudeChangeRange/m_fSpeedOfChange );
	else 
		m_fCurrentMagnitude -= min( m_fCurrentMagnitude-m_fTargetMagnitude, m_fMagnitudeChangeRange/m_fSpeedOfChange );

	m_vecFinalForce.createFrom2Angles( m_fCurrentAngleX, m_fCurrentAngleZ, m_fCurrentMagnitude );


}

/** @param speed modifikator rychlosti. */
void Wind::setSpeedOfChange( float speed )
{
	m_fSpeedOfChange = max( speed, WIND_CHANGE_SPEED_MODIFICATOR_MINIMUM );
}


void Wind::init()
{
	m_fTargetMagnitude = m_fBaseMagnitude;
	m_fCurrentMagnitude = m_fBaseMagnitude;

	m_fCurrentAngleZ = m_fAngleZAxis;
	m_fTargetAngleZ = m_fAngleZAxis;

	m_fCurrentAngleX = m_fAngleXAxis;
	m_fTargetAngleX = m_fAngleXAxis;
}

/** @param period Typ z vyctu wind_change_period. */
void Wind::setValueChangePeriod( int period )
{
	m_iChangeRate = period;
}
