/*
 Copyright (C) 2010-2017 Kristian Duske
 
 This file is part of TrenchBroom.
 
 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 TrenchBroom 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 General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef TrenchBroom_Vertex_h
#define TrenchBroom_Vertex_h

#include <cstddef>
#include <vector>

namespace TrenchBroom {
    namespace Renderer {
        template <typename A1>
        class VertexSpec1;
        template <typename A1, typename A2>
        class VertexSpec2;
        template <typename A1, typename A2, typename A3>
        class VertexSpec3;
        template <typename A1, typename A2, typename A3, typename A4>
        class VertexSpec4;
        template <typename A1, typename A2, typename A3, typename A4, typename A5>
        class VertexSpec5;
        
        struct GetVertexComponent1 {
            template <typename V> const typename V::Spec::A1::ElementType& operator()(const V& v) const { return v.v1; }
        };
        
        struct GetVertexComponent2 {
            template <typename V> const typename V::Spec::A2::ElementType& operator()(const V& v) const { return v.v2; }
        };
        
        struct GetVertexComponent3 {
            template <typename V> const typename V::Spec::A3::ElementType& operator()(const V& v) const { return v.v3; }
        };
        
        struct GetVertexComponent4 {
            template <typename V> const typename V::Spec::A4::ElementType& operator()(const V& v) const { return v.v4; }
        };
        
        struct GetVertexComponent5 {
            template <typename V> const typename V::Spec::A5::ElementType& operator()(const V& v) const { return v.v5; }
        };
        
        template <typename A1>
        class Vertex1 {
        public:
            using Spec = VertexSpec1<A1>;
            using List = std::vector<Vertex1<A1> >;
            
            typename A1::ElementType v1;
            
            Vertex1() {}

            Vertex1(const typename A1::ElementType& i_v1) :
            v1(i_v1) {}
            
            bool operator==(const Vertex1<A1>& other) const {
                return v1 == other.v1;
            }

            template <typename I1>
            static List toList(I1 cur1,
                                  const size_t count,
                                  const size_t offset1 = 0, const size_t stride1 = 1) {
                List result;
                result.reserve(count);
                if (count > 0) {
                    std::advance(cur1, static_cast<typename I1::difference_type>(offset1));
                    result.push_back(Vertex1(*cur1));
                    for (size_t i = 1; i < count; ++i) {
                        std::advance(cur1, static_cast<typename I1::difference_type>(stride1));
                        result.push_back(Vertex1(*cur1));
                    }
                }
                return result;
            }
        };
        
        template <typename A1, typename A2>
        class Vertex2 {
        public:
            using Spec = VertexSpec2<A1, A2>;
            using List = std::vector<Vertex2<A1, A2> >;
            
            typename A1::ElementType v1;
            typename A2::ElementType v2;
            
            Vertex2() {}

            Vertex2(const typename A1::ElementType& i_v1,
                    const typename A2::ElementType& i_v2) :
            v1(i_v1),
            v2(i_v2) {}
            
            bool operator==(const Vertex2<A1, A2>& other) const {
                return (v1 == other.v1 &&
                        v2 == other.v2);
            }
            
            template <typename I1, typename I2>
            static List toList(I1 cur1, I2 cur2,
                                  const size_t count,
                                  const size_t offset1 = 0, const size_t stride1 = 1,
                                  const size_t offset2 = 0, const size_t stride2 = 1) {
                List result;
                result.reserve(count);
                if (count > 0) {
                    std::advance(cur1, static_cast<typename I1::difference_type>(offset1));
                    std::advance(cur2, static_cast<typename I2::difference_type>(offset2));
                    result.push_back(Vertex2(*cur1, *cur2));
                    for (size_t i = 1; i < count; ++i) {
                        std::advance(cur1, static_cast<typename I1::difference_type>(stride1));
                        std::advance(cur2, static_cast<typename I2::difference_type>(stride2));
                        result.push_back(Vertex2(*cur1, *cur2));
                    }
                }
                return result;
            }
        };
        
        template <typename A1, typename A2, typename A3>
        class Vertex3 {
        public:
            using Spec = VertexSpec3<A1, A2, A3>;
            using List = std::vector<Vertex3<A1, A2, A3> >;
            
            typename A1::ElementType v1;
            typename A2::ElementType v2;
            typename A3::ElementType v3;
            
            Vertex3() {}
            
            Vertex3(const typename A1::ElementType& i_v1,
                    const typename A2::ElementType& i_v2,
                    const typename A3::ElementType& i_v3) :
            v1(i_v1),
            v2(i_v2),
            v3(i_v3) {}
            
            bool operator==(const Vertex3<A1, A2, A3>& other) const {
                return (v1 == other.v1 &&
                        v2 == other.v2 &&
                        v3 == other.v3);
            }
            
            template <typename I1, typename I2, typename I3>
            static List toList(I1 cur1, I2 cur2, I3 cur3,
                                  const size_t count,
                                  const size_t offset1 = 0, const size_t stride1 = 1,
                                  const size_t offset2 = 0, const size_t stride2 = 1,
                                  const size_t offset3 = 0, const size_t stride3 = 1) {
                List result;
                result.reserve(count);
                if (count > 0) {
                    std::advance(cur1, static_cast<typename I1::difference_type>(offset1));
                    std::advance(cur2, static_cast<typename I2::difference_type>(offset2));
                    std::advance(cur3, static_cast<typename I3::difference_type>(offset3));
                    result.push_back(Vertex3(*cur1, *cur2, *cur3));
                    for (size_t i = 1; i < count; ++i) {
                        std::advance(cur1, static_cast<typename I1::difference_type>(stride1));
                        std::advance(cur2, static_cast<typename I2::difference_type>(stride2));
                        std::advance(cur3, static_cast<typename I3::difference_type>(stride3));
                        result.push_back(Vertex3(*cur1, *cur2, *cur3));
                    }
                }
                return result;
            }
        };
        
        template <typename A1, typename A2, typename A3, typename A4>
        class Vertex4 {
        public:
            using Spec = VertexSpec4<A1, A2, A3, A4>;
            using List = std::vector<Vertex4<A1, A2, A3, A4> >;
            
            typename A1::ElementType v1;
            typename A2::ElementType v2;
            typename A3::ElementType v3;
            typename A4::ElementType v4;
            
            Vertex4() {}
            
            Vertex4(const typename A1::ElementType& i_v1,
                    const typename A2::ElementType& i_v2,
                    const typename A3::ElementType& i_v3,
                    const typename A4::ElementType& i_v4) :
            v1(i_v1),
            v2(i_v2),
            v3(i_v3),
            v4(i_v4) {}
            
            bool operator==(const Vertex4<A1, A2, A3, A4>& other) const {
                return (v1 == other.v1 &&
                        v2 == other.v2 &&
                        v3 == other.v3 &&
                        v4 == other.v4);
            }
            
            template <typename I1, typename I2, typename I3, typename I4>
            static List toList(I1 cur1, I2 cur2, I3 cur3, I4 cur4,
                                  const size_t count,
                                  const size_t offset1 = 0, const size_t stride1 = 1,
                                  const size_t offset2 = 0, const size_t stride2 = 1,
                                  const size_t offset3 = 0, const size_t stride3 = 1,
                                  const size_t offset4 = 0, const size_t stride4 = 1) {
                List result;
                result.reserve(count);
                if (count > 0) {
                    std::advance(cur1, static_cast<typename I1::difference_type>(offset1));
                    std::advance(cur2, static_cast<typename I2::difference_type>(offset2));
                    std::advance(cur3, static_cast<typename I3::difference_type>(offset3));
                    std::advance(cur4, static_cast<typename I4::difference_type>(offset4));
                    result.push_back(Vertex4(*cur1, *cur2, *cur3, *cur4));
                    for (size_t i = 1; i < count; ++i) {
                        std::advance(cur1, static_cast<typename I1::difference_type>(stride1));
                        std::advance(cur2, static_cast<typename I2::difference_type>(stride2));
                        std::advance(cur3, static_cast<typename I3::difference_type>(stride3));
                        std::advance(cur4, static_cast<typename I4::difference_type>(stride4));
                        result.push_back(Vertex4(*cur1, *cur2, *cur3, *cur4));
                    }
                }
                return result;
            }
        };

        template <typename A1, typename A2, typename A3, typename A4, typename A5>
        class Vertex5 {
        public:
            using Spec = VertexSpec5<A1, A2, A3, A4, A5>;
            using List = std::vector<Vertex5<A1, A2, A3, A4, A5> >;

            typename A1::ElementType v1;
            typename A2::ElementType v2;
            typename A3::ElementType v3;
            typename A4::ElementType v4;
            typename A5::ElementType v5;

            Vertex5() {}
            
            Vertex5(const typename A1::ElementType& i_v1,
                    const typename A2::ElementType& i_v2,
                    const typename A3::ElementType& i_v3,
                    const typename A4::ElementType& i_v4,
                    const typename A5::ElementType& i_v5) :
            v1(i_v1),
            v2(i_v2),
            v3(i_v3),
            v4(i_v4),
            v5(i_v5) {}
            
            bool operator==(const Vertex5<A1, A2, A3, A4, A5>& other) const {
                return (v1 == other.v1 &&
                        v2 == other.v2 &&
                        v3 == other.v3 &&
                        v4 == other.v4 &&
                        v5 == other.v5);
            }
            
            template <typename I1, typename I2, typename I3, typename I4, typename I5>
            static List toList(I1 cur1, I2 cur2, I3 cur3, I4 cur4, I5 cur5,
                                  const size_t count,
                                  const size_t offset1 = 0, const size_t stride1 = 1,
                                  const size_t offset2 = 0, const size_t stride2 = 1,
                                  const size_t offset3 = 0, const size_t stride3 = 1,
                                  const size_t offset4 = 0, const size_t stride4 = 1,
                                  const size_t offset5 = 0, const size_t stride5 = 1) {
                List result;
                result.reserve(count);
                if (count > 0) {
                    std::advance(cur1, static_cast<typename I1::difference_type>(offset1));
                    std::advance(cur2, static_cast<typename I2::difference_type>(offset2));
                    std::advance(cur3, static_cast<typename I3::difference_type>(offset3));
                    std::advance(cur4, static_cast<typename I4::difference_type>(offset4));
                    std::advance(cur5, static_cast<typename I5::difference_type>(offset5));
                    result.push_back(Vertex5(*cur1, *cur2, *cur3, *cur4, *cur5));
                    for (size_t i = 1; i < count; ++i) {
                        std::advance(cur1, static_cast<typename I1::difference_type>(stride1));
                        std::advance(cur2, static_cast<typename I2::difference_type>(stride2));
                        std::advance(cur3, static_cast<typename I3::difference_type>(stride3));
                        std::advance(cur4, static_cast<typename I4::difference_type>(stride4));
                        std::advance(cur5, static_cast<typename I5::difference_type>(stride5));
                        result.push_back(Vertex5(*cur1, *cur2, *cur3, *cur4, *cur5));
                    }
                }
                return result;
            }
        };
    }
}

#endif
