///////////////////////////////////////////////////////////////////////////////
/// @file RenderLight.cpp
///
/// @brief class for light renderstate settings
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Nov 2001
/// @author Lightning
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deRenderStates_priv.hpp"
#include "math.h"
#include "float.h"

WorldObjectClassImp(deRenderLight);

//constructor and destructor
deRenderLight::deRenderLight()
{
	//set the colors to 0
	memset(&m_Data.AmbientColor, 0, sizeof(m_Data.AmbientColor));
	memset(&m_Data.DiffuseColor, 0, sizeof(m_Data.DiffuseColor));
	memset(&m_Data.SpecularColor, 0, sizeof(m_Data.SpecularColor));

	//set and point at position 0
	memset(&m_Data.Position, 0, sizeof(m_Data.Position));
	memset(&m_Data.Direction, 0, sizeof(m_Data.Direction));

	//default to directional
	m_Data.LightType = LIGHTTYPE_DIRECTIONAL;

	//max range and falloff of 0
	m_Data.MaxRange = 0.0f;
	m_Data.FallOff = 0.0f;
	
	//attenuation defaults
	m_Data.Attenuation[0] = 0.0f;
	m_Data.Attenuation[1] = 0.0f;
	m_Data.Attenuation[2] = 0.0f;

	//theta and phi
	m_Data.Theta = 0.0f;	
	m_Data.Phi = 0.0f;

	//set the ID to 0
	m_Data.ID = 0;
	
	//default light to on
	m_Data.Status = deTRUE;

	return;
}

deRenderLight::~deRenderLight()
{
	return;
}

IdeRenderState::Type deRenderLight::GetRSType() const
{
	return IdeRenderState::TYPE_LIGHT;
}

void * deRenderLight::GetRSInterface(IdeRenderState::Type type) const
{
	if (type == TYPE_LIGHT)
		return ((IdeRenderLight*)this);
	if (type == IdeRenderState::TYPE_RSTATE)
		return ((IdeRenderState*)this);
	return NULL;
}

struct deRenderLight_Header_t
{
	short VersionMajor;
	short VersionMinor;
};
static const deRenderLight_Header_t deRenderLight_Header = {0,1};
/*
Version 0.1:
October 3, 2002
deColor {deFloat r,g,b,a}
enum LightType {LIGHTTYPE_POINT,LIGHTTYPE_SPOT,LIGHTTYPE_DIRECTIONAL}
deVertex {deFloat x,y,z}

short		Version_Major
short		Version_Minor
LightType	LightType;
deColor		AmbientColor;
deColor		DiffuseColor;
deColor		SpecularColor;
deVertex	Position;
deVertex	Direction;
float		MaxRange;
float		FallOff;
float		Attenuation[3];
float		Theta;
float		Phi;
int			ID;
deBoolean	Status;
[total length = 116 bytes]
*/

deBoolean deRenderLight::Serialize(IdeFile* FS)
{
	if (FS == NULL)
		return deFALSE;

	DE3D_STREAM_PUT(FS, deRenderLight_Header.VersionMajor, u16);
	DE3D_STREAM_PUT(FS, deRenderLight_Header.VersionMinor, u16);
	//light type
	DE3D_STREAM_PUT(FS, m_Data.LightType, s32);
	//ambient color
	DE3D_STREAM_PUT(FS, m_Data.AmbientColor.r, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.AmbientColor.g, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.AmbientColor.b, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.AmbientColor.a, deFloat);
	//diffuse color
	DE3D_STREAM_PUT(FS, m_Data.DiffuseColor.r, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.DiffuseColor.g, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.DiffuseColor.b, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.DiffuseColor.a, deFloat);
	//specular color
	DE3D_STREAM_PUT(FS, m_Data.SpecularColor.r, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.SpecularColor.g, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.SpecularColor.b, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.SpecularColor.a, deFloat);
	//position
	DE3D_STREAM_PUT(FS, m_Data.Position.x, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.Position.y, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.Position.z, deFloat);
	//direction
	DE3D_STREAM_PUT(FS, m_Data.Direction.x, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.Direction.y, deFloat);
	DE3D_STREAM_PUT(FS, m_Data.Direction.z, deFloat);
	//maximum range
	DE3D_STREAM_PUT(FS, m_Data.MaxRange, float);
	//falloff value
	DE3D_STREAM_PUT(FS, m_Data.FallOff, float);
	//attenuation values
	DE3D_STREAM_PUT(FS, m_Data.Attenuation[0], float);
	DE3D_STREAM_PUT(FS, m_Data.Attenuation[1], float);
	DE3D_STREAM_PUT(FS, m_Data.Attenuation[2], float);
	//theta angle value
	DE3D_STREAM_PUT(FS, m_Data.Theta, float);
	//phi angle value
	DE3D_STREAM_PUT(FS, m_Data.Phi, float);
#pragma todo(decide if this ID and status is really necessary in the world file)
	//ID (0-7)
	DE3D_STREAM_PUT(FS, m_Data.ID, s32);
	//on/off status
	DE3D_STREAM_PUT(FS, m_Data.Status, s8);
	
	return deTRUE;
}

deBoolean deRenderLight::DeSerialize(IdeFile* FS, long DataLength)
{
	if (FS == NULL)
		return deFALSE;
	
	u32 Length = 0;
	short VersionMajor, VersionMinor;

	DE3D_STREAM_GET(FS, VersionMajor, u16, Length, DataLength);
	DE3D_STREAM_GET(FS, VersionMinor, u16, Length, DataLength);
	
	// can't read a newer version than we're compiled for
	if (VersionMajor > deRenderLight_Header.VersionMajor ||
		(VersionMajor == deRenderLight_Header.VersionMajor && VersionMinor > deRenderLight_Header.VersionMinor))
		return deFALSE;
	
	if (VersionMajor == 0 && VersionMinor == 1)
	{
		//light type
		DE3D_STREAM_GET(FS, m_Data.LightType, s32, Length, DataLength);
		//ambient color
		DE3D_STREAM_GET(FS, m_Data.AmbientColor.r, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.AmbientColor.g, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.AmbientColor.b, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.AmbientColor.a, deFloat, Length, DataLength);
		//diffuse color
		DE3D_STREAM_GET(FS, m_Data.DiffuseColor.r, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.DiffuseColor.g, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.DiffuseColor.b, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.DiffuseColor.a, deFloat, Length, DataLength);
		//specular color
		DE3D_STREAM_GET(FS, m_Data.SpecularColor.r, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.SpecularColor.g, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.SpecularColor.b, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.SpecularColor.a, deFloat, Length, DataLength);
		//position
		DE3D_STREAM_GET(FS, m_Data.Position.x, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Position.y, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Position.z, deFloat, Length, DataLength);
		//direction
		DE3D_STREAM_GET(FS, m_Data.Direction.x, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Direction.y, deFloat, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Direction.z, deFloat, Length, DataLength);
		//maximum range
		DE3D_STREAM_GET(FS, m_Data.MaxRange, deFloat, Length, DataLength);
		//falloff value
		DE3D_STREAM_GET(FS, m_Data.FallOff, deFloat, Length, DataLength);
		//attenuation values
		DE3D_STREAM_GET(FS, m_Data.Attenuation[0], float, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Attenuation[1], float, Length, DataLength);
		DE3D_STREAM_GET(FS, m_Data.Attenuation[2], float, Length, DataLength);
		//theta angle value
		DE3D_STREAM_GET(FS, m_Data.Theta, float, Length, DataLength);
		//phi angle value
		DE3D_STREAM_GET(FS, m_Data.Phi, float, Length, DataLength);
	#pragma todo(decide if this ID and status is really necessary in the world file)
		//ID (0-7)
		DE3D_STREAM_GET(FS, m_Data.ID, s32, Length, DataLength);
		//on/off status
		DE3D_STREAM_GET(FS, m_Data.Status, u8, Length, DataLength);
		return deTRUE;
	}

	return deFALSE;	
}

deBoolean deRenderLight::SetLightType(LightType_t Type)
{
	//set the light type

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetLightType: Not Locked");

	//verify that the type is valid
	if(Type >= LIGHTTYPE_COUNT || Type <= LIGHTTYPE_INVALID)
		DEBUGRETURN(deFALSE, "SetLightType: Bad Type");
	
	m_Data.LightType = Type;
	return deTRUE;
}

deRenderLight::LightType_t deRenderLight::GetLightType() const
{
	//get the light type
	return m_Data.LightType;
}

deBoolean deRenderLight::SetAmbientColor(deColor *AmbientColor)
{
	//set the light ambient color

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetAmbientColor: Not Locked");

	m_Data.AmbientColor = *AmbientColor;
	return deTRUE;
}

deBoolean deRenderLight::GetAmbientColor(deColor *AmbientColor) const
{
	//get the ambient color
	*AmbientColor = m_Data.AmbientColor;

	//return true
	return deTRUE;
}

