/**
 * @file lldrawable.h
 * @brief LLDrawable class definition
 *
 * $LicenseInfo:firstyear=2002&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_DRAWABLE_H
#define LL_DRAWABLE_H

#include <vector>
#include <map>

#include "v2math.h"
#include "v3math.h"
#include "v4math.h"
#include "m4math.h"
#include "v4coloru.h"
#include "llvector4a.h"
#include "llquaternion.h"
#include "xform.h"
#include "llviewerobject.h"
#include "llrect.h"
#include "llappviewer.h" // for gFrameTimeSeconds
#include "llvieweroctree.h"
#include <unordered_set>

class LLCamera;
class LLDrawPool;
class LLDrawable;
class LLFace;
class LLFacePool;
class LLSpatialGroup;
class LLSpatialBridge;
class LLSpatialPartition;
class LLVOVolume;
class LLViewerTexture;

// Can have multiple silhouettes for each object
const U32 SILHOUETTE_HIGHLIGHT = 0;

// All data for new renderer goes into this class.
LL_ALIGN_PREFIX(16)
class LLDrawable
    : public LLViewerOctreeEntryData
{
    LL_ALIGN_NEW;
public:
    typedef std::vector<LLFace*> face_list_t;

    LLDrawable(const LLDrawable& rhs)
        : LLViewerOctreeEntryData(rhs)
    {
        *this = rhs;
    }

    const LLDrawable& operator=(const LLDrawable& rhs)
    {
        LL_ERRS() << "Illegal operation!" << LL_ENDL;
        return *this;
    }

    static void initClass();

    LLDrawable(LLViewerObject *vobj, bool new_entry = false);

    void markDead();            // Mark this drawable as dead
    bool isDead() const         { return isState(DEAD); }
    bool isNew() const          { return !isState(BUILT); }
    bool isUnload() const       { return isState(FOR_UNLOAD); }

    bool isLight() const;

    virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, bool for_select = false);

    LLSpatialGroup* getSpatialGroup()const          {return (LLSpatialGroup*)getGroup();}
    LLViewerRegion* getRegion()               const { return mVObjp->getRegion(); }
    const LLTextureEntry* getTextureEntry(U8 which) const { return mVObjp->getTE(which); }
    LLPointer<LLViewerObject>& getVObj()                              { return mVObjp; }
    const LLViewerObject *getVObj() const                         { return mVObjp; }
    LLVOVolume* getVOVolume() const; // cast mVObjp tp LLVOVolume if OK

    const LLMatrix4&      getWorldMatrix() const        { return mXform.getWorldMatrix(); }
    const LLMatrix4&      getRenderMatrix() const;
    void                  setPosition(LLVector3 v) const { }
    const LLVector3&      getPosition() const           { return mXform.getPosition(); }
    const LLVector3&      getWorldPosition() const      { return mXform.getPositionW(); }
    const LLVector3       getPositionAgent() const;
    const LLVector3&      getScale() const              { return mCurrentScale; }
    void                  setScale(const LLVector3& scale) { mCurrentScale = scale; }
    const LLQuaternion&   getWorldRotation() const      { return mXform.getWorldRotation(); }
    const LLQuaternion&   getRotation() const           { return mXform.getRotation(); }
    F32                   getIntensity() const          { return llmin(mXform.getScale().mV[0], 4.f); }
    S32                   getLOD() const                { return mVObjp ? mVObjp->getLOD() : 1; }

    void  getMinMax(LLVector3& min,LLVector3& max) const { mXform.getMinMax(min,max); }
    LLXformMatrix*      getXform() { return &mXform; }

    U32                 getState()           const { return mState; }
    bool                isState   (U32 bits) const { return ((mState & bits) != 0); }
    void                setState  (U32 bits)       { mState |= bits; }
    void                clearState(U32 bits)       { mState &= ~bits; }

    bool                isAvatar()  const           { return mVObjp.notNull() && mVObjp->isAvatar(); }
    bool                isRoot() const              { return !mParent || mParent->isAvatar(); }
    LLDrawable*         getRoot();
    bool                isSpatialRoot() const       { return !mParent || mParent->isAvatar(); }
    virtual bool        isSpatialBridge() const     { return false; }
    virtual LLSpatialPartition* asPartition()       { return NULL; }
    LLDrawable*         getParent() const           { return mParent; }

    // must set parent through LLViewerObject::     ()
    //bool                setParent(LLDrawable *parent);

    inline LLFace*      getFace(const S32 i) const;
    inline S32          getNumFaces()        const;
    face_list_t& getFaces() { return mFaces; }
    const face_list_t& getFaces() const { return mFaces; }

    //void                removeFace(const S32 i); // SJB: Avoid using this, it's slow
    LLFace*             addFace(LLFacePool *poolp, LLViewerTexture *texturep);
    LLFace*             addFace(const LLTextureEntry *te, LLViewerTexture *texturep);
    LLFace*             addFace(const LLTextureEntry *te, LLViewerTexture *texturep, LLViewerTexture *normalp);
    LLFace*             addFace(const LLTextureEntry *te, LLViewerTexture *texturep, LLViewerTexture *normalp, LLViewerTexture *specularp);
    void                deleteFaces(S32 offset, S32 count);
    void                setNumFaces(const S32 numFaces, LLFacePool *poolp, LLViewerTexture *texturep);
    void                setNumFacesFast(const S32 numFaces, LLFacePool *poolp, LLViewerTexture *texturep);
    void                mergeFaces(LLDrawable* src);

    void init(bool new_entry);
    void unload();
    void destroy();

    void update();
    F32 updateXform(bool undamped);

    virtual void makeActive();
    /*virtual*/ void makeStatic(bool warning_enabled = true);

    bool isActive() const                           { return isState(ACTIVE); }
    bool isStatic() const                           { return !isActive(); }
    bool isAnimating() const;

    virtual bool updateMove();
    virtual void movePartition();

    void updateTexture();
    void updateMaterial();
    virtual void updateDistance(LLCamera& camera, bool force_update);
    bool updateGeometry();
    void updateFaceSize(S32 idx);

    void updateSpecialHoverCursor(bool enabled);

    virtual void shiftPos(const LLVector4a &shift_vector);

    S32 getGeneration() const                   { return mGeneration; }

    bool getLit() const                         { return !isState(UNLIT); }
    void setLit(bool lit)                       { lit ? clearState(UNLIT) : setState(UNLIT); }

    bool isVisible() const;
    bool isRecentlyVisible() const;

    virtual void cleanupReferences();

    void setGroup(LLViewerOctreeGroup* group);
    void setRadius(const F32 radius);
    F32 getRadius() const                       { return mRadius; }
    F32 getVisibilityRadius() const;

    void updateUVMinMax();  // Updates the cache of sun space bounding box.

    const LLVector3& getBounds(LLVector3& min, LLVector3& max) const;
    virtual void updateSpatialExtents();
    virtual void updateBinRadius();

    void setRenderType(S32 type)                { mRenderType = type; }
    bool isRenderType(S32 type)                 { return mRenderType == type; }
    S32  getRenderType()                        { return mRenderType; }

    // Debugging methods
    S32 findReferences(LLDrawable *drawablep); // Not const because of @#$! iterators...

    LLSpatialPartition* getSpatialPartition();

    void removeFromOctree();

    void setSpatialBridge(LLSpatialBridge* bridge) { mSpatialBridge = (LLDrawable*) bridge; }
    LLSpatialBridge* getSpatialBridge() { return (LLSpatialBridge*) (LLDrawable*) mSpatialBridge; }

    // Statics
    static void incrementVisible();
    static void cleanupDeadDrawables();

protected:
    ~LLDrawable() { destroy(); }
    void moveUpdatePipeline(bool moved);
    void updatePartition();
    bool updateMoveDamped();
    bool updateMoveUndamped();

public:
    friend class LLPipeline;
    friend class LLDrawPool;
    friend class LLSpatialBridge;

    typedef std::unordered_set<LLPointer<LLDrawable> > drawable_set_t;
    typedef std::set<LLPointer<LLDrawable> > ordered_drawable_set_t;
    typedef std::vector<LLPointer<LLDrawable> > drawable_vector_t;
    typedef std::list<LLPointer<LLDrawable> > drawable_list_t;
    typedef std::queue<LLPointer<LLDrawable> > drawable_queue_t;

    struct CompareDistanceGreater
    {
        bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
        {
            return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
        }
    };

    struct CompareDistanceGreaterVisibleFirst
    {
        bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
        {
            if (lhs->isVisible() && !rhs->isVisible())
            {
                return true; //visible things come first
            }
            else if (!lhs->isVisible() && rhs->isVisible())
            {
                return false; //rhs is visible, comes first
            }

            return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
        }
    };

    typedef enum e_drawable_flags
    {
        IN_REBUILD_Q    = 0x00000001,
        EARLY_MOVE      = 0x00000004,
        MOVE_UNDAMPED   = 0x00000008,
        ON_MOVE_LIST    = 0x00000010,
        UV              = 0x00000020,
        UNLIT           = 0x00000040,
        LIGHT           = 0x00000080,
        REBUILD_VOLUME  = 0x00000100,   //volume changed LOD or parameters, or vertex buffer changed
        REBUILD_TCOORD  = 0x00000200,   //texture coordinates changed
        REBUILD_COLOR   = 0x00000400,   //color changed
        REBUILD_POSITION= 0x00000800,   //vertex positions/normals changed
        REBUILD_GEOMETRY= REBUILD_POSITION|REBUILD_TCOORD|REBUILD_COLOR,
        REBUILD_MATERIAL= REBUILD_TCOORD|REBUILD_COLOR,
        REBUILD_ALL     = REBUILD_GEOMETRY|REBUILD_VOLUME,
        REBUILD_RIGGED  = 0x00001000,
        ON_SHIFT_LIST   = 0x00002000,
        ACTIVE          = 0x00004000,
        DEAD            = 0x00008000,
        INVISIBLE       = 0x00010000, // stay invisible until flag is cleared
        NEARBY_LIGHT    = 0x00020000, // In gPipeline.mNearbyLightSet
        BUILT           = 0x00040000,
        FORCE_INVISIBLE = 0x00080000, // stay invis until CLEAR_INVISIBLE is set (set of orphaned)
        HAS_ALPHA       = 0x00100000,
        RIGGED          = 0x00200000, //has a rigged face
        RIGGED_CHILD    = 0x00400000, //has a child with a rigged face
        PARTITION_MOVE  = 0x00800000,
        ANIMATED_CHILD  = 0x01000000,
        ACTIVE_CHILD    = 0x02000000,
        FOR_UNLOAD      = 0x04000000, //should be unload from memory
        MIRROR          = 0x08000000, // Used as a mirror, needs a hero probe position to be calculated.
    } EDrawableFlags;

public:
    LLXformMatrix       mXform;

    // vis data
    LLPointer<LLDrawable> mParent;

    F32             mDistanceWRTCamera;

    static F32 sCurPixelAngle; //current pixels per radian

private:
    U32             mState;
    S32             mRenderType;
    LLPointer<LLViewerObject> mVObjp;
    face_list_t     mFaces;
    LLPointer<LLDrawable> mSpatialBridge;

    F32             mRadius;
    S32             mGeneration;

    LLVector3       mCurrentScale;

    static U32 sNumZombieDrawables;
    static std::vector<LLPointer<LLDrawable> > sDeadList;
} LL_ALIGN_POSTFIX(16);


inline LLFace* LLDrawable::getFace(const S32 i) const
{
    //switch these asserts to LL_ERRS() -- davep
    //llassert((U32)i < mFaces.size());
    //llassert(mFaces[i]);

    if ((U32) i >= mFaces.size())
    {
        LL_WARNS() << "Invalid face index." << LL_ENDL;
        return NULL;
    }

    if (!mFaces[i])
    {
        LL_WARNS() << "Null face found." << LL_ENDL;
        return NULL;
    }

    return mFaces[i];
}


inline S32 LLDrawable::getNumFaces()const
{
    return (S32)mFaces.size();
}

#endif