
#ifdef WIN32
#include <stdio.h>
#include <GL/glew.h>
#include <GL/glut.h>
#else 
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif

#include "shader.h"

#include <wml/Math/WmlVector3.h>
#include <wml/Math/WmlMatrix3.h>


using namespace Wml;


// per frame uppräknare 
int        g_counter;

shader		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

// 
long g_count = 0;

Triangle g_poly[kMaxg_poly];

// vertexs
Vector3f g_vertsOrg[kMaxRow][kMaxCorners];
Vector3f g_normalsOrg[kMaxRow][kMaxCorners];
Vector3f g_vertsRes[kMaxRow][kMaxCorners];
Vector3f 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:  initsierar ben vikterna
//
void initBoneWeights(void)
{
  long	row, corner;

  // sätter värden till alla vetexar 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][0]);
	    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][0] = (float) row * CYLINDER_SEGMENT_LENGTH;
	g_vertsOrg[row][corner][1] = cos(corner * 2*Pi / kMaxCorners);
	g_vertsOrg[row][corner][2] = sin(corner * 2*Pi / kMaxCorners);

	g_normalsOrg[row][corner][0] = 0;
	g_normalsOrg[row][corner][1] = cos(corner * 2*Pi / kMaxCorners);
	g_normalsOrg[row][corner][2] = 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(Vector3f));
  memcpy(g_normalsRes,  g_normalsOrg, kMaxRow * kMaxCorners* sizeof(Vector3f));

}


//////////////////////////////////////
//		B O N E
// Desc:	en enkel ben struct men en 
//			pos vektor och en rot vektor 
//			rot vektorn skulle lika järna 
//			kunna vara av 3x3 men VectorUtils2 har bara 4x4
struct bone
{
  bone()
    : pos(Vector3f::ZERO), rot(Matrix3f::IDENTITY), trans(Vector3f::ZERO)
  {
  }

  Vector3f pos;
  Matrix3f rot;
  Vector3f trans;
};



///////////////////////////////////////
//		G _ B O N E S
// vårt skelett
bone g_bones[kMaxBones];

bone g_bonesRes[kMaxBones];


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



///////////////////////////////////////////////////////
//		D E F O R M  C Y L I N D E R 
//
// Desc:	deformera cylinder meshen enligt skelettet
void DeformCylinder()
{
  Vector3f 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_bonesRes[].trans
      // 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 amination av skelettet
//			vrider ben 1 i en sin(counter) 
void animateBones(void)
{
  float angleScales[10] = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f };

  float angle = sin((float)g_counter / 20.0f) / 5.0f * 3.0f;

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

  g_bonesRes[0].rot.FromAxisAngle(Vector3f(0.f, 0.f, 1.f),
				  angle * angleScales[0]);

  for (int bone = 1; bone < kMaxBones; bone++)
    {
      g_bonesRes[bone].rot.FromAxisAngle(Vector3f(0.f, 0.f, 1.f),
					 angle * angleScales[bone]);

      Vector3f trans;

      g_bonesRes[bone].rot = g_bonesRes[bone].rot * g_bonesRes[bone - 1].rot;
      
      g_bonesRes[bone].trans = (g_bones[bone].pos - g_bones[bone - 1].pos)*
                          g_bonesRes[bone-1].rot+g_bonesRes[bone - 1].trans;
    }
}


///////////////////////////////////////////////
//		S E T  B O N E  R O T A T I O N
// Desc:	sätter bone rotationen i vertex shadern
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
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 ===========---------
  // 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()
{
  g_counter++;

  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)
{
  //printf("%d fps\n", g_count);
  g_count = 0;
  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("skinning demo");


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

  // Enable backface culling
  // glEnable(GL_CULL_FACE);


  // Set up depth buffer
  glEnable(GL_DEPTH_TEST);
  // glDepthFunc(GL_LESS);
  glDisable(GL_BLEND);
  // glBlendFunc(GL_SRC_ALPHA,GL_ONE);


  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);

  // initiering
  glewInit();
  BuildCylinder();
  setupBones();
  initBoneWeights();
  g_shader.initShaders( "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, -28.0);
  glutMainLoop();

}


