#include "Jellyfish.h"
//#include <glh_genext.h>
#include "constants.h"
#ifndef _WIN32
#include <sys/time.h>
#endif

extern GLuint vertexProgramsNV[1];

extern PFNGLPROGRAMPARAMETER4FNVPROC		glProgramParameter4fNV;
extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC		glProgramLocalParameter4fARB;
extern PFNGLBINDPROGRAMNVPROC				glBindProgramNV;

float queryElapsedTime();

inline float myRandom( )
{
  static unsigned int n = 0;

  n += 7;
  n = (n<<13) ^ n;

  return 0.5f * ( 2.0f - ( (n * (n * n * 15731 + 789221) 
    + 1376312589) & 0x7fffffff) * 9.313225746e-10f);
}


Jellyfish::Jellyfish( )
{
  Vector3 a,b,c;

  nVertices = 0;
 
  generateLightingTexture( );
  create( );
}

Jellyfish::~Jellyfish( )
{
  delete [] strip;
  delete [] normals;
  delete [] segments;
  delete [] points;

  glDeleteTextures(1, &texture);
}

void Jellyfish::create( )
{
  // jellyfish body
  float crossSection[118] = {
    0.0f,9.44361f,0.38913f,9.44208f,1.0702f,9.38564f,1.77009f,9.26147f,
    2.25174f, 9.11471f,2.92905f,8.79864f,3.42198f,8.45245f,4.0278f,7.89932f,
    4.35141f,7.52679f,4.65243f,7.07149f,4.9798f,6.48072f,5.2849f,5.67923f,
    5.46437f,5.16185f,5.66547f,4.77636f,5.93869f,4.45945f,6.28112f,4.16573f, 
    6.74834f, 3.90317f,7.27513f,3.53442f,7.54982f,3.28982f,7.8358f,2.95494f, 
    8.0277f,2.60876f,8.1293f,2.24376f,8.18051f,1.76588f,8.12177f,1.20145f, 
    7.98255f,0.719805f,7.82451f,0.471455f,7.6025f,0.264505f,7.30147f,0.0801254f, 
    7.0f,0.0f,6.54744f,0.0245184f,6.0959f,0.202837f,5.85738f,0.454947f, 
    5.68345f,0.817635f,5.54046f,1.53635f,5.41629f,2.01047f,5.1231f,2.5636f, 
    4.63738f,3.14684f,4.26862f,3.44818f,3.80579f,3.71534f,3.17363f,3.94456f, 
    2.56405f,4.14023f,2.13133f,4.3961f,1.92061f,4.66327f,1.84911f,4.96429f, 
    1.93942f,5.22769f,2.15767f,5.42712f,2.55653f,5.56635f,2.94034f,5.62655f, 
    3.23384f,5.72439f,3.39188f,5.87491f,3.44456f,6.10443f,3.38435f,6.35654f, 
    3.28276f,6.52588f,3.07204f,6.72155f,2.60168f,6.95484f,2.14261f,7.13546f, 
    1.40102f,7.32328f,0.58073f,7.41766f,0.0f,7.43333f
  };

  nVertices = 118;
  float rotAngle = 2.0f * M_PI / 24.0f;
  float cosAngle = cosf(rotAngle);
  float sinAngle = sinf(rotAngle);
  float dy, dx, slope;

  strip = new Vector3[nVertices];
  normals = new Vector3[nVertices];
  tex = new Vector3[nVertices];
  unsigned int i;
  for ( i = 0; i < nVertices / 2; i++ )
  {
    strip[2*i].set(crossSection[2*i], crossSection[2*i+1]-10.0f, 0.0f );
    tex[2*i].set(0.0f, strip[2*i](1) / 10.0f, 0.0f);

    strip[2*i+1].set(cosAngle * crossSection[2*i], crossSection[2*i+1]-10.0f, 
                     sinAngle * crossSection[2*i] );
    tex[2*i+1].set(rotAngle / (2.0f * M_PI), strip[2*i+1](1) / 10.0f, 0.0f);

    if ( i == 0 || i == nVertices / 2 - 1)
    {
      normals[2*i].set(0.0f, 1.0f, 0.0f);
      normals[2*i+1].set(0.0f, 1.0f, 0.0f);
    }
    else
    {
      dx = crossSection[2*(i+1)] - crossSection[2*(i-1)];
      dy = crossSection[2*(i+1)+1] - crossSection[2*(i-1)+1];
      slope = dy / dx;

      normals[2*i].set( 1.0f, -1.0f / slope, 0.0f );
      normals[2*i+1].set( cosAngle, -1.0f / slope, sinAngle );
    }

    normals[2*i].normalize();
    normals[2*i+1].normalize();
  }

  //  tentacles 
  nSegments = 20;
  segments = new Vector3[nSegments];
  for ( i = 0; i < nSegments; i++ )
  {
    segments[i].set(7.0f, -10.0f - float(i), 0.0f);
  }

  // water debris
  nPoints = 1000;
  points = new Vector3[nPoints];
  for ( i = 0; i < nPoints; i++ )
  {
    points[i](0) = 700.0f * (myRandom( ) - 0.5f);
    points[i](1) = 700.0f * (myRandom( ) - 0.5f);
    points[i](2) = 700.0f * (myRandom( ) - 0.5f);
  }


  // Create jellyfish display list
  const unsigned int nSlices = 24;
  const float nRadPerSlice = 2.0f * M_PI / 24.0f;
  float angle, temp, texOffset;
  Vector3 v,n;

  jellyfishList = glGenLists(1);
  glNewList(jellyfishList, GL_COMPILE);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glDisable(GL_TEXTURE_2D);
	glEnable(GL_TEXTURE_1D);
  glBindTexture(GL_TEXTURE_1D, texture);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDepthMask(GL_FALSE);

#if USE_ARB_VERTEXPROGRAM
	glEnable(GL_VERTEX_PROGRAM_ARB);
	glBindProgramNV(GL_VERTEX_PROGRAM_ARB, vertexProgramsNV[0]);
#else
	glEnable(GL_VERTEX_PROGRAM_NV);
	glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertexProgramsNV[0]);
#endif
 
  // draw the body
  glColor4f(1.0f, 0.0f, 1.0f, 0.4f);

  for (  i = 0; i < nSlices; i++)
  {
    angle = float(i) * nRadPerSlice;
    texOffset = 1.0f - angle / (2.0f * M_PI);
    cosAngle = cosf(angle);
    sinAngle = sinf(angle);
    unsigned int j;
    glBegin(GL_TRIANGLE_STRIP);
    for (  j = 0; j < nVertices; j++)
    {
      v = strip[j];
      temp = cosAngle * v(0) + sinAngle * v(2);
      v(2) = -sinAngle * v(0) + cosAngle * v(2);
      v(0) = temp;
      n = normals[j];
      temp = cosAngle * n(0) + sinAngle * n(2);
      n(2) = -sinAngle * n(0) + cosAngle * n(2);
      n(0) = temp;

      glNormal3fv( n.get() );
      glVertex3fv( v.get() );
    }
    glEnd( );


    // draw the tentacles
    glBegin(GL_TRIANGLE_STRIP);
    Vector3 w(0.0f, 0.0f, 1.0f);

    glNormal3f( cosAngle, 0.0f, -sinAngle);

    for ( j = 0; j < nSegments; j++)
    {
      w *= 0.9f;

      v = segments[j] + w;
      temp = cosAngle * v(0) + sinAngle * v(2);
      v(2) = -sinAngle * v(0) + cosAngle * v(2);
      v(0) = temp;

      glVertex3fv( v.get() );

      v = segments[j] - w;
      temp = cosAngle * v(0) + sinAngle * v(2);
      v(2) = -sinAngle * v(0) + cosAngle * v(2);
      v(0) = temp;

      glVertex3fv( v.get() );
    }
    glEnd( );
  }

#if USE_ARB_VERTEXPROGRAM
	glDisable(GL_VERTEX_PROGRAM_ARB);
#else
	glDisable(GL_VERTEX_PROGRAM_NV);
#endif
	
  glDisable(GL_TEXTURE_1D);

  glDepthMask(GL_TRUE);
  glEndList( );
}

void Jellyfish::render( )
{
  static float lastTime = 0.0f;
  float dt;
  float time = 0.0f;
  static float ourTime = 0.0f;
  float k = 0.25f;    // inverse wavelength of ripple
  float w = 3.0f;     // inverse period of ripple

  // What follows is the CPU's only per-frame computations
  // for animating the jellyfish
/*
#ifdef _WIN32
  time = queryElapsedTime( );
#else
  timeval tmptime;
  gettimeofday (&tmptime,NULL);
  time = tmptime.tv_sec+tmptime.tv_usec*.000001;
#endif
*/
  time = queryElapsedTime( );

  dt = time - lastTime;
  lastTime = time;
  float hfactor = .805f - 0.195f * sinf(w * ourTime);
  if ( cosf(w * ourTime) < 0.0f )
  {
    dt *= 2.0f;
  }
  ourTime += dt;

  // Begin drawing, water debris first
/*  glDisable(GL_BLEND);
  glBegin(GL_POINTS);
  glColor3f( 1.0f, 1.0f, 1.0f );
  for ( unsigned int i = 0; i < nPoints; i++ )
  {
    points[i](1) -= w * dt;
    if ( points[i](1) < -50.0f )
    {
      points[i](1) += 1000.0f;
    }

    glVertex3fv( points[i].get() );
  }
  glEnd( );*/

  // Now send some parameters and draw the jellyfish
#if USE_ARB_VERTEXPROGRAM
  glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, 16, k, hfactor, w * ourTime, 1.0f);
#else
  glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 16, k, hfactor, w * ourTime, 1.0f);
#endif
  glCallList(jellyfishList);
}

void Jellyfish::generateLightingTexture( )
{
  const unsigned int size = 256;
  unsigned int index;
  float *data;
  data = new float[4 * size];

  for ( unsigned int i = 0; i < size; i++ )
  {
    index = 4 * i;

    data[index] = 1.0f - float(i) / float(size);
    data[index] *= data[index];
    data[++index] = 0.0f;
    data[++index] = 1.0f;
    data[++index] = 0.7f - float(i) / float(size);
  }

  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_1D, texture);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, size, 0, GL_RGBA, GL_FLOAT, data);

  delete [] data;
}