deBoolean deRenderLight::SetDiffuseColor(deColor *DiffuseColor)
{
	//set the light diffuse color

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetDiffuseColor: Not Locked");

	m_Data.DiffuseColor = *DiffuseColor;
	return deTRUE;
}

deBoolean deRenderLight::GetDiffuseColor(deColor *DiffuseColor) const
{
	//get the diffuse color
	*DiffuseColor = m_Data.DiffuseColor;

	//return true
	return deTRUE;
}

deBoolean deRenderLight::SetSpecularColor(deColor *SpecularColor)
{
	//set the light specular color

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetSpecularColor: Not Locked");

	m_Data.SpecularColor = *SpecularColor;
	return deTRUE;
}

deBoolean deRenderLight::GetSpecularColor(deColor *SpecularColor) const
{
	//get the specular color
	*SpecularColor = m_Data.SpecularColor;

	return deTRUE;
}

deBoolean deRenderLight::SetPosition(deVertex *Position)
{
	//set the light position

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetPosition: Not Locked");

	m_Data.Position = *Position;
	return deTRUE;
}

deBoolean deRenderLight::GetPosition(deVertex *Position) const
{
	//get the position
	*Position = m_Data.Position;
	return deTRUE;

}

deBoolean deRenderLight::SetDirection(deVertex *Direction)
{
	//set the light direction

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetDirection: Not Locked");

	m_Data.Direction = *Direction;
	return deTRUE;
}

deBoolean deRenderLight::GetDirection(deVertex *Direction) const
{
	//get the direction
	*Direction = m_Data.Direction;
	return deTRUE;
}

deBoolean deRenderLight::SetRange(float MaxRange)
{
	//set the light range

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetRange: Not Locked");

	//make sure it isn't too large
	if(MaxRange > sqrtf(FLT_MAX))
		return deFALSE;

	m_Data.MaxRange = MaxRange;
	return deTRUE;
}

float deRenderLight::GetRange() const
{
	//get the max range
	return m_Data.MaxRange;
}

deBoolean deRenderLight::SetFallOff(float FallOff)
{
	//set the light fall off

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetFallOff: Not Locked");

	m_Data.FallOff = FallOff;
	return deTRUE;
}

float deRenderLight::GetFallOff() const
{
	//get the fall off
	return m_Data.FallOff;
}

deBoolean deRenderLight::SetAttenuation(long Number, float Value)
{
	//set the light attenuation

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetAttenuation: Not Locked");

	//make sure the number is from 0 to 2
	if(Number < 0 || Number > 2)
		DEBUGRETURN(deFALSE, "SetLightType: Value outside valid range (0-2)");

	m_Data.Attenuation[Number] = Value;
	return deTRUE;
}

float deRenderLight::GetAttenuation(long Number) const
{
	//get the attenuation

	if(Number < 0 || Number > 2)
		return 0;

	return m_Data.Attenuation[Number];
}

deBoolean deRenderLight::SetTheta(float Theta)
{
	//set the light theta

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetTheta: Not Locked");

	m_Data.Theta = Theta;
	return deTRUE;
}

float deRenderLight::GetTheta() const
{
	//get the theta
	return m_Data.Theta;
}

deBoolean deRenderLight::SetPhi(float Phi)
{
	//set the light phi

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetPhi: Not Locked");

	m_Data.Phi = Phi;
	return deTRUE;
}

float deRenderLight::GetPhi() const
{
	//get the phi
	return m_Data.Phi;
}

deBoolean deRenderLight::SetID(long ID)
{
	//set the light id/number

	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetID: Not Locked");

	m_Data.ID = ID;
	return deTRUE;
}

long deRenderLight::GetID() const
{
	//return which light id we represent
	return m_Data.ID;
}

deBoolean deRenderLight::SetStatus(deBoolean On)
{
	//set if the light is on or not
	
	//if not locked, fail
	if(!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetStatus: Not Locked");

	m_Data.Status = On;
	return deTRUE;
}

deBoolean deRenderLight::GetStatus() const
{
	return m_Data.Status;
}

const deRenderLight::LightData* deRenderLight::GetAllData() const
{
	return &m_Data;
}

deBoolean deRenderLight::SetAllData(const LightData* Data)
{
	if (!m_LockFlag)
		DEBUGRETURN(deFALSE, "SetAllData: Not Locked");

	memcpy(&m_Data, Data, sizeof(LightData));

	return deTRUE;
}

