/**
 * @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