
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <Cg/cg.h>
#include <Cg/cgGL.h>

#ifndef CWD
# define CWD ""
#endif

/******************************************************************************/
/*** Static Data                                                            ***/
/******************************************************************************/

const static int TextureRes = 512;

static CGcontext Context = NULL;
static CGprogram Program = NULL;
static CGparameter KdParam = NULL;
static CGparameter ModelViewProjParam = NULL;
static CGparameter TestColorParam = NULL;

static CGprofile profile;

GLfloat LightDiffuse[] = {1.0, 0.0, 0.0, 1.0};  
GLfloat LightPosition[] = {1.0, 1.0, 1.0, 0.0};  

GLfloat CubeNormals[6][3] = 
 {  
  {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
  {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} 
 };

GLint CubeFaces[6][4] = 
 {  
  {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
  {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} 
 };

GLfloat CubeVertices[8][3];  

/******************************************************************************/

#define MATRIX_INDEX(i, j) (j + i * 4)


static void CheckCgError(void)
{
  CGerror err = cgGetError();

  if (err != CG_NO_ERROR)
   {
     printf("CG error: %s\n", cgGetErrorString(err));
     exit(1);
   }
}


static void DrawCube(void)
 {
  int i;

  cgGLBindProgram(Program);
  CheckCgError();

  /*
   * Set various uniform parameters including the ModelViewProjection
   * matrix for transforming the incoming position into HPOS.
   */
  if(KdParam != NULL)
   cgGLSetParameter4f(KdParam, 1.0, 1.0, 0.0, 1.0);
    
  /* Set the concatenate modelview and projection matrices */
  if(ModelViewProjParam != NULL)
    cgGLSetStateMatrixParameter(ModelViewProjParam,
                                CG_GL_MODELVIEW_PROJECTION_MATRIX,
                                CG_GL_MATRIX_IDENTITY);

  cgGLEnableProfile(profile);


  /*
   * Create cube with per-vertex varying attributes 
   */
  for(i = 0; i < 6; i++) 
   {
    glBegin(GL_QUADS);

    glNormal3fv(&CubeNormals[i][0]);
    cgGLSetParameter3f(TestColorParam, 1.0, 0.0, 0.0);
    glVertex3fv(&CubeVertices[CubeFaces[i][0]][0]);

    cgGLSetParameter3f(TestColorParam, 0.0, 1.0, 0.0);
    glVertex3fv(&CubeVertices[CubeFaces[i][1]][0]);

    cgGLSetParameter3f(TestColorParam, 0.0, 0.0, 1.0);
    glVertex3fv(&CubeVertices[CubeFaces[i][2]][0]);

    cgGLSetParameter3f(TestColorParam, 1.0, 1.0, 1.0);
    glVertex3fv(&CubeVertices[CubeFaces[i][3]][0]);
    
    glEnd();
   }

  cgGLDisableProfile(profile);
 }

static void Display(void)
 {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  DrawCube();
  glutSwapBuffers();
 }

static void InitializeCube(GLfloat v[8][3])
 {
  /* Setup cube vertex data. */
  v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
  v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
  v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
  v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
  v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
  v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;
 }

static void InitializeGlut(int *argc, char *argv[])
 {
  glutInit(argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(Display);

  InitializeCube(CubeVertices);

  /* Enable a single OpenGL light. */
  glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
  glEnable(GL_LIGHT0);
#if 0
  glEnable(GL_LIGHTING);
#else
  glDisable(GL_LIGHTING);
#endif

  /* Use depth buffering for hidden surface elimination. */
  glEnable(GL_DEPTH_TEST);

  /* Setup the view of the cube. */
  glMatrixMode(GL_PROJECTION);
  gluPerspective( /* field of view in degree */ 40.0,
    /* aspect ratio */ 1.0,
    /* Z near */ 1.0, /* Z far */ 10.0);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
            0.0, 0.0, 0.0,  /* center is at (0,0,0) */
            0.0, 1.0, 0.);  /* up is in positive Y direction */

  /* Adjust cube position to be asthetic angle. */
  glTranslatef(0.0, 0.0, -1.0);
#if 1
  glRotatef(60, 1.0, 0.0, 0.0);
  glRotatef(-20, 0.0, 0.0, 1.0);
#endif
  
 }


int main(int argc, char *argv[])
 {
  InitializeGlut(&argc, argv);

  if (cgGLIsProfileSupported(CG_PROFILE_VP20))
    profile = CG_PROFILE_VP20;
  else if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1))
    profile = CG_PROFILE_ARBVP1;
  else
   {
    fprintf(stderr, "Video card does not support vertex programs, exiting...\n");
    exit(-1);
   }

  /* Test cgContext creation */
  Context = cgCreateContext();
  CheckCgError();

  /* Test adding source text to context */
  Program = cgCreateProgramFromFile(Context,
                                    CG_SOURCE, CWD "cgGL_vertex_example.cg",
                                    profile,
                                    NULL, NULL);
  CheckCgError();

fprintf(stderr, "LAST LISTING----%s----\n", cgGetLastListing(Context));

fprintf(stderr, "---- PROGRAM BEGIN ----\n%s---- PROGRAM END ----\n",
        cgGetProgramString(Program, CG_COMPILED_PROGRAM));

  if(Program != NULL)
   {
    cgGLLoadProgram(Program);
    CheckCgError();

    KdParam = cgGetNamedParameter(Program, "Kd");
    CheckCgError();

    ModelViewProjParam = cgGetNamedParameter(Program, "ModelViewProj");
    CheckCgError();

    TestColorParam = cgGetNamedParameter(Program, "IN.TestColor");
    CheckCgError();
   }

  glutMainLoop();

  cgDestroyProgram(Program);
  cgDestroyContext(Context);

  return 0;
 }

