// SkyBox.cpp

#include "SkyBox.h"

SkyBox::SkyBox(dReal posx, dReal posy, dReal posz, dReal Width, dReal Height, dReal Length)
: Base3DObject(false, false, "", "", posx, posy, posz)
{
	x = posx;
	y = posy;
	z = posz;
	width = Width;
	height = Height;
	length = Length;

	// Since we want the sky box to be centered around X, Y, and Z for ease,
	// we do a little math to accomplish this.  We just change the X, Y and Z
	// to perform this task.  If we just minus half the width, height and length
	// from x, y and z it will give us the desired result.  Now when we create the
	// box it will center it around (x, y, z)

	// This centers the sky box around (x, y, z)
	x = x - width  / 2;
	y = y - height / 2;
	z = z - length / 2;

	//Do this in a separate function so it can be done in VRJuggler's contextInit function.
	//MakeDisplayList();
}

SkyBox::~SkyBox()
{
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
	glDeleteTextures(1, &(*g_Texture[BACK_ID]));
	glDeleteTextures(1, &(*g_Texture[FRONT_ID]));
	glDeleteTextures(1, &(*g_Texture[BOTTOM_ID]));
	glDeleteTextures(1, &(*g_Texture[TOP_ID]));
	glDeleteTextures(1, &(*g_Texture[LEFT_ID]));
	glDeleteTextures(1, &(*g_Texture[RIGHT_ID]));
	glDeleteLists(*displayListNumber, 1);
#else //using normal data types
	glDeleteTextures(1, &g_Texture[BACK_ID]);
	glDeleteTextures(1, &g_Texture[FRONT_ID]);
	glDeleteTextures(1, &g_Texture[BOTTOM_ID]);
	glDeleteTextures(1, &g_Texture[TOP_ID]);
	glDeleteTextures(1, &g_Texture[LEFT_ID]);
	glDeleteTextures(1, &g_Texture[RIGHT_ID]);
	glDeleteLists(displayListNumber, 1);
#endif
}

void SkyBox::Draw()
{
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
	glCallList(*displayListNumber);
#else //using normal data types
	glCallList(displayListNumber);
#endif
}

void SkyBox::MakeDisplayList()
{
	// Below we read in the 6 texture maps used for the sky box.
	// The ID's are important, so don't mix them up.  There is a
	// texture for every side of the box.

#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
	*g_Texture[BACK_ID] = CreateTexture("data/skybox_back.jpg");
	*g_Texture[FRONT_ID] = CreateTexture("data/skybox_front.jpg");
	*g_Texture[BOTTOM_ID] = CreateTexture("data/skybox_bottom.jpg");
	*g_Texture[TOP_ID] = CreateTexture("data/skybox_top.jpg");
	*g_Texture[LEFT_ID] = CreateTexture("data/skybox_left.jpg");
	*g_Texture[RIGHT_ID] = CreateTexture("data/skybox_right.jpg");
#else //using normal data types
	g_Texture[BACK_ID] = CreateTexture("data/skybox_back.jpg");
	g_Texture[FRONT_ID] = CreateTexture("data/skybox_front.jpg");
	g_Texture[BOTTOM_ID] = CreateTexture("data/skybox_bottom.jpg");
	g_Texture[TOP_ID] = CreateTexture("data/skybox_top.jpg");
	g_Texture[LEFT_ID] = CreateTexture("data/skybox_left.jpg");
	g_Texture[RIGHT_ID] = CreateTexture("data/skybox_right.jpg");
#endif

	float matAmbientAndDiffuse[4] = {1.0, 1.0, 1.0, 1};
	float matSpecular[4] = {0, 0, 0, 1};
	//float matShininess = 60;

#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
	*displayListNumber = glGenLists(1);
	glNewList(*displayListNumber, GL_COMPILE);
#else //using normal data types
	displayListNumber = glGenLists(1);
	glNewList(displayListNumber, GL_COMPILE);
#endif

		glEnable(GL_TEXTURE_2D);
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matAmbientAndDiffuse);
		glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
		//glMaterialf(GL_FRONT, GL_SHININESS, matShininess);

		// This is the most important function of this tutorial.  This function
		// used to just create a silly colored cube in the RotateCube tutorial,
		// but now it creates something beautiful.  You'll notice we added
		// some more parameters to the function.  This way we can change the perspective
		// of the sky box.  It doesn't really look good if it's a perfect cube.  Some
		// textures look better at different ratios.  We assign the sky box textures
		// to each side of the box creating the illusion of a detailed 3D world.
		// You will notice I had to change the texture coordinates for every one
		// to be flipped correctly.  Also, depending on your culling, the vertex
		// order might need to be changed around.  I don't use culling in this tutorial
		// so it will work fine here, but be sure to remember this if you do.

		// Bind the BACK texture of the sky map to the BACK side of the cube
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[BACK_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[BACK_ID]);
#endif

		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);		
			
			// Assign the texture coordinates and vertices for the BACK Side
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z); 
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height, z);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z);
			
		glEnd();

		// Bind the FRONT texture of the sky map to the FRONT side of the box
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[FRONT_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[FRONT_ID]);
#endif

		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);	
		
			// Assign the texture coordinates and vertices for the FRONT Side
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z + length);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height, z + length);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z + length); 
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z + length);
		glEnd();

		// Bind the BOTTOM texture of the sky map to the BOTTOM side of the box
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[BOTTOM_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[BOTTOM_ID]);
#endif

		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);		
		
			// Assign the texture coordinates and vertices for the BOTTOM Side
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y,			z + length);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y,			z + length); 
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
		glEnd();

		// Bind the TOP texture of the sky map to the TOP side of the box
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[TOP_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[TOP_ID]);
#endif
		
		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);		
			
			// Assign the texture coordinates and vertices for the TOP Side
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y + height, z + length); 
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y + height,	z + length);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z);
			
		glEnd();

		// Bind the LEFT texture of the sky map to the LEFT side of the box
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[LEFT_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[LEFT_ID]);
#endif
		
		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);		
			
			// Assign the texture coordinates and vertices for the LEFT Side
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z);	
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height,	z + length); 
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z + length);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);		
			
		glEnd();

		// Bind the RIGHT texture of the sky map to the RIGHT side of the box
#ifdef USING_VRJUGGLER //using Juggler's special context-specific data types
		glBindTexture(GL_TEXTURE_2D, *g_Texture[RIGHT_ID]);
#else //using normal data types
		glBindTexture(GL_TEXTURE_2D, g_Texture[RIGHT_ID]);
#endif

		// Start drawing the side as a QUAD
		glBegin(GL_QUADS);		

			// Assign the texture coordinates and vertices for the RIGHT Side
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z + length);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height,	z + length); 
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height,	z);
		glEnd();

	glDisable(GL_TEXTURE_2D);
	glEndList();
}

// We added this from our Texture Map tutorial.  We did however add these 2 lines:
//
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//
// This clamps the texture so that it does NOT wrap which creates
// seems in our sky box.  We need to define GL_CLAMP_TO_EDGE in our
// main.h.  This is not part of the normal opengl header files apparently.
// If you have an NVidia card it should just accept GL_CLAMP, but other
// video cards don't handle that flag as well.  The NVidia driver interprets 
// GL_CLAMP as GL_CLAMP_TO_EDGE, which will cause it to look correct on NVidia 
// cards, but wrong on all other cards. GL_CLAMP really isn't clamping to the 
// edge of the texture, it's clamping to an average of border color and the 
// texture edge, which tends to cause visible black edges.
//
//

///////////////////////////////// CREATE TEXTURE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This creates a texture in OpenGL that we can texture map
/////
///////////////////////////////// CREATE TEXTURE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

GLuint SkyBox::CreateTexture(std::string filename)
{
	GLuint newTextureID;
	
	if(filename == "")
	{
		std::cout << "No image filename given." << std::endl;
		std::cout << "Quitting..." << std::endl;
		exit(0);
	}
	
	corona::Image* image = corona::OpenImage(filename.c_str(), corona::PF_R8G8B8);

	if (!image)
	{
		std::cout << "Error loading image file: " << filename << std::endl;
		std::cout << "Quitting..." << std::endl;
		exit(0);
	}

	//since texture coordinates are upside-down in the build list function...
	image = corona::FlipImage(image, corona::CA_X);

	// Generate a texture
	glGenTextures(1, &newTextureID);

	// This sets the alignment requirements for the start of each pixel row in memory.
	glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

	// Bind the texture to newTextureID
	glBindTexture(GL_TEXTURE_2D, newTextureID);

	// Build Mipmaps (builds different versions of the picture for distances - looks better)
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->getWidth(), image->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->getPixels());

	// Lastly, we need to tell OpenGL the quality of our texture map.  GL_LINEAR_MIPMAP_LINEAR
	// is the smoothest.  GL_LINEAR_MIPMAP_NEAREST is faster than GL_LINEAR_MIPMAP_LINEAR, 
	// but looks blochy and pixilated.  Good for slower computers though. 
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);	

	// The default GL_TEXTURE_WRAP_S and ""_WRAP_T property is GL_REPEAT.
	// We need to turn this to GL_CLAMP_TO_EDGE, otherwise it creates ugly seems
	// in our sky box.  GL_CLAMP_TO_EDGE does not repeat when bound to an object.
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	// Now we need to free the image data that we loaded since OpenGL stored it as a texture
	delete image;

	return newTextureID;
}

bool SkyBox::Update(dReal deltaTime)
{
	//do nothing

	return true;
}