/**
 * @file llvertexbuffer.h
 * @brief LLVertexBuffer wrapper for OpengGL vertex buffer objects
 *
 * $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_LLVERTEXBUFFER_H
#define LL_LLVERTEXBUFFER_H

#include "llgl.h"
#include "v2math.h"
#include "v3math.h"
#include "v4math.h"
#include "v4coloru.h"
#include "llstrider.h"
#include "llrender.h"
#include "lltrace.h"
#include <set>
#include <vector>
#include <list>

#define LL_MAX_VERTEX_ATTRIB_LOCATION 64

//============================================================================
// NOTES
// Threading:
//  All constructors should take an 'create' paramater which should only be
//  'true' when called from the main thread. Otherwise createGLBuffer() will
//  be called as soon as getVertexPointer(), etc is called (which MUST ONLY be
//  called from the main (i.e OpenGL) thread)


//============================================================================
// base class
class LLPrivateMemoryPool;
class LLVertexBuffer final : public LLRefCount
{
public:
    struct MappedRegion
    {
        U32 mStart;
        U32 mEnd;
    };

    LLVertexBuffer(const LLVertexBuffer& rhs)
    {
        *this = rhs;
    }

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

    static void initClass(LLWindow* window);
    static void cleanupClass();
    static void setupClientArrays(U32 data_mask);
    static void drawArrays(U32 mode, const std::vector<LLVector3>& pos);
    static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, U32 num_indices, const U16* indicesp);

    static void unbind(); //unbind any bound vertex buffer

    //get the size of a vertex with the given typemask
    static U32 calcVertexSize(const U32& typemask);

    //get the size of a buffer with the given typemask and vertex count
    //fill offsets with the offset of each vertex component array into the buffer
    // indexed by the following enum
    static U32 calcOffsets(const U32& typemask, U32* offsets, U32 num_vertices);

    //WARNING -- when updating these enums you MUST
    // 1 - update LLVertexBuffer::sTypeSize
    // 2 - update LLVertexBuffer::vb_type_name
    // 3 - add a strider accessor
    // 4 - modify LLVertexBuffer::setupVertexBuffer
    // 6 - modify LLViewerShaderMgr::mReservedAttribs

    // clang-format off
    enum AttributeType {                      // Shader attribute name, set in LLShaderMgr::initAttribsAndUniforms()
        TYPE_VERTEX = 0,        //  "position"
        TYPE_NORMAL,            //  "normal"
        TYPE_TEXCOORD0,         //  "texcoord0"
        TYPE_TEXCOORD1,         //  "texcoord1"
        TYPE_TEXCOORD2,         //  "texcoord2"
        TYPE_TEXCOORD3,         //  "texcoord3"
        TYPE_COLOR,             //  "diffuse_color"
        TYPE_EMISSIVE,          //  "emissive"
        TYPE_TANGENT,           //  "tangent"
        TYPE_WEIGHT,            //  "weight"
        TYPE_WEIGHT4,           //  "weight4"
        TYPE_CLOTHWEIGHT,       //  "clothing"
        TYPE_TEXTURE_INDEX,     //  "texture_index"
        TYPE_MAX,   // TYPE_MAX is the size/boundary marker for attributes that go in the vertex buffer
        TYPE_INDEX, // TYPE_INDEX is beyond _MAX because it lives in a separate (index) buffer
    };
    // clang-format on

    enum {
        MAP_VERTEX = (1<<TYPE_VERTEX),
        MAP_NORMAL = (1<<TYPE_NORMAL),
        MAP_TEXCOORD0 = (1<<TYPE_TEXCOORD0),
        MAP_TEXCOORD1 = (1<<TYPE_TEXCOORD1),
        MAP_TEXCOORD2 = (1<<TYPE_TEXCOORD2),
        MAP_TEXCOORD3 = (1<<TYPE_TEXCOORD3),
        MAP_COLOR = (1<<TYPE_COLOR),
        MAP_EMISSIVE = (1<<TYPE_EMISSIVE),
        MAP_TANGENT = (1<<TYPE_TANGENT),
        MAP_WEIGHT = (1<<TYPE_WEIGHT),
        MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
        MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
        MAP_TEXTURE_INDEX = (1<<TYPE_TEXTURE_INDEX),
    };

protected:
    friend class LLRender;

    ~LLVertexBuffer(); // use unref()

    void setupVertexBuffer();

    void    genBuffer(U32 size);
    void    genIndices(U32 size);
    bool    createGLBuffer(U32 size);
    bool    createGLIndices(U32 size);
    void    destroyGLBuffer();
    void    destroyGLIndices();
    bool    updateNumVerts(U32 nverts);
    bool    updateNumIndices(U32 nindices);

public:
    LLVertexBuffer(U32 typemask);

    // allocate buffer
    bool    allocateBuffer(U32 nverts, U32 nindices);

    // map for data access (see also getFooStrider below)
    U8*     mapVertexBuffer(AttributeType type, U32 index, S32 count = -1);
    U8*     mapIndexBuffer(U32 index, S32 count = -1);
    void    unmapBuffer();

    // set for rendering
    // assumes (and will assert on) the following:
    //      - this buffer has no pending unampBuffer call
    //      - a shader is currently bound
    //      - This buffer has sufficient attributes within it to satisfy the needs of the currently bound shader
    void    setBuffer();

    // Only call each getVertexPointer, etc, once before calling unmapBuffer()
    // call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer()
    // example:
    //   vb->getVertexBuffer(verts);
    //   vb->getNormalStrider(norms);
    //   setVertsNorms(verts, norms);
    //   vb->unmapBuffer();
    bool getVertexStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
    bool getVertexStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
    bool getIndexStrider(LLStrider<U16>& strider, U32 index=0, S32 count = -1);
    bool getTexCoord0Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
    bool getTexCoord1Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
    bool getTexCoord2Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
    bool getNormalStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
    bool getNormalStrider(LLStrider<LLVector4a>& strider, U32 index = 0, S32 count = -1);
    bool getTangentStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
    bool getTangentStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
    bool getColorStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
    bool getEmissiveStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
    bool getWeightStrider(LLStrider<F32>& strider, U32 index=0, S32 count = -1);
    bool getWeight4Strider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
    bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);

    void setPositionData(const LLVector4a* data);
    void setNormalData(const LLVector4a* data);
    void setTangentData(const LLVector4a* data);
    void setWeight4Data(const LLVector4a* data);
    void setTexCoordData(const LLVector2* data);
    void setColorData(const LLColor4U* data);
    void setIndexData(const U16* data);
    void setIndexData(const U32* data);

    U32 getNumVerts() const                 { return mNumVerts; }
    U32 getNumIndices() const               { return mNumIndices; }

    U32 getTypeMask() const                 { return mTypeMask; }
    bool hasDataType(AttributeType type) const      { return ((1 << type) & getTypeMask()); }
    U32 getSize() const                     { return mSize; }
    U32 getIndicesSize() const              { return mIndicesSize; }
    U8* getMappedData() const               { return mMappedData; }
    U8* getMappedIndices() const            { return mMappedIndexData; }
    U32 getOffset(AttributeType type) const         { return mOffsets[type]; }

    // these functions assume (and assert on) the current VBO being bound
    // Detailed error checking can be enabled by setting gDebugGL to true
    void draw(U32 mode, U32 count, U32 indices_offset) const;
    void drawArrays(U32 mode, U32 offset, U32 count) const;
    void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;

    //for debugging, validate data in given range is valid
    bool validateRange(U32 start, U32 end, U32 count, U32 offset) const;

    #ifdef LL_PROFILER_ENABLE_RENDER_DOC
    void setLabel(const char* label);
    #endif


protected:
    U32     mGLBuffer = 0;      // GL VBO handle
    U32     mGLIndices = 0;     // GL IBO handle
    U32     mNumVerts = 0;      // Number of vertices allocated
    U32     mNumIndices = 0;    // Number of indices allocated
    U32     mIndicesType = GL_UNSIGNED_SHORT; // type of indices in index buffer
    U32     mIndicesStride = 2;     // size of each index in bytes
    U32     mOffsets[TYPE_MAX]; // byte offsets into mMappedData of each attribute

    U8* mMappedData = nullptr;  // pointer to currently mapped data (NULL if unmapped)
    U8* mMappedIndexData = nullptr; // pointer to currently mapped indices (NULL if unmapped)

    U32     mTypeMask = 0;      // bitmask of present vertex attributes

    U32     mSize = 0;          // size in bytes of mMappedData
    U32     mIndicesSize = 0;   // size in bytes of mMappedIndexData

    std::vector<MappedRegion> mMappedVertexRegions;  // list of mMappedData byte ranges that must be sent to GL
    std::vector<MappedRegion> mMappedIndexRegions;   // list of mMappedIndexData byte ranges that must be sent to GL

private:
    // DEPRECATED
    // These function signatures are deprecated, but for some reason
    // there are classes in an external package that depend on LLVertexBuffer

    // TODO: move these classes into viewer repository
    friend class LLNavShapeVBOManager;
    friend class LLNavMeshVBOManager;

    LLVertexBuffer(U32 typemask, U32 usage)
        : LLVertexBuffer(typemask)
    {}

    bool    allocateBuffer(S32 nverts, S32 nindices, BOOL create) { return allocateBuffer(nverts, nindices); }

public:

    static U64 getBytesAllocated();
    static const U32 sTypeSize[TYPE_MAX];
    static const U32 sGLMode[LLRender::NUM_MODES];
    static U32 sGLRenderBuffer;
    static U32 sGLRenderIndices;
    static U32 sLastMask;
    static U32 sVertexCount;
};

#ifdef LL_PROFILER_ENABLE_RENDER_DOC
#define LL_LABEL_VERTEX_BUFFER(buf, name) buf->setLabel(name)
#else
#define LL_LABEL_VERTEX_BUFFER(buf, name)
#endif


#endif // LL_LLVERTEXBUFFER_H