/**
 * Initializes a 2d camera and sets it in a position. In order to display on all of the
 * screen's area this position is usually defined as the middle of the screen.
 * @param pX				X Position of the camera.
 * @param pY				Y Position of the camera.
 */
IND_Camera2d::IND_Camera2d(float pX, float pY) {
	_pos._x = pX;
	_pos._y = pY;
	_pos._z = 0.5f;

	_look   = IND_Vector3(0.0f, 0.0f, -1.0f); 
	_up     = IND_Vector3(0.0f, -1.0f, 0.0f); 
	_right  = IND_Vector3(1.0f, 0.0f, 0.0f);
	
	_zoom = 1.0f;
	_angle = 0.0f;
	_prevAngle = 0.0f;
}
/*!
\b Parameters:

\arg \b pEyeX, pEyeY, pEyeZ                         Position of the camera
\arg \b pLookAtX, pLookAtY, pLookAtZ                Vector defining the direction of the camera
\arg \b pUpX, pUpY, pUpZ                            Vector "up" of the camera.

Operation:

This function sets a perspective matrix through the definition of a position and two vectors (lookat and up).

Using this method is equivalent to using a combination of methods of the class ::setCamera3d().
*/
void OpenGLRender::lookAt(float pEyeX, float pEyeY, float pEyeZ,
                          float pLookAtX, float pLookAtY, float pLookAtZ,
                          float pUpX, float pUpY, float pUpZ) {
    
                                 
    //Build the view matrix from given vectors
	IND_Matrix lookatmatrix; 
	_math.matrix4DLookAtMatrixEyeLookUpLH(IND_Vector3(pEyeX,pEyeY,pEyeZ),
	                           IND_Vector3(pLookAtX,pLookAtY,pLookAtZ),
	                           IND_Vector3(pUpX,pUpY,pUpZ),
	                           lookatmatrix);

#ifdef _DEBUG
	int mmode;
	glGetIntegerv(GL_MATRIX_MODE,&mmode);
	assert( mmode == GL_MODELVIEW);
#endif
    glLoadIdentity();
    glMultMatrixf(reinterpret_cast<GLfloat *>(&lookatmatrix));
}
void OpenGLES2Render::setTransform2d(int pX,
                                  int pY,
                                  float pAngleX,
                                  float pAngleY,
                                  float pAngleZ,
                                  float pScaleX,
                                  float pScaleY,
                                  int pAxisCalX,
                                  int pAxisCalY,
                                  bool pMirrorX,
                                  bool pMirrorY,
                                  int pWidth,
                                  int pHeight,
                                  IND_Matrix *pMatrix) {

	//Temporal holders for all accumulated transforms
	IND_Matrix totalTrans;
	_math.matrix4DSetIdentity(totalTrans);
	IND_Matrix temp;
	_math.matrix4DSetIdentity(temp);

	//Initialize to identity given matrix, if exists
	if (pMatrix) {
		_math.matrix4DSetIdentity(*pMatrix);
	}
    
	// Translations
	if (pX != 0 || pY != 0) {
		IND_Matrix trans;
		_math.matrix4DSetTranslation(trans,static_cast<float>(pX),static_cast<float>(pY),0.0f);
		_math.matrix4DMultiply(totalTrans,trans,temp);
		totalTrans = temp;
	}

	// Scaling
	if (pScaleX != 1.0f || pScaleY != 1.0f) {
		IND_Matrix scale;
		_math.matrix4DSetScale(scale,pScaleX,pScaleY,0.0f);
		_math.matrix4DMultiply(totalTrans,scale,temp);
		totalTrans = temp;
	}

	// Rotations
	if (pAngleX != 0.0f) {
		IND_Matrix angleX;
		_math.matrix4DSetRotationAroundAxis(angleX,pAngleX,IND_Vector3(1.0f,0.0f,0.0f));
		_math.matrix4DMultiply(totalTrans,angleX,temp);
		totalTrans = temp;
	}

	if (pAngleY != 0.0f) {
		IND_Matrix angleY;
		_math.matrix4DSetRotationAroundAxis(angleY,pAngleY,IND_Vector3(0.0f,1.0f,0.0f));
		_math.matrix4DMultiply(totalTrans,angleY,temp);
		totalTrans = temp;
	}

	if (pAngleZ != 0.0f) {
		IND_Matrix angleZ;
		_math.matrix4DSetRotationAroundAxis(angleZ,pAngleZ,IND_Vector3(0.0f,0.0f,1.0f));
		_math.matrix4DMultiply(totalTrans,angleZ,temp);
		totalTrans = temp;
	}

	// Hotspot - Add hotspot to make all transforms to be affected by it
	if (pAxisCalX != 0 || pAxisCalY != 0) {
		IND_Matrix hotspot;
		_math.matrix4DSetTranslation(hotspot,static_cast<float>(pAxisCalX),static_cast<float>(pAxisCalY),0.0f);
		_math.matrix4DMultiply(totalTrans,hotspot,temp);
		totalTrans = temp;
	}

    // Mirroring (180º rotations) and translation
	if (pMirrorX || pMirrorY) {
		//A mirror is a rotation in desired axis (the actual mirror) and a repositioning because rotation
		//also moves 'out of place' the entity translation-wise
		if (pMirrorX) {
			IND_Matrix mirrorX;
            //After rotation around origin, move back texture to correct place
            _math.matrix4DSetTranslation(mirrorX,
                                         static_cast<float>(pWidth),
                                         0.0f,
                                         0.0f);
			_math.matrix4DMultiply(totalTrans,mirrorX,temp);
			totalTrans = temp;
            
            //Rotate in y, to invert texture
			_math.matrix4DSetRotationAroundAxis(mirrorX,180.0f,IND_Vector3(0.0f,1.0f,0.0f));
			_math.matrix4DMultiply(totalTrans,mirrorX,temp);
			totalTrans = temp;
		}
        
		//A mirror is a rotation in desired axis (the actual mirror) and a repositioning because rotation
		//also moves 'out of place' the entity translation-wise
		if (pMirrorY) {
			IND_Matrix mirrorY;
            //After rotation around origin, move back texture to correct place
            _math.matrix4DSetTranslation(mirrorY,
                                         0.0f,
                                         static_cast<float>(pHeight),
                                         0.0f);
			_math.matrix4DMultiply(totalTrans,mirrorY,temp);
			totalTrans = temp;
            
            //Rotate in x, to invert texture
			_math.matrix4DSetRotationAroundAxis(mirrorY,180.0f,IND_Vector3(1.0f,0.0f,0.0f));
			_math.matrix4DMultiply(totalTrans,mirrorY,temp);
			totalTrans = temp;
		}
	}
	//Cache the change
	_modelToWorld = totalTrans;

	//Apply the changes to the GL matrix stack (model view)
    //Camera transform
    float camMatrixArray [16];
    _cameraMatrix.arrayRepresentation(camMatrixArray);
    glLoadMatrixf(camMatrixArray);
    
    //Actual object transform
    float matrixArray [16];
    totalTrans.arrayRepresentation(matrixArray);
	glMultMatrixf(matrixArray);

	// ----- Return World Matrix (in IndieLib format) ----
	//Transformations have been applied where needed
	if (pMatrix) {
		*pMatrix = totalTrans;
	}
}
/*!
\b Parameters:

\arg \b pCamera3d               ::IND_Camera3d object that defines a camera.

\b Operation:

This function sets a 3d camera. See the methods of ::IND_Camera3d for information on how you can manipulate the camera.
*/
void DirectXRender::setCamera3d(IND_Camera3d *pCamera3d) {
	D3DXMATRIX mTrans, mMatView, mMatProjection;
	D3DXMatrixIdentity(&mMatView);
	D3DXMatrixIdentity(&mMatProjection);

	// ----- View matrix -----

	pCamera3d->_up      = IND_Vector3 (0.0f, 1.0f, 0.0f);
	pCamera3d->_look    = IND_Vector3 (0.0f, 0.0f, 1.0f);
	pCamera3d->_right   = IND_Vector3 (1.0f, 0.0f, 0.0f);
    //Buffer D3DVec3 structs from our camera 3d vectors
    D3DXVECTOR3 d3dpos (pCamera3d->_pos._x,pCamera3d->_pos._y,pCamera3d->_pos._z);
    D3DXVECTOR3 d3dlook (pCamera3d->_look._x,pCamera3d->_look._y, pCamera3d->_look._z);
    D3DXVECTOR3 d3dup (pCamera3d->_up._x,pCamera3d->_up._y, pCamera3d->_up._z);
    D3DXVECTOR3 d3dright (pCamera3d->_right._x,pCamera3d->_right._y, pCamera3d->_right._z);

	// Yaw is rotation around the y axis (_up)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX yawMatrix;
	D3DXMatrixRotationAxis(&yawMatrix, &d3dup, D3DXToRadian(pCamera3d->_yaw));
	// To apply yaw we rotate the _look & _right vectors about the _up vector (using our yaw matrix)
	D3DXVec3TransformCoord(&d3dlook, &d3dlook, &yawMatrix);
	D3DXVec3TransformCoord(&d3dright, &d3dright, &yawMatrix);

	// Pitch is rotation around the x axis (_right)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX pitchMatrix;
	D3DXMatrixRotationAxis(&pitchMatrix, &d3dright, D3DXToRadian(pCamera3d->_pitch));
	// To apply pitch we rotate the _look and _up vectors about the _right vector (using our pitch matrix)
	D3DXVec3TransformCoord(&d3dlook, &d3dlook, &pitchMatrix);
	D3DXVec3TransformCoord(&d3dup, &d3dup, &pitchMatrix);

	// Roll is rotation around the z axis (_look)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX rollMatrix;
	D3DXMatrixRotationAxis(&rollMatrix, &d3dlook, D3DXToRadian(pCamera3d->_roll));
	// To apply roll we rotate up and right about the look vector (using our roll matrix)
	// Note: roll only really applies for things like aircraft unless you are implementing lean
	D3DXVec3TransformCoord(&d3dright, &d3dright, &rollMatrix);
	D3DXVec3TransformCoord(&d3dup, &d3dup, &rollMatrix);

	// Build the view matrix from the transformed camera axis
	mMatView._11 = pCamera3d->_right._x;
	mMatView._12 = pCamera3d->_up._x;
	mMatView._13 = pCamera3d->_look._x;
	mMatView._21 = pCamera3d->_right._y;
	mMatView._22 = pCamera3d->_up._y;
	mMatView._23 = pCamera3d->_look._y;
	mMatView._31 = pCamera3d->_right._z;
	mMatView._32 = pCamera3d->_up._z;
	mMatView._33 = pCamera3d->_look._z;

	mMatView._41 = - D3DXVec3Dot(&d3dright, &d3dright);
	mMatView._42 = - D3DXVec3Dot(&d3dright, &d3dup);
	mMatView._43 = - D3DXVec3Dot(&d3dright, &d3dlook);

	// ---- Zoom ----

	D3DXMatrixScaling(&mTrans, pCamera3d->_zoom, pCamera3d->_zoom, pCamera3d->_zoom);
	D3DXMatrixMultiply(&mMatView, &mTrans, &mMatView);

	_info._device->SetTransform(D3DTS_VIEW, &mMatView);

	// ----- Projection matrix -----

	// Fov projection
	if (!pCamera3d->isOrthoProjection()) {
		D3DXMatrixPerspectiveFovLH(&mMatProjection,                         // output
		                           pCamera3d->_fov,                        // Fov vertical
		                           pCamera3d->_aspect,                     // Relación de aspecto del viewport
		                           pCamera3d->_nearClippingPlane,          // Near clipping plane z
		                           pCamera3d->_farClippingPlane);          // Far clipping  plane z
	}
	// Orthographic projection
	else {
		D3DXMatrixOrthoLH(&mMatProjection, pCamera3d->_orthoWidth, pCamera3d->_orthoHeight, pCamera3d->_nearClippingPlane, pCamera3d->_farClippingPlane);
	}

	_info._device->SetTransform(D3DTS_PROJECTION, &mMatProjection);
}
TEST_FIXTURE(INDMathTests,RotateVector) {
	//Rotation matrix
	IND_Matrix mrot;
	//Vector to rotate
	IND_Vector3 vec;

    //------------Rotation around Z axis-------------------------------
	//90 degress around z axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
    math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 0.0f, 1.0f));
	
    //Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(1.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around z axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 0.0f, 1.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.7f, vec._x, 0.01f);
	CHECK_CLOSE(0.7f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

	//90 degress around z axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 0.0f, 1.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(-1.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around z axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 0.0f, 1.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(-0.7f, vec._x, 0.01f);
	CHECK_CLOSE(0.7f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

	//90 degress around z axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 0.0f, 1.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(1.0f, vec._z, 0.01f);

    //45 degress around z axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 0.0f, 1.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(1.0f, vec._z, 0.01f);

    //------------Rotation around Y axis-------------------------------
	//90 degress around y axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(-1.0f, vec._z, 0.01f);

    //45 degress around y axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.7f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(-0.7f, vec._z, 0.01f);

	//90 degress around y axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(1.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around y axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(1.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);


    //90 degress around y axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(1.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around y axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(0.0f, 1.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.7f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.7f, vec._z, 0.01f);

    //------------Rotation around X axis-------------------------------
    //90 degress around x axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(1.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around x axis (x axis vector)
	vec = IND_Vector3(1.0f, 0.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(1.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

	//90 degress around x axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.0f, vec._y, 0.01f);
	CHECK_CLOSE(1.0f, vec._z, 0.01f);

    //45 degress around x axis (y axis vector)
	vec = IND_Vector3(0.0f, 1.0f, 0.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(0.7f, vec._y, 0.01f);
	CHECK_CLOSE(0.7f, vec._z, 0.01f);
	
    //90 degress around x axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 90.f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(-1.0f, vec._y, 0.01f);
	CHECK_CLOSE(0.0f, vec._z, 0.01f);

    //45 degress around x axis (z axis vector)
	vec = IND_Vector3(0.0f, 0.0f, 1.0f);
	math->matrix4DSetRotationAroundAxis(mrot, 45.0f, IND_Vector3(1.0f, 0.0f, 0.0f));

	//Rotation
	math->transformVector3DbyMatrix4D(vec, mrot);

	CHECK_CLOSE(0.0f, vec._x, 0.01f);
	CHECK_CLOSE(-0.7f, vec._y, 0.01f);
	CHECK_CLOSE(0.7f, vec._z, 0.01f);
}