//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "Geometry.h"
#include "Permutations.h"
#include "ImageManagement.h"
#include "qamath.h"
#include "Transforms.h"
#include "TransformWrapper.h"
#include "RotTest.h"
#include <d3dm.h>
#include <tux.h>
#include "DebugOutput.h"

TESTPROCAPI RotationTest(LPDIRECT3DMOBILEDEVICE pDevice,
                         D3DMTRANSFORMSTATETYPE TransMatrix,
                         D3DQA_ROTTYPE RotType,
                         UINT uiFrameNumber,
                         D3DMFORMAT Format,
                         UINT uiTestID, 
                         WCHAR *wszImageDir,
                         UINT uiInstanceHandle,
                         HWND hWnd,
                         LPTESTCASEARGS pTestCaseArgs)
{
	//
	// Level of detail in sphere model
	//
	CONST UINT uiNumDivisions = 12;

    HRESULT hr;

	//
	// Rotation radians, per axis
	//
	float fX, fY, fZ;

	//
	// Temporary values used while computing per-axis rotation radians
	//
	float fAxis1, fAxis2;

	//
	// Number of axis, about which a particular rotation type rotates
	//
	UINT uiNumAxis;

	//
	// Each test case consists of multiple rendered frames
	//
	UINT uiFrame;

	//
	// Rotation matrix to be applied to Direct3D TnL
	//
	D3DMMATRIX RotMatrix;

	//
	// Interfaces for scene data
	//
	LPDIRECT3DMOBILEVERTEXBUFFER pVBSphere = NULL;
	LPDIRECT3DMOBILEINDEXBUFFER pIBSphere = NULL;

	//
	// All failure conditions set this to an error
	//
	INT iTestResult = TPR_PASS;

	//
	// Number of frames expected to be generated by this test case
	//
	UINT uiNumDesiredFrames;

	//
	// The geometry-generating algorithm reports it's "specs"; useful at
	// DrawPrimitive-time
	//
	D3DMPRIMITIVETYPE PrimType;
	UINT uiPrimCount;
	UINT uiNumVertices;

	UINT uiTotalFrames;

	//
	// Flat shading is in base profile
	//
	pDevice->SetRenderState( D3DMRS_SHADEMODE, D3DMSHADE_FLAT );

	//
	// Lighting is "baked" in verts
	//
	pDevice->SetRenderState( D3DMRS_LIGHTING, 0 );

	pDevice->SetRenderState(D3DMRS_CULLMODE, D3DMCULL_CW);

	//
	// Enable clipping
	//
	if( FAILED( hr = pDevice->SetRenderState(D3DMRS_CLIPPING, TRUE)))
	{
		DebugOut(DO_ERROR, L"SetRenderState(D3DMRS_CLIPPING,TRUE) failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = GetAxisCount(&uiNumAxis, RotType)))
	{
		DebugOut(DO_ERROR, L"GetNumberofAxis failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	//
	// Valid range is [1,3]
	//
	if (!((1 <= uiNumAxis) &&
	      (3 >= uiNumAxis)))
	{
		DebugOut(DO_ERROR, 
		    L"GetAxisCount resulted in invalid number of axis. (Expected 1, 2, or 3, Got %d) Failing.",
		    uiNumAxis);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	//
	// Retrieve expected number of frames for this kind of test case
	//
	uiNumDesiredFrames = NumTransformFramesByIters[uiNumAxis];

	if (FAILED(hr = GetPermutationSpecs(uiNumAxis,            // UINT uiNumIters,
	                                    uiNumDesiredFrames,   // UINT uiMaxIter,
	                                    &uiTotalFrames,       // UINT *puiNumStepsTotal,
	                                    NULL)))
	{
		DebugOut(DO_ERROR, L"GetPermutationSpecs failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	//
	// Confirm that test case can generate exactly the desired number of frames
	//
	if (uiNumDesiredFrames != uiTotalFrames)
	{
		DebugOut(DO_ERROR, 
		    L"Unexpected number of frames. (Expected %d, Got %d) Failing.",
		    uiNumDesiredFrames,
		    uiTotalFrames);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = GenerateSphere( pDevice,           // LPDIRECT3DMOBILEDEVICE pDevice,
	                                &pVBSphere,        // LPDIRECT3DMOBILEVERTEXBUFFER *ppVB,
	                                Format,            // D3DMFORMAT Format,
	                                &pIBSphere,        // LPDIRECT3DMOBILEINDEXBUFFER *ppIB, 
	                                uiNumDivisions,    // const int nDivisions
	                                &PrimType,         // D3DMPRIMITIVETYPE *pPrimType
	                                &uiPrimCount,      // PUINT puiPrimCount
	                                &uiNumVertices)))  // PUINT puiNumVertices
	{
		DebugOut(DO_ERROR, L"GenerateSphere failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	uiFrame = uiFrameNumber;

	if (FAILED(hr = SetTransformDefaults(pDevice))) // LPDIRECT3DMOBILEDEVICE pDevice
	{
		DebugOut(DO_ERROR, L"SetTransformDefaults failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	switch(uiNumAxis)
	{
	case 1:
		if (FAILED(hr = OneAxisPermutations(uiFrame,                 // UINT uiFrame,
			                                D3DQA_ROTFACTOR_MIN,     // float fMin,
			                                D3DQA_ROTFACTOR_MAX,     // float fMax,
			                                uiNumDesiredFrames,      // UINT uiMaxIters,
			                                &fAxis1)))               // float *pfAxis,
		{
			DebugOut(DO_ERROR, L"OneAxisPermutations failed. (hr = 0x%08x) Failing.", hr);
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		// Default all axis to no rotation
		fX = fY = fZ = 0.0f;

		if (UsesAxis(D3DQA_ROTAXIS_X, RotType))
			fX = fAxis1;

		if (UsesAxis(D3DQA_ROTAXIS_Y, RotType))
			fY = fAxis1;

		if (UsesAxis(D3DQA_ROTAXIS_Z, RotType))
			fZ = fAxis1;

		if (false == GetRotation(RotType,    // D3DQA_ROTTYPE rType,
			                     fX,         // const float fXRot,
			                     fY,         // const float fYRot,
			                     fZ,         // const float fZRot,
			                     &RotMatrix))// D3DMMATRIX* const RotMatrix,
		{
			DebugOut(DO_ERROR, L"GetRotation failed. Failing.");
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		break;
	case 2:
		if (FAILED(hr = TwoAxisPermutations(uiFrame,                 // UINT uiFrame,
			                                D3DQA_ROTFACTOR_MIN,     // float fMin,
			                                D3DQA_ROTFACTOR_MAX,     // float fMax,
			                                uiNumDesiredFrames,      // UINT uiMaxIters,
			                                &fAxis1,                 // float *pfAxis1,
			                                &fAxis2)))               // float *pfAxis2,
		{
			DebugOut(DO_ERROR, L"TwoAxisPermutations failed. (hr = 0x%08x) Failing.", hr);
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		// Default all axis to no rotation
		fX = fY = fZ = 0.0f;

		if (false == UsesAxis(D3DQA_ROTAXIS_X, RotType))
		{
			fY = fAxis1;
			fZ = fAxis2;
		}
		else if (false == UsesAxis(D3DQA_ROTAXIS_Y, RotType))
		{
			fX = fAxis1;
			fZ = fAxis2;
		}
		else if (false == UsesAxis(D3DQA_ROTAXIS_Z, RotType))
		{
			fX = fAxis1;
			fY = fAxis2;
		}
		else
		{
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		if (false == GetRotation(RotType,    // D3DQA_ROTTYPE rType,
			                     fX,         // const float fXRot,
			                     fY,         // const float fYRot,
			                     fZ,         // const float fZRot,
			                     &RotMatrix))// D3DMMATRIX* const RotMatrix,
		{
			DebugOut(DO_ERROR, L"GetRotation failed. Failing.");
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		break;
	case 3:
		if (FAILED(hr = ThreeAxisPermutations(uiFrame,                 // UINT uiFrame,
			                                  D3DQA_ROTFACTOR_MIN,     // float fMin,
			                                  D3DQA_ROTFACTOR_MAX,     // float fMax,
			                                  uiNumDesiredFrames,      // UINT uiMaxIters,
			                                  &fX,                     // float *pfX,
			                                  &fY,                     // float *pfY,
			                                  &fZ )))                  // float *pfZ
		{
			DebugOut(DO_ERROR, L"ThreeAxisPermutations failed. (hr = 0x%08x) Failing.", hr);
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		if (false == GetRotation(RotType,    // D3DQA_ROTTYPE rType,
			                     fX,         // const float fXRot,
			                     fY,         // const float fYRot,
			                     fZ,         // const float fZRot,
			                     &RotMatrix))   // D3DMMATRIX* const RotMatrix,
		{
			DebugOut(DO_ERROR, L"GetRotation failed. Failing.");
			iTestResult = TPR_FAIL;
			goto cleanup;
		}

		break;
	default:
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = pDevice->SetTransform(TransMatrix,  // D3DMTRANSFORMSTATETYPE State,
		                                  &RotMatrix, D3DMFMT_D3DMVALUE_FLOAT))) // CONST D3DMMATRIX *pMatrix
	{
		DebugOut(DO_ERROR, L"SetTransform failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	//
	// Clear the backbuffer and the zbuffer
	//
	pDevice->Clear( 0, NULL, D3DMCLEAR_TARGET|D3DMCLEAR_ZBUFFER,
						D3DMCOLOR_XRGB(0,0,0), 1.0f, 0 );

	if (FAILED(hr = pDevice->BeginScene()))
	{
		DebugOut(DO_ERROR, L"BeginScene failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = pDevice->DrawIndexedPrimitive(
						 PrimType, // D3DMPRIMITIVETYPE Type,
						 0,        // INT BaseVertexIndex,
						 0,        // UINT MinIndex,
						 uiNumVertices, // UINT NumVertices,
						 0,        // UINT StartIndex,
						 uiPrimCount))) // UINT PrimitiveCount
	{
		DebugOut(DO_ERROR, L"DrawIndexedPrimitive failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = pDevice->EndScene()))
	{
		DebugOut(DO_ERROR, L"EndScene failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	if (FAILED(hr = pDevice->Present(NULL, NULL, NULL, NULL)))
	{
		DebugOut(DO_ERROR, L"Present failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

	//
	// Flush the swap chain and capture a frame
	//
	if (FAILED(hr = DumpFlushedFrame(pTestCaseArgs, // LPTESTCASEARGS pTestCaseArgs
	                                 0,             // UINT uiFrameNumber,
	                                 NULL)))        // RECT *pRect = NULL
	{
		DebugOut(DO_ERROR, L"DumpFlushedFrame failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
		goto cleanup;
	}

cleanup:

	if (FAILED(hr = pDevice->SetIndices(NULL)))
	{
		DebugOut(DO_ERROR, L"SetIndices failed. (hr = 0x%08x) Failing.", hr);
		iTestResult = TPR_FAIL;
	}

	if (pVBSphere)
		pVBSphere->Release();

	if (pIBSphere)
		pIBSphere->Release();

	return iTestResult;
}


