#ifndef _ASSIGNMENT2_H_
#define _ASSIGNMENT2_H_

#include <vector>

#define TRANSFORM_NONE      0 
#define TRANSFORM_ROTATE    1
#define TRANSFORM_SCALE     2 
#define TRANSFORM_TRANSLATE 3

#define OBJ_WIREFRAME       0
#define OBJ_SOLID           1
#define OBJ_EDGE            2 
#define OBJ_BBOX            3

#define IMG_WIDTH  256
#define IMG_HEIGHT 256

#define X 0
#define Y 1
#define Z 2
#define W 3

#define R 0
#define G 1
#define B 2

#define S 0
#define T 1

#define PI 3.1415926535

struct Vector
{
  private:

    float data[4];

  public:
  
    Vector(void)
    {
        data[X] = 
            data[Y] =
            data[Z] = 
            data[W] = 0.f;
    }

    Vector(float x, float y, float z, float w)
    {
        data[X] = x;
        data[Y] = y;
        data[Z] = z;
        data[W] = w;
        
    }

    float &operator [](unsigned int idx)
    {
        return data[idx];
    }  
};

struct Point
{
  private:

    float data[4];

  public:
  
    Point(void)
    {
        data[X] = 
            data[Y] =
            data[Z] = 0.f; 
        data[W] = 1.f;
    }

    Point(float x, float y, float z, float w)
    {
        data[X] = x;
        data[Y] = y;
        data[Z] = z;
        data[W] = w;
    }

    float &operator [](unsigned int idx)
    {
        return data[idx];
    }  

    const float *gldata(void)
    {
        return data;
    }
};

struct Color
{
  private:

    float data[3];

  public:
  
    Color(void)
    {
        data[R] = 
            data[G] =
            data[B] = 0.f; 
    }

    Color(float r, float g, float b)
    {
        data[R] = r;
        data[G] = g;
        data[B] = b;
    }

    float &operator [](unsigned int idx)
    {
        return data[idx];
    }  
};

typedef float TexCoord[2];
typedef float GLColor4[4];

typedef float Matrix[16];
typedef float iMatrix[32];

struct Vertex
{
    // Vertex Coordinate
    Point    v;

    // Vertex Normal
    Vector   n;

    // Vertex TextureCorrdinate
    TexCoord t;

    // Vertex Color 
    Color    c;
};

struct BoundingBox
{
    Point  minP;
    Point  maxP;
    Vector vSize;
};

struct Face
{
    // Vertex Index
    int v1;
    int v2;
    int v3;
};

struct Object
{
    std::vector<Vertex> vertices;
    std::vector<Face  > faces;

    BoundingBox         bBox;
};

inline
void subPnt(Vector &vResult, Point &p1, Point &p2)
{
    vResult[X] = p1[X] - p2[X];
    vResult[Y] = p1[Y] - p2[Y];
    vResult[Z] = p1[Z] - p2[Z];
    vResult[W] = p1[W] - p2[W];
}

inline
void add(Vector &vResult, Vector &v1, Vector &v2)
{
    vResult[X] = v1[X] + v2[X];
    vResult[Y] = v1[Y] + v2[Y];
    vResult[Z] = v1[Z] + v2[Z];
    vResult[W] = v1[W] + v2[W];
}

inline
void addScaled(Point &result, Point &p, Vector &v, float s)
{
    result[X] = p[X] + (v[X] * s);
    result[Y] = p[Y] + (v[Y] * s);
    result[Z] = p[Z] + (v[Z] * s);
    result[W] = p[W] + (v[W] * s);
}

inline
void mult(Point &result, Matrix m, Point &p)
{
    for(int i = 0; i < 4; ++i)
    { 
        result[i] = 
            m[i +  0] * p[0] + 
            m[i +  4] * p[1] + 
            m[i +  8] * p[2] + 
            m[i + 12] * p[3];
    }
}

inline
void mult(Vector &result, Matrix m, Vector &v)
{
    for(int i = 0; i < 4; ++i)
    { 
        result[i] = 
            m[i +  0] * v[0] + 
            m[i +  4] * v[1] + 
            m[i +  8] * v[2] + 
            m[i + 12] * v[3];
    }
}

inline
void assign(Vector &v1, Vector &v2)
{
    v1[X] = v2[X];
    v1[Y] = v2[Y];
    v1[Z] = v2[Z];
    v1[W] = v2[W];
}

inline
float dot(Vector &v1, Vector &v2)
{
    return v1[X] * v2[X] + v1[Y] * v2[Y] + v1[Z] * v2[Z];
}

inline
float length(Vector &v)
{
    return sqrtf(v[X] * v[X] + v[Y] * v[Y] + v[Z] * v[Z]);
}

inline
void normalize(Vector &v)
{
    float fLength = length(v);

    fLength = 1.f / fLength;

    v[X] *= fLength;
    v[Y] *= fLength;
    v[Z] *= fLength;
    v[W] *= fLength;
}

inline
float maxComponent3(Vector &v)
{
    float returnValue = v[X];

    if(v[Y] > returnValue)
    {
        returnValue = v[Y];
    }
    
    if(v[Z] > returnValue)
    {
        returnValue = v[Z];
    }

    return returnValue;
}

inline
void dumpMatrix(Matrix m)
{
    for(int i = 0; i < 4; ++i)
    {
        fprintf(stderr, "%f %f %f %f\n",
                m[i * 4 + 0],
                m[i * 4 + 1],
                m[i * 4 + 2],
                m[i * 4 + 3]);
    }
}

inline
void dumpPnt(Point &p)
{
    fprintf(stderr, "%f %f %f %f\n",
            p[0],
            p[1],
            p[2],
            p[3]);
}

inline
void dumpPnt(const char *szInfo, Point &p)
{
    fprintf(stderr, "%s\n%f %f %f %f\n",
            szInfo,
            p[0],
            p[1],
            p[2],
            p[3]);
}

inline
void combineColor(Color &result, GLColor4 lc, GLColor4 mc, float s)
{
    result[R] += lc[R] * s * mc[R];
    result[G] += lc[G] * s * mc[G];
    result[B] += lc[B] * s * mc[B];
}

inline
void invert(Matrix &result, Matrix m)
{
	
	iMatrix temp;
	for(int i=0;i<4;i++)
		for(int j=0; j<8; j++)
		{
           if(j<4)
			   temp[i*8+j] = m[i*4+j];
           else
               temp[i*8+j] = 0;
           if(j==i+4) 
			   temp[i*8+j] = 1.0;
		}
	
	for(int i=0;i<3;i++)
		for(int k=i+1;k<4;k++)
			for(int j=7;j>=i;j--)
				temp[k*8+j] -= temp[i*8+j] * (temp[k*8+i]/temp[i*8+i]);

	
	for(int i=3;i>0;i--)
		for(int k=i-1;k>=0;k--)
			for(int j=7;j>=i;j--)
				temp[k*8+j] -= temp[i*8+j] * (temp[k*8+i]/temp[i*8+i]);

	for(int i=0;i<4;i++)
        for(int j=7;j>=0;j--)
            temp[i*8+j] = temp[i*8+j]/temp[i*8+i];

        
    for(int s=0;s<4;s++)
        for(int r=0;r<4;r++)
			result[s*4+r] = temp[s*8+r+4];
	                     

}

inline 
void transpose(Matrix &result, Matrix m)
{
	for(int j=0; j<4; j++)
		for(int i=0; i<4; i++)
		{
			result[j*4+i] = m[i*4+j];
		}
			
}

extern float x_angle;
extern float y_angle;
extern float x_offset;
extern float y_offset;
extern float z_offset;
extern float scale_size;


extern int  obj_mode;

extern bool show_bbox;
extern bool show_normals;
extern bool show_texImg;

extern bool drawTextures;
extern bool drawVertexColor;
extern bool genTextureCoords;

extern Object oObject;


void drawObject(bool useVertexColors);
void drawBoundingBox(void);
void calcPlaneMapping(void);
void calcCylindricalMapping(void);
void calcSphereMapping(void);

void initTexture(void);

void disp(void);


#endif
