// Author : Chris Tolley
// Date   : 7/21/2004
// Plant.cpp contains the class definition for a class of plants to use
// in the survival game

#pragma warning ( disable : 4786 )
#pragma warning ( disable : 4305 )

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <math.h>

#include "viewFrustum.h"
#include "Plant.h"
#include "quadTreeTerrain.h"
#include "SurvivalEngine.h"
#include "ms3d.h"
#include "logger.h"

// Some constants to help us out
// The max age of a plant
const int MAX_AGE        = 255;
// The max size of a plant
const int MAX_SIZE		 = 255;
// The max scale value, used in rendering and collision detection
const int MAX_SCALE      = 4;
// The amount of time it takes for a plant to produce another plant
const int SEED_RESET	 = 64;
// The min point in a plants life that it is fertile
const float FERTILE_MIN  = 127;
// The max point in a plants life that it is fertile
const float FERTILE_MAX  = 254;
// The size the plant has to be in order to be fertile
const float FERTILE_SIZE = 128;

// Declara the static variables in plant
unsigned int    Plant::m_suiList	= 0;
SurvivalEngine* Plant::m_pEngine	= NULL;
texture         Plant::m_Texture;

// Constructor
Plant::Plant( const float x, const float y, const float z )
: m_fSize( .1 ), m_fAge( .1 ), m_fGrowthRate( 1 ), 
  m_fSeedTime( 0 ), m_fDirection( 1 )
{
	// Move the plant to it's position on the screen.
	m_mLocal.Translate( x, y, z );
}// End Plant::Constructor


// Destructor
Plant::~Plant()
{
}// End Plant::Destructor


// Renders the plant to the screen.  Overloaded from object
void Plant::Render()
{
	// Calculate the scale value based on the size
	float scale = ( m_fAge * MAX_SCALE ) / MAX_AGE;

	// Make sure the plant is in the view frustum before we draw it
	float cx = 0;  float cy = 0; float cz = 0;
	m_mLocal.Transform( cx, cy, cz );

	// If the plant is inside the view frustum
	if( viewFrustum::sphereInFrustum
						( cx, cy, cz, cx + scale, cy + scale, cz + scale ) )
	{
		// Set up the matrix
		glPushMatrix();
		glMultMatrixf( m_mLocal.get() );

		// Preform the scale
		glScalef( scale, scale, scale );

		// If we are dying, then set a color value
		if( m_fDirection < 0 )
		{
			// Calculate the color to make this plant
			float color = m_fAge / MAX_AGE;
			glColor3f( color, color, color );
		}// End if

		// Call the list to draw the sphere
		glCallList( m_suiList );

		// Set the color to full brightness so that things will look right
		glColor3f( 1, 1, 1 );
		
		// Clean up the matrix stack
		glPopMatrix();
	}// End if
}// End Plant::Render


// Updates the plant.  Overloaded from object
void Plant::Update( float elapsedSec )
{
	// Make elapsedSec negative it direction if negative
	elapsedSec *= m_fDirection;

	// Add the time that has elasped since the last update to
	// the age of this plant
	m_fAge += elapsedSec;

	// Update the size of the plant
	m_fSize += ( elapsedSec * m_fGrowthRate );

	// Update the seedtime
	m_fSeedTime -= elapsedSec;
	if( m_fSeedTime < 0 )
		m_fSeedTime = 0;

	// Check to see if it is time for this plant to produce a seed
	if( m_fAge >= FERTILE_MIN && m_fAge <= FERTILE_MAX )
	{
		// If the plant is the correct size
		if( m_fSize >= FERTILE_SIZE )
		{
			// If seedtime is zero 
			if( m_fSeedTime == 0 )
			{
				// then it is time to produce another seed
				// Create a new position on the map
				int x = ( rand() % 5 ) + 5;
				int z = ( rand() % 5 ) + 5;

				// Make them negative half of the time
				if( ( rand() % 2 ) == 1 )
					x = x * -1;
				if( ( rand() % 2 ) == 1 )
					z = z * -1;

				// Offset x and z by the position of this plant
				float px = 0; float py = 0; float pz = 0;
				m_mLocal.Transform( px, py, pz );
				x += px;  z += pz;

				// Call the function pointer to insert a new plant onto the map
				m_pEngine->CreatePlant( x, z );

				// Set seed time for 30 seconds
				m_fSeedTime = SEED_RESET;
			}// End if
		}// End if
	}// End if

	// Check to see if it is time to switch from growth to decay
	if( m_fAge >= MAX_AGE )
	{
		m_fDirection = -1;
	}// End if
}// End Plant::Update


// Returns true if this Object is inside the sphere.  Derived from Object
bool Plant::InSphere( float x, float z, float radius )
{
	// Get the center point of this Plant
	float px, py, pz;
	GetPos( px, py, pz );

	// Calculate the distance between this plant and point x,z
	float distance = sqrt( ( ( x - px ) * ( x - px ) ) +
						   ( ( z - pz ) * ( z - pz ) ) );

	// If the distance between this plant and x,z is less than radius
	// plus this of this plant, then it is in the sphere
	return distance <= ( radius + ( m_fAge * MAX_SCALE ) / MAX_AGE );
}// End Plant::InSphere


// Returns the amount of food available from this plant
int Plant::GetFoodUnits()
{
	// Calculate the return value based on the plants age
	// 8 is a debug value only
	int returnValue = 8;

	// Add the return value * the direction of growth to the plants age
	m_fAge += ( returnValue * m_fDirection );

	// Return the return value
	return returnValue;
}// End Plant::GetFoodUnits


// Initializes the display list used for all plants.
void Plant::InitList( const char* PlantModel, SurvivalEngine* engine )
{
	// Load the plant model file
	ms3d plantModel( PlantModel );

	// Create the display list
	Plant::m_suiList = glGenLists( 1 );

	// Make sure that the list is a valid list
	if(  m_suiList == 0 )
	{
		logger::event( "Plant::Initialize", 
							"Error in creating display list.", 0 );
		return;
	}// End if

	// Draw the plant and store the drawing commands in a display list
	glNewList( m_suiList, GL_COMPILE );
		glMatrixMode( GL_TEXTURE );
		glPushMatrix();
		glRotatef( 180, 1, 0, 0 );
		plantModel.DrawTexturedMesh();
		glPopMatrix();
		glMatrixMode( GL_MODELVIEW );
	glEndList();

	// Set the pointer to the engine
	Plant::m_pEngine = engine;
}// End Plant::InitList


// Cleans up the display list
void Plant::DltList()
{
	// Delete the list
	glDeleteLists( m_suiList, 1 );
}// End Plant::DltList