#ifndef _RRPRIMITIVEASSEMBLER_HPP
#define _RRPRIMITIVEASSEMBLER_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Reference Renderer
 * -----------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Primitive assembler
 *//*--------------------------------------------------------------------*/

#include "rrDefs.hpp"
#include "rrVertexPacket.hpp"

namespace rr
{
namespace pa
{

struct Triangle
{
    enum
    {
        NUM_VERTICES = 3
    };

    Triangle(void) : v0(DE_NULL), v1(DE_NULL), v2(DE_NULL), provokingIndex(-1)
    {
    }

    Triangle(VertexPacket *v0_, VertexPacket *v1_, VertexPacket *v2_, int provokingIndex_)
        : v0(v0_)
        , v1(v1_)
        , v2(v2_)
        , provokingIndex(provokingIndex_)
    {
    }

    VertexPacket *getProvokingVertex(void)
    {
        switch (provokingIndex)
        {
        case 0:
            return v0;
        case 1:
            return v1;
        case 2:
            return v2;
        default:
            DE_ASSERT(false);
            return DE_NULL;
        }
    }

    VertexPacket *v0;
    VertexPacket *v1;
    VertexPacket *v2;

    int provokingIndex;
} DE_WARN_UNUSED_TYPE;

struct Triangles
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (2);

        for (size_t ndx = 0; ndx + 2 < numVertices; ndx += 3)
            *(outputIterator++) = Triangle(vertices[ndx], vertices[ndx + 1], vertices[ndx + 2], provokingOffset);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return vertices / 3;
    }
} DE_WARN_UNUSED_TYPE;

struct TriangleStrip
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        if (numVertices < 3)
        {
        }
        else
        {
            VertexPacket *vert0 = vertices[0];
            VertexPacket *vert1 = vertices[1];
            size_t ndx          = 2;

            for (;;)
            {
                {
                    if (ndx >= numVertices)
                        break;

                    *(outputIterator++) = Triangle(vert0, vert1, vertices[ndx],
                                                   (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (2));
                    vert0               = vertices[ndx];

                    ndx++;
                }

                {
                    if (ndx >= numVertices)
                        break;

                    *(outputIterator++) = Triangle(vert0, vert1, vertices[ndx],
                                                   (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (1) : (2));
                    vert1               = vertices[ndx];

                    ndx++;
                }
            }
        }
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 3) ? (0) : (vertices - 2);
    }
} DE_WARN_UNUSED_TYPE;

struct TriangleFan
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        if (numVertices == 0)
        {
        }
        else
        {
            const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (1) : (2);
            VertexPacket *const first = vertices[0];

            for (size_t ndx = 1; ndx + 1 < numVertices; ++ndx)
                *(outputIterator++) = Triangle(first, vertices[ndx], vertices[ndx + 1], provokingOffset);
        }
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 3) ? (0) : (vertices - 2);
    }
} DE_WARN_UNUSED_TYPE;

struct Line
{
    enum
    {
        NUM_VERTICES = 2
    };

    Line(void) : v0(DE_NULL), v1(DE_NULL), provokingIndex(-1)
    {
    }

    Line(VertexPacket *v0_, VertexPacket *v1_, int provokingIndex_) : v0(v0_), v1(v1_), provokingIndex(provokingIndex_)
    {
    }

    VertexPacket *getProvokingVertex(void)
    {
        switch (provokingIndex)
        {
        case 0:
            return v0;
        case 1:
            return v1;
        default:
            DE_ASSERT(false);
            return DE_NULL;
        }
    }

    VertexPacket *v0;
    VertexPacket *v1;

    int provokingIndex;
} DE_WARN_UNUSED_TYPE;

struct Lines
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (1);

        for (size_t ndx = 0; ndx + 1 < numVertices; ndx += 2)
            *(outputIterator++) = Line(vertices[ndx], vertices[ndx + 1], provokingOffset);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return vertices / 2;
    }
} DE_WARN_UNUSED_TYPE;

struct LineStrip
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        if (numVertices == 0)
        {
        }
        else
        {
            VertexPacket *prev = vertices[0];

            for (size_t ndx = 1; ndx < numVertices; ++ndx)
            {
                *(outputIterator++) =
                    Line(prev, vertices[ndx], (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (1));
                prev = vertices[ndx];
            }
        }
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 2) ? (0) : (vertices - 1);
    }
} DE_WARN_UNUSED_TYPE;

struct LineLoop
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        if (numVertices < 2)
        {
        }
        else
        {
            VertexPacket *prev = vertices[0];

            for (size_t ndx = 1; ndx < numVertices; ++ndx)
            {
                *(outputIterator++) =
                    Line(prev, vertices[ndx], (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (1));
                prev = vertices[ndx];
            }

            *(outputIterator++) =
                Line(prev, vertices[0], (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (1));
        }
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 2) ? (0) : (vertices);
    }
} DE_WARN_UNUSED_TYPE;

struct Point
{
    enum
    {
        NUM_VERTICES = 1
    };

    Point(void) : v0(DE_NULL)
    {
    }

    Point(VertexPacket *v0_) : v0(v0_)
    {
    }

    VertexPacket *v0;
} DE_WARN_UNUSED_TYPE;

struct Points
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        DE_UNREF(provokingConvention);

        for (size_t ndx = 0; ndx < numVertices; ++ndx)
            *(outputIterator++) = Point(vertices[ndx]);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices);
    }
} DE_WARN_UNUSED_TYPE;

struct LineAdjacency
{
    enum
    {
        NUM_VERTICES = 4
    };

    LineAdjacency(void) : v0(DE_NULL), v1(DE_NULL), v2(DE_NULL), v3(DE_NULL), provokingIndex(-1)
    {
    }

    LineAdjacency(VertexPacket *v0_, VertexPacket *v1_, VertexPacket *v2_, VertexPacket *v3_, int provokingIndex_)
        : v0(v0_)
        , v1(v1_)
        , v2(v2_)
        , v3(v3_)
        , provokingIndex(provokingIndex_)
    {
    }

    VertexPacket *getProvokingVertex(void)
    {
        switch (provokingIndex)
        {
        case 1:
            return v1;
        case 2:
            return v2;
        default:
            DE_ASSERT(false);
            return DE_NULL;
        }
    }

    VertexPacket *v0;
    VertexPacket *v1;
    VertexPacket *v2;
    VertexPacket *v3;

    int provokingIndex;
} DE_WARN_UNUSED_TYPE;

struct LinesAdjacency
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (1) : (2);

        for (size_t ndx = 0; ndx + 3 < numVertices; ndx += 4)
            *(outputIterator++) =
                LineAdjacency(vertices[ndx], vertices[ndx + 1], vertices[ndx + 2], vertices[ndx + 3], provokingOffset);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return vertices / 4;
    }
} DE_WARN_UNUSED_TYPE;

struct LineStripAdjacency
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (1) : (2);

        for (size_t ndx = 0; ndx + 3 < numVertices; ++ndx)
            *(outputIterator++) =
                LineAdjacency(vertices[ndx], vertices[ndx + 1], vertices[ndx + 2], vertices[ndx + 3], provokingOffset);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 4) ? (0) : (vertices - 3);
    }
} DE_WARN_UNUSED_TYPE;

struct TriangleAdjacency
{
    enum
    {
        NUM_VERTICES = 6
    };

    TriangleAdjacency(void)
        : v0(DE_NULL)
        , v1(DE_NULL)
        , v2(DE_NULL)
        , v3(DE_NULL)
        , v4(DE_NULL)
        , v5(DE_NULL)
        , provokingIndex(-1)
    {
    }

    TriangleAdjacency(VertexPacket *v0_, VertexPacket *v1_, VertexPacket *v2_, VertexPacket *v3_, VertexPacket *v4_,
                      VertexPacket *v5_, int provokingIndex_)
        : v0(v0_)
        , v1(v1_)
        , v2(v2_)
        , v3(v3_)
        , v4(v4_)
        , v5(v5_)
        , provokingIndex(provokingIndex_)
    {
    }

    VertexPacket *getProvokingVertex(void)
    {
        switch (provokingIndex)
        {
        case 0:
            return v0;
        case 2:
            return v2;
        case 4:
            return v4;
        default:
            DE_ASSERT(false);
            return DE_NULL;
        }
    }

    VertexPacket *v0;
    VertexPacket *v1; //!< adjacent
    VertexPacket *v2;
    VertexPacket *v3; //!< adjacent
    VertexPacket *v4;
    VertexPacket *v5; //!< adjacent

    int provokingIndex;
} DE_WARN_UNUSED_TYPE;

struct TrianglesAdjacency
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        const int provokingOffset = (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (4);

        for (size_t ndx = 0; ndx + 5 < numVertices; ndx += 6)
            *(outputIterator++) =
                TriangleAdjacency(vertices[ndx], vertices[ndx + 1], vertices[ndx + 2], vertices[ndx + 3],
                                  vertices[ndx + 4], vertices[ndx + 5], provokingOffset);
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return vertices / 6;
    }
} DE_WARN_UNUSED_TYPE;

struct TriangleStripAdjacency
{
    template <typename Iterator>
    static void exec(Iterator outputIterator, VertexPacket *const *vertices, size_t numVertices,
                     rr::ProvokingVertex provokingConvention)
    {
        if (numVertices < 6)
        {
        }
        else if (numVertices < 8)
        {
            *(outputIterator++) =
                TriangleAdjacency(vertices[0], vertices[1], vertices[2], vertices[5], vertices[4], vertices[3],
                                  (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (4));
        }
        else
        {
            const size_t primitiveCount = getPrimitiveCount(numVertices);
            size_t i;

            // first
            *(outputIterator++) =
                TriangleAdjacency(vertices[0], vertices[1], vertices[2], vertices[6], vertices[4], vertices[3],
                                  (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (4));

            // middle
            for (i = 1; i + 1 < primitiveCount; ++i)
            {
                // odd
                if (i % 2 == 1)
                {
                    *(outputIterator++) =
                        TriangleAdjacency(vertices[2 * i + 2], vertices[2 * i - 2], vertices[2 * i + 0],
                                          vertices[2 * i + 3], vertices[2 * i + 4], vertices[2 * i + 6],
                                          (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (2) : (4));
                }
                // even
                else
                {
                    *(outputIterator++) =
                        TriangleAdjacency(vertices[2 * i + 0], vertices[2 * i - 2], vertices[2 * i + 2],
                                          vertices[2 * i + 6], vertices[2 * i + 4], vertices[2 * i + 3],
                                          (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (4));
                }
            }

            // last

            // odd
            if (i % 2 == 1)
                *(outputIterator++) = TriangleAdjacency(vertices[2 * i + 2], vertices[2 * i - 2], vertices[2 * i + 0],
                                                        vertices[2 * i + 3], vertices[2 * i + 4], vertices[2 * i + 5],
                                                        (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (2) : (4));
            // even
            else
                *(outputIterator++) = TriangleAdjacency(vertices[2 * i + 0], vertices[2 * i - 2], vertices[2 * i + 2],
                                                        vertices[2 * i + 5], vertices[2 * i + 4], vertices[2 * i + 3],
                                                        (provokingConvention == rr::PROVOKINGVERTEX_FIRST) ? (0) : (4));
        }
    }

    static size_t getPrimitiveCount(size_t vertices)
    {
        return (vertices < 6) ? 0 : ((vertices - 4) / 2);
    }
} DE_WARN_UNUSED_TYPE;

} // namespace pa
} // namespace rr

#endif // _RRPRIMITIVEASSEMBLER_HPP
