/*
 *  Vertex.h
 *
 *  Created by Thant on 7/16/2010.
 *  Copyright 2010 TeleNav Inc. All rights reserved.
 *
 */

#ifndef Foundation_Math_Vertex_INCLUDED
#define Foundation_Math_Vertex_INCLUDED

#include <foundation/TnTypes.h>

namespace Tn {

namespace math {

/*
  template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD>
  struct Vertex;

  Options:
    COLOR         0, 4          // ubyte r, g, b, a
    TEXCOORD      0, 2          // short s, t
    NORMAL        0, 3          // short nx, ny, nz
    COORD         2, 3          // float x, y, z
    OFFSET        true,false    // short dx, dy, dz representing an offset vector
    SHIFT         true,false    // short sx, sy, sz representing a shift vector (only used with SHIFT_ENABLED==true)
*/

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
struct Vertex;

// Color

template <int COLOR>
struct ColorB;

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t colorBOffset();
    
template <>
struct ColorB<0> {
};

template <>
struct ColorB<4> {
   uint8_t r;
   uint8_t g;
   uint8_t b;
   uint8_t a;
};


// TexCoord

template <int TEXCOORD, typename T>
struct TexCoord;

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t texCoordOffset();

template <>
struct TexCoord<0, float> {
};
    
template <>
struct TexCoord<0, int16_t> {
};


template <>
struct TexCoord<2, int16_t> {
   int16_t s;
   int16_t t;
};

template <>
struct TexCoord<2, float> {
    float s;
    float t;
};
    
typedef TexCoord<0,float>   NullTexFloat;
typedef TexCoord<0,int16_t> NullTexShort;
typedef TexCoord<2,float>   TexFloat2D;
typedef TexCoord<2,int16_t> TexShort2D;

// Normal

template <int NORMAL>
struct Normal;

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t normalOffset();

template <>
struct Normal<0> {
};

template <>
struct Normal<3> {
   int16_t nx;
   int16_t ny;
   int16_t nz;
};

// Coordinate

template <int COORD>
struct Coord;

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t coordOffset();

template <>
struct Coord<0> {
};
    
template <>
struct Coord<1> {
    float x;
};

template <>
struct Coord<2> {
   float x;
   float y;
};

template <>
struct Coord<3> {
   float x;
   float y;
   float z;
};
    
// offset vector (for stretching line widths)
    
template <bool ENABLED>
struct OffsetVector;
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t offsetVectorOffset();
    
template <>
struct OffsetVector<false> {
};

template <>
struct OffsetVector<true> {
    float dx;
    float dy;
    //float dz; //not using third coord for now
};
    
// shift vector (literally just for traffic) will always be used in conjunction with OffsetVector
    
template <bool ENABLED>
struct ShiftVector;
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t shiftVectorOffset();

template <>
struct ShiftVector<false> {
};

template <>
struct ShiftVector<true> {
    float sx;
    float sy;
    //int16_t sz; //not using third coord for now
};


    
// Vertex 
// TODO: Get rid of this. replace whatever is using it with something parameterized on COLOR, TEXTURE_TYPE, NORMAL, COORD.
    
enum eVertexType
{
    eVertexType_1D,
    eVertexType_2D,
    eVertexType_3D,
    eVertexType_TEXTURED_2D,
    eVertexType_TEXTURED_3D,
    eVertexType_TEXTURED_3D_FLOAT,
    eVertexType_COLORED_2D,
    eVertexType_COLORED_3D,
    eVertexType_TEXTURED_COLORED_2D,
    eVertexType_TEXTURED_COLORED_3D,
    
    eVertexType_1D_OFFSET,
    eVertexType_2D_OFFSET,
    eVertexType_3D_OFFSET,
    
    eVertexType_TEXTURED_2D_OFFSET,
    eVertexType_TEXTURED_2D_OFFSET_FLOAT,
    eVertexType_TEXTURED_3D_OFFSET,
    eVertexType_TEXTURED_3D_OFFSET_FLOAT,
    eVertexType_COLORED_2D_OFFSET,
    eVertexType_COLORED_3D_OFFSET,
    eVertexType_TEXTURED_COLORED_2D_OFFSET,
    eVertexType_TEXTURED_COLORED_2D_OFFSET_FLOAT,
    eVertexType_TEXTURED_COLORED_3D_OFFSET,
    eVertexType_TEXTURED_COLORED_3D_OFFSET_FLOAT,
    
    eVertexType_TEXTURED_3D_OFFSET_SHIFTED,
    eVertexType_TEXTURED_3D_OFFSET_SHIFTED_FLOAT
};
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
struct Vertex : ColorB<COLOR>, TEXTURE_TYPE, Normal<NORMAL>, Coord<COORD>, OffsetVector<OFFSET_ENABLED>, ShiftVector<SHIFT_ENABLED>
{
private:
    static const Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> prototype;
public:

    //All initialized below by static "offset" functions
    static uint32_t const color_offset;
    static uint32_t const tex_coord_offset;
    static uint32_t const normal_offset;
    static uint32_t const coord_offset;
    static uint32_t const offset_vec_offset;
    static uint32_t const shift_vec_offset;
    
    //Note: on 64-bit platforms, size_of returns an unsigned long.  We never expect the Vertex class to get that large...
    static uint32_t const size;
    
    static eVertexType Type();
};


template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t colorBOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    ColorB<COLOR> const & color = v;
    uint64_t offset = uint64_t(&color) - uint64_t(&v);
    return uint32_t(offset);
}


template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::color_offset = colorBOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();


template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t texCoordOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    TEXTURE_TYPE const & texCoord = v;
    uint64_t offset = uint64_t(&texCoord) - uint64_t(&v);
    return uint32_t(offset);
}

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED, SHIFT_ENABLED>::tex_coord_offset = texCoordOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();


template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t normalOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    Normal<NORMAL> const & normal = v;
    uint64_t offset = uint64_t(&normal) - uint64_t(&v);
    return uint32_t(offset);
}


template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::normal_offset = normalOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();



template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t coordOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    Coord<COORD> const & coord = v;
    uint64_t offset = uint64_t(&coord) - uint64_t(&v);
    return uint32_t(offset);
}

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::coord_offset = coordOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t offsetVectorOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    OffsetVector<OFFSET_ENABLED> const & offsetVector = v;
    uint64_t offset = uint64_t(&offsetVector) - uint64_t(&v);
    return uint32_t(offset);
}

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::offset_vec_offset = offsetVectorOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t shiftVectorOffset()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    ShiftVector<SHIFT_ENABLED> const & shiftVector = v;
    uint64_t offset = uint64_t(&shiftVector) - uint64_t(&v);
    return uint32_t(offset);
}

template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::shift_vec_offset = shiftVectorOffset<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t sizeOfVertex()
{
    Vertex<COLOR, TEXTURE_TYPE, NORMAL, COORD, OFFSET_ENABLED, SHIFT_ENABLED> v;
    return static_cast<uint32_t>(sizeof(v));
}
    
template <int COLOR, typename TEXTURE_TYPE, int NORMAL, int COORD, bool OFFSET_ENABLED, bool SHIFT_ENABLED>
uint32_t const Vertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>::size = sizeOfVertex<COLOR,TEXTURE_TYPE,NORMAL,COORD,OFFSET_ENABLED,SHIFT_ENABLED>();

    

} // namespace math

} // namespace Tn

#endif
