// Part B: Many-bone worm

// New version by Ingemar 2010
// Removed all dependencies of the Wild Magic (wml) library.
// Replaced it with VectorUtils2 (in source)
// Replaced old shader module with the simpler "ShaderUtils" unit.

#ifdef __APPLE__
// Mac
	#include <OpenGL/gl.h>
	#include <GLUT/glut.h>
#else
	#ifdef WIN32
// MS
		#include <stdio.h>
		#include <GL/glew.h>
		#include <GL/glut.h>
	#else
// Linux
		#include <GL/gl.h>
		#include <GL/glut.h>
	#endif
#endif

#include "ShaderUtils.h"
#include "VectorUtils2.h"

// Ref till shader
GLuint g_shader;

typedef struct Triangle
{
  GLuint        v1;
  GLuint        v2;
  GLuint        v3;
} Triangle;

#define CYLINDER_SEGMENT_LENGTH 0.26
#define kMaxRow 100
#define kMaxCorners 8
#define kMaxBones 10
#define kMaxg_poly ((kMaxRow-1) * kMaxCorners * 2)
#ifndef Pi
#define Pi 3.1416
#endif
#ifndef true
#define true 1
#endif

#define BONE_LENGTH 4.0

Triangle g_poly[kMaxg_poly];

// vertexs
Point3D g_vertsOrg[kMaxRow][kMaxCorners];
Point3D g_normalsOrg[kMaxRow][kMaxCorners];
Point3D g_vertsRes[kMaxRow][kMaxCorners];
Point3D g_normalsRes[kMaxRow][kMaxCorners];

// vertex attributes
float g_boneWeights[kMaxRow][kMaxCorners][kMaxBones];



///////////////////////////////////////////////////
//		I N I T  B O N E  W E I G H T S
// Desc:  initierar benvikterna
//
void initBoneWeights(void)
{
	long	row, corner;

	// sätter värden till alla vertexar i meshen
	for (row = 0; row < kMaxRow; row++)
		for (corner = 0; corner < kMaxCorners; corner++)
		{
			float boneWeights[kMaxBones];
			float totalBoneWeight = 0.f;

			float maxBoneWeight = 0.f;

			for (int bone = 0; bone < kMaxBones; bone++)
			{
				float bonePos = BONE_LENGTH * bone;
				float boneDist = fabs(bonePos - g_vertsOrg[row][corner].x);
				float boneWeight = (BONE_LENGTH - boneDist) / (BONE_LENGTH);
				if (boneWeight < 0)
					boneWeight = 0;
				boneWeights[bone] = boneWeight;
				totalBoneWeight += boneWeight;
	
				if (maxBoneWeight < boneWeight)
					maxBoneWeight = boneWeight;
			}

			for (int bone = 0; bone < kMaxBones; bone++)
			{
				g_boneWeights[row][corner][bone] = boneWeights[bone] / totalBoneWeight;
			}
		}
}


///////////////////////////////////////////////////
//		B U I L D  C Y L I N D E R
// Desc:  bygger upp cylindern 
//
void BuildCylinder()
{
  long	row, corner, cornerIndex;

  // sätter värden till alla vetexar i meshen
  for (row = 0; row < kMaxRow; row++)
    for (corner = 0; corner < kMaxCorners; corner++)
      {
	g_vertsOrg[row][corner].x = (float) row * CYLINDER_SEGMENT_LENGTH;
	g_vertsOrg[row][corner].y = cos(corner * 2*Pi / kMaxCorners);
	g_vertsOrg[row][corner].z = sin(corner * 2*Pi / kMaxCorners);

	g_normalsOrg[row][corner].x = 0;
	g_normalsOrg[row][corner].y = cos(corner * 2*Pi / kMaxCorners);
	g_normalsOrg[row][corner].z = sin(corner * 2*Pi / kMaxCorners);
      };

  // g_poly definerar mellan vilka vertexar som 
  // tringalarna ska ritas
  for (row = 0; row < kMaxRow-1; row++)
    for (corner = 0; corner < kMaxCorners; corner++)
      {
	// Quads built from two triangles

	if (corner < kMaxCorners-1) 
	  {
	    cornerIndex = row * kMaxCorners + corner;
	    g_poly[cornerIndex * 2].v1 = cornerIndex;
	    g_poly[cornerIndex * 2].v2 = cornerIndex + 1;
	    g_poly[cornerIndex * 2].v3 = cornerIndex + kMaxCorners + 1;

	    g_poly[cornerIndex * 2 + 1].v1 = cornerIndex;
	    g_poly[cornerIndex * 2 + 1].v2 = cornerIndex + kMaxCorners + 1;
	    g_poly[cornerIndex * 2 + 1].v3 = cornerIndex + kMaxCorners;
	  }
	else
	  { // Specialfall: sista i varvet, gåu runt hörnet korrekt
	    cornerIndex = row * kMaxCorners + corner;
	    g_poly[cornerIndex * 2].v1 = cornerIndex;
	    g_poly[cornerIndex * 2].v2 = cornerIndex + 1 - kMaxCorners;
	    g_poly[cornerIndex * 2].v3 = cornerIndex + kMaxCorners + 1 - kMaxCorners;

	    g_poly[cornerIndex * 2 + 1].v1 = cornerIndex;
	    g_poly[cornerIndex * 2 + 1].v2 = cornerIndex + kMaxCorners + 1 - kMaxCorners;
	    g_poly[cornerIndex * 2 + 1].v3 = cornerIndex + kMaxCorners;
	  }
      }

  // lägger en kopia av orginal modellen i g_vertsRes
  memcpy(g_vertsRes,  g_vertsOrg, kMaxRow * kMaxCorners* sizeof(Point3D));
  memcpy(g_normalsRes,  g_normalsOrg, kMaxRow * kMaxCorners* sizeof(Point3D));
}


//////////////////////////////////////
//		B O N E
// Desc:	en enkel ben-struct med en 
//			pos-vektor och en rot-vektor 
//			rot vektorn skulle lika gärna 
//			kunna vara av 3x3 men VectorUtils2 har bara 4x4
typedef struct bone
{
  Point3D pos;
  Matrix4D rot;
} bone;


///////////////////////////////////////
//		G _ B O N E S
// vårt skelett
bone g_bones[kMaxBones]; // Ursprungsdata, Šndra ej
bone g_bonesRes[kMaxBones]; // Animerat


///////////////////////////////////////////////////////
//		S E T U P  B O N E S
//
void setupBones(void)
{
  for (int bone = 0; bone < kMaxBones; bone++)
  {
	SetVector((float) bone * BONE_LENGTH, 0.0f, 0.0f, &g_bones[bone].pos);
	IdentityMatrix(g_bones[bone].rot);
  }
}


///////////////////////////////////////////////////////
//		D E F O R M  C Y L I N D E R 
//
// Desc:	deformera cylinder meshen enligt skelettet
void DeformCylinder()
{
  Point3D v[kMaxBones];

  float w[kMaxBones];

  // för samtliga vertexar 
  for (int row = 0; row < kMaxRow; row++)
  {
    for (int corner = 0; corner < kMaxCorners; corner++)
    {
      // ---------=========  UPG 4 ===========---------
      // TODO: skinna meshen mot alla benen.
      //
      // data som du kan använda:
      // g_bonesRes[].rot
      // g_bones[].pos
      // g_boneWeights
      // g_vertsOrg
      // g_vertsRes
      
    }
  }
}


/////////////////////////////////////////////
//		A N I M A T E  B O N E S
// Desc:	en väldigt enkel animation av skelettet
//			vrider ben 1 i en sin(counter) 
void animateBones(void)
{
	// Hur mycket kring varje led? €ndra gŠrna.
	float angleScales[10] = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f };

	float time = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
	// Hur mycket skall vi vrida?
	float angle = sin(time * 3.f) / 2.0f;

	memcpy(&g_bonesRes, &g_bones, kMaxBones*sizeof(bone)); 

	Rz(angle * angleScales[0], &g_bonesRes[0].rot);

	for (int bone = 1; bone < kMaxBones; bone++)
		Rz(angle * angleScales[bone], &g_bonesRes[bone].rot);
}


///////////////////////////////////////////////
//		S E T  B O N E  R O T A T I O N
// Desc:	sätter bone rotationen i vertex shadern
// (Ej obligatorisk.)
void setBoneRotation(void)
{
}


///////////////////////////////////////////////
//		 S E T  B O N E  L O C A T I O N
// Desc:	sätter bone positionen i vertex shadern
// (Ej obligatorisk.)
void setBoneLocation(void)
{
}


///////////////////////////////////////////////
//		 D R A W  C Y L I N D E R
// Desc:	sätter bone positionen i vertex shadern
void DrawCylinder()
{
  animateBones();

  // ---------=========  UPG 2 (extra) ===========---------
  // ersätt DeformCylinder med en vertex shader som gör vad DeformCylinder gör.
  // begynelsen till shader koden ligger i filen "ShaderCode.vert" ...
  // 
	
  DeformCylinder();
	
  // setBoneLocation();
  // setBoneRotation();

  // Note: This would be best drawn as a set of triangle strips
  glVertexPointer(3, GL_FLOAT, 0, &g_vertsRes[0]);
  glColorPointer(3, GL_FLOAT, 0, &g_boneWeights[0]);
  glNormalPointer( GL_FLOAT, 0, &g_normalsRes[0]);
	
  // bone weights lagras i GL_TEXTURE1_ARB 
  // som i vertex shadern nås via gl_MultiTexCoord1
  glClientActiveTextureARB(GL_TEXTURE1_ARB); 
  glTexCoordPointer(3, GL_FLOAT, 0, &g_boneWeights); 
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  glDrawElements(GL_TRIANGLES, kMaxg_poly*3, GL_UNSIGNED_INT, &g_poly[0]);
}


void DisplayWindow()
{
  glClearColor(0.4, 0.4, 0.2, 1);
  glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT);

  glPushMatrix();
  glTranslatef(-10, 0, -10.0);

  glScalef(3, 3, 3);
  DrawCylinder();

  glPopMatrix();

  glutSwapBuffers();
};

void OnTimer(int value)
{
  glutPostRedisplay();
  glutTimerFunc(20, &OnTimer, value);
}

void keyboardFunc( unsigned char key, int x, int y)
{
  if(key == 27)	//Esc
    exit(1);
}

/////////////////////////////////////////
//		M A I N
//
int main(int argc, char **argv)
{
  glutInit(&argc, argv);

  glutInitWindowSize(512, 512);
  glutInitWindowPosition(100, 100);

  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
  glutCreateWindow("Them bones, them bones");

  glutDisplayFunc(DisplayWindow);
  glutTimerFunc(20, &OnTimer, 0);
  glutKeyboardFunc( keyboardFunc ); 

  // Set up depth buffer
  glEnable(GL_DEPTH_TEST);
  glDisable(GL_BLEND);

  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);

  // initiering
#ifdef WIN32
  glewInit();
#endif
  BuildCylinder();
  setupBones();
  initBoneWeights();
  g_shader = loadShaders("shader.vert" , "shader.frag");

  // Set up projection matrix
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(90, 1.3, 0.1, 100);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef(-20, 0, -48.0);
  glutMainLoop();
}

