/** 
 * @file llpartdata.h
 * @brief Particle system data packing
 *
 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLPARTDATA_H
#define LL_LLPARTDATA_H

#include "lluuid.h"
#include "v3math.h"
#include "v3dmath.h"
#include "v2math.h"
#include "v4color.h"

class LLMessageSystem;
class LLDataPacker;

const S32 PS_CUR_VERSION = 18;

//
// These constants are used by the script code, not by the particle system itself
//

enum LLPSScriptFlags
{
	// Flags for the different parameters of individual particles
	LLPS_PART_FLAGS,
	LLPS_PART_START_COLOR,
	LLPS_PART_START_ALPHA,
	LLPS_PART_END_COLOR,
	LLPS_PART_END_ALPHA,
	LLPS_PART_START_SCALE,
	LLPS_PART_END_SCALE,
	LLPS_PART_MAX_AGE,

	// Flags for the different parameters of the particle source
	LLPS_SRC_ACCEL,
	LLPS_SRC_PATTERN,
	LLPS_SRC_INNERANGLE,
	LLPS_SRC_OUTERANGLE,
	LLPS_SRC_TEXTURE,
	LLPS_SRC_BURST_RATE,
	LLPS_SRC_BURST_DURATION,
	LLPS_SRC_BURST_PART_COUNT,
	LLPS_SRC_BURST_RADIUS,
	LLPS_SRC_BURST_SPEED_MIN,
	LLPS_SRC_BURST_SPEED_MAX,
	LLPS_SRC_MAX_AGE,
	LLPS_SRC_TARGET_UUID,
	LLPS_SRC_OMEGA,
	LLPS_SRC_ANGLE_BEGIN,
	LLPS_SRC_ANGLE_END,

	LLPS_PART_BLEND_FUNC_SOURCE,
	LLPS_PART_BLEND_FUNC_DEST,
	LLPS_PART_START_GLOW,
	LLPS_PART_END_GLOW
};


class LLPartData
{
public:
	LLPartData() :
		mFlags(0),
		mMaxAge(0.f),
		mParameter(0.f)
	{
	}
	BOOL unpackLegacy(LLDataPacker &dp);
	BOOL unpack(LLDataPacker &dp);

	BOOL pack(LLDataPacker &dp);
	
	bool hasGlow() const;
	bool hasBlendFunc() const;

	// Masks for the different particle flags
	enum
	{
		LL_PART_INTERP_COLOR_MASK =		0x01,
		LL_PART_INTERP_SCALE_MASK =		0x02,
		LL_PART_BOUNCE_MASK =			0x04,
		LL_PART_WIND_MASK =				0x08,
		LL_PART_FOLLOW_SRC_MASK =		0x10,		// Follows source, no rotation following (expensive!)
		LL_PART_FOLLOW_VELOCITY_MASK =	0x20,		// Particles orient themselves with velocity
		LL_PART_TARGET_POS_MASK =		0x40,
		LL_PART_TARGET_LINEAR_MASK =	0x80,		// Particle uses a direct linear interpolation
		LL_PART_EMISSIVE_MASK =			0x100,		// Particle is "emissive", instead of being lit
		LL_PART_BEAM_MASK =				0x200,		// Particle is a "beam" connecting source and target
		LL_PART_RIBBON_MASK =			0x400,		// Particles are joined together into one continuous triangle strip

		// Not implemented yet!
		//LL_PART_RANDOM_ACCEL_MASK =		0x100,		// Particles have random acceleration
		//LL_PART_RANDOM_VEL_MASK =		0x200,		// Particles have random velocity shifts"
		//LL_PART_TRAIL_MASK =			0x400,		// Particles have historical "trails"

		//sYSTEM SET FLAGS
		LL_PART_DATA_GLOW =				0x10000,
		LL_PART_DATA_BLEND =			0x20000,

		// Viewer side use only!
		LL_PART_HUD =					0x40000000,
		LL_PART_DEAD_MASK =				0x80000000,
	};

	enum
	{
		LL_PART_BF_ONE = 0,
		LL_PART_BF_ZERO = 1,
		LL_PART_BF_DEST_COLOR = 2,
		LL_PART_BF_SOURCE_COLOR = 3,
		LL_PART_BF_ONE_MINUS_DEST_COLOR = 4,
		LL_PART_BF_ONE_MINUS_SOURCE_COLOR = 5,
		UNSUPPORTED_DEST_ALPHA = 6,
		LL_PART_BF_SOURCE_ALPHA = 7,
		UNSUPPORTED_ONE_MINUS_DEST_ALPHA = 8,
		LL_PART_BF_ONE_MINUS_SOURCE_ALPHA = 9,
		LL_PART_BF_COUNT = 10
	};

	static bool validBlendFunc(S32 func);

	void setFlags(const U32 flags);
	void setMaxAge(const F32 max_age);
	void setStartScale(const F32 xs, F32 ys);
	void setEndScale(const F32 xs, F32 ys);
	void setStartColor(const LLVector3 &rgb);
	void setEndColor(const LLVector3 &rgb);
	void setStartAlpha(const F32 alpha);
	void setEndAlpha(const F32 alpha);


	friend class LLPartSysData;
	friend class LLViewerPartSourceScript;

private:
	S32 getSize() const;

	// These are public because I'm really lazy...
public:
	U32					mFlags;						// Particle state/interpolators in effect
	F32					mMaxAge;					// Maximum age of the particle
	LLColor4			mStartColor;				// Start color
	LLColor4			mEndColor;					// End color
	LLVector2			mStartScale;				// Start scale
	LLVector2			mEndScale;					// End scale

	LLVector3			mPosOffset;					// Offset from source if using FOLLOW_SOURCE
	F32					mParameter;					// A single floating point parameter

	F32					mStartGlow;
	F32					mEndGlow;
	
	U8					mBlendFuncSource;
	U8					mBlendFuncDest;
};


class LLPartSysData
{
public:
	LLPartSysData();

	BOOL unpack(LLDataPacker &dp);
	BOOL unpackLegacy(LLDataPacker &dp);
	BOOL unpackBlock(const S32 block_num);
		
	static BOOL isNullPS(const S32 block_num); // Returns FALSE if this is a "NULL" particle system (i.e. no system)

	bool isLegacyCompatible() const;

	// Different masks for effects on the source
	enum
	{
		LL_PART_SRC_OBJ_REL_MASK		=	0x01,		// Accel and velocity for particles relative object rotation
		LL_PART_USE_NEW_ANGLE			=	0x02,		// Particles uses new 'correct' angle parameters.
	};

	// The different patterns for how particles are created
	enum
	{
		LL_PART_SRC_PATTERN_DROP =				0x01,
		LL_PART_SRC_PATTERN_EXPLODE =			0x02,
		// Not implemented fully yet
		LL_PART_SRC_PATTERN_ANGLE =				0x04,
		LL_PART_SRC_PATTERN_ANGLE_CONE =		0x08,
		LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY =	0x10,
	};


	void setBurstSpeedMin(const F32 spd) {	mBurstSpeedMin = llclamp(spd, -100.f, 100.f); }
	void setBurstSpeedMax(const F32 spd) {	mBurstSpeedMax = llclamp(spd, -100.f, 100.f); }
	void setBurstRadius(const F32 rad)	 {	mBurstRadius = llclamp(rad, 0.f, 50.f); }
	void setPartAccel(const LLVector3 &accel);
	void setUseNewAngle()	{ mFlags |=  LL_PART_USE_NEW_ANGLE; }
	void unsetUseNewAngle()	{ mFlags &= ~LL_PART_USE_NEW_ANGLE; }

	// Since the actual particle creation rate is
	// a combination of multiple parameters, we
	// need to clamp it using a separate method instead of an accessor.
	void clampSourceParticleRate();
	
	friend std::ostream&	 operator<<(std::ostream& s, const LLPartSysData &data);		// Stream a

	S32 getdataBlockSize() const;
	
private:
	BOOL unpackSystem(LLDataPacker &dp);

public:
	// Public because I'm lazy....

	//
	// There are two kinds of data for the particle system
	// 1. Parameters which specify parameters of the source (mSource*)
	// 2. Parameters which specify parameters of the particles generated by the source (mPart*)
	//

	U32		mCRC;
	U32		mFlags;

	U8		mPattern;						// Pattern for particle velocity/output
	F32		mInnerAngle;					// Inner angle for PATTERN_ANGLE
	F32		mOuterAngle;					// Outer angle for PATTERN_ANGLE
	LLVector3 mAngularVelocity;				// Angular velocity for emission axis (for PATTERN_ANGLE)

	F32		mBurstRate;						// How often to do a burst of particles
	U8		mBurstPartCount;				// How many particles in a burst
	F32		mBurstRadius;
	F32		mBurstSpeedMin;					// Minimum particle velocity
	F32		mBurstSpeedMax;					// Maximum particle velocity

	F32		mMaxAge;						// Maximum lifetime of this particle source

	LLUUID	mTargetUUID;					// Target UUID for the particle system

	F32		mStartAge;						// Age at which to start the particle system (for an update after the
											// particle system has started)


	//
	// These are actually particle properties, but can be mutated by the source,
	// so are stored here instead
	//
	LLVector3	mPartAccel;
	LLUUID		mPartImageID;

	//
	// The "template" partdata where we actually store the non-mutable particle parameters
	//
	LLPartData	mPartData;

protected:
	S32		mNumParticles;					// Number of particles generated
};

#endif // LL_LLPARTDATA_H