BOOL IFXSubdivisionManager::CheckRangeAndScaling (FloatProperty property,   F32 *pfValue)
{
	// Do Property specify evaluation if necessary:
	if (property == SurfaceTension)
	{
		// Check range:
		if (*pfValue < m_fMinSurfaceTensionParam)
			*pfValue = m_fMinSurfaceTensionParam;
		if (*pfValue > m_fMaxSurfaceTensionParam)
			*pfValue = m_fMaxSurfaceTensionParam;

		m_pButterflyScheme->SetSmoothingFactor(*pfValue);

		m_bUpdateRequired = IFX_TRUE;
	}

	return IFX_TRUE;
}

#include "IFXSubdivisionManager.h"
#include "IFXScreenSpaceMetric.h"
#include "IFXCoreCIDs.h"
#include "IFXMesh.h"

const F32 IFXSubdivisionManager::m_fMaxSurfaceTensionParam = 1.0f;
const F32 IFXSubdivisionManager::m_fMinSurfaceTensionParam = 0.0f;
const U32 IFXSubdivisionManager::m_uMaxBaseMeshSize = 5000;

U32 IFXGetMaxBaseMeshSize()
{
	return IFXSubdivisionManager::m_uMaxBaseMeshSize;
}

IFXSubdivisionManager::IFXSubdivisionManager()
{
	m_uNumBaseTriangles = 0;
	m_uNumBaseVertices  = 0;
	m_pBaseTriangle                 = NULL;
	m_ppBaseVertex                   = NULL;

	m_pTriangleAllocator            = NULL;
	m_pVertexAllocator        = NULL;

	m_pOutputMeshGrp                = NULL;

	SetDefaultProperties();

	// Initialize butterfly scheme:
	m_pButterflyScheme              = new IFXButterflyScheme;
	m_pButterflyScheme->SetSmoothingFactor(m_pfFloat[SurfaceTension]);
	m_pButterflyScheme->SetSubdivMgr(this);
}

IFXSubdivisionManager::~IFXSubdivisionManager()
{
	DeallocateOutputMesh();
	m_pOutputMeshGrp = NULL;

	DeallocateFreeLists();

	if (m_pBaseTriangle)    delete [] m_pBaseTriangle;
	if (m_ppBaseVertex)      delete []   m_ppBaseVertex;
	m_pBaseTriangle     = NULL;
	m_ppBaseVertex      = NULL;

	if (m_pButterflyScheme)
	{
		delete m_pButterflyScheme;
		m_pButterflyScheme = NULL;
	}
}

void IFXSubdivisionManager::SetDefaultProperties()
{
	// Boolean properties:

	m_pbBoolean[Adaptive]               = IFX_FALSE;

	m_pbBoolean[CrackFilling]           = IFX_TRUE;

	m_pbBoolean[NormalSubdivision]      = IFX_TRUE;
	m_pbBoolean[TexCoordSubdivision]    = IFX_FALSE;
	m_pbBoolean[LazyEvaluation]     = IFX_TRUE;

	// Integer properties:
	m_puInteger[MaxComputeDepth]            = 5;
	m_puInteger[MaxRenderDepth]             = 5;
	m_puInteger[CurrentComputeDepth]        = 0;

	m_puInteger[MaxTriangleAllocation]      = 600000;
	m_puInteger[MaxVertexAllocation]        = 400000;

	m_puInteger[InitialTriangleAllocation]  = 5000;
	m_puInteger[InitialVertexAllocation]    = 2500;

	m_puInteger[GrowTriangleAllocation]     = 1000;
	m_puInteger[GrowVertexAllocation]       = 500;

	m_puInteger[LazyEvaluationLimit]    = 1;

	// Floating point properties:
	m_pfFloat[SurfaceTension]               = 0.65f;

	// Other properties:
	m_pAdaptiveMetric                       = NULL;

	m_bDontUpdate                           = IFX_FALSE;
	m_bUpdateRequired           = IFX_TRUE;

	// OutputMesh allocation mgt:
	m_uOutMshInitial_ratio          = 0.020f; // Percentatge of total allowed allocation to initially allocate.
	m_numOutVertsIncr           = 3000;   // # of vertices by which we grow output meshes:
	m_numOutFacesIncr           = 5000;   // # of faces by which we grow output meshses:
	m_bReallocateOutputMesh         = IFX_FALSE;
	m_uNumOutMeshes             = 0;
	m_pOutMshVtxCnt             = NULL;
}

IFXRESULT IFXSubdivisionManager::AllocateFreeLists()
{
	// Create triangle and vertex allocators, allocate inital space:
	m_pTriangleAllocator  = new IFXTQTTriangleAllocator
		(m_puInteger[InitialTriangleAllocation], m_puInteger[GrowTriangleAllocation]);

	m_pVertexAllocator    = new IFXVertexAllocator
		(m_puInteger[InitialVertexAllocation], m_puInteger[GrowVertexAllocation]);

	if (!m_pTriangleAllocator || !m_pVertexAllocator)
		return IFX_E_OUT_OF_MEMORY;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::DeallocateFreeLists()
{
	if (m_pTriangleAllocator)           delete m_pTriangleAllocator;
	m_pTriangleAllocator            = NULL;

	if (m_pVertexAllocator)   delete m_pVertexAllocator;
	m_pVertexAllocator    = NULL;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::AllocateOutputMesh(IFXMeshGroup *pInputMeshGrp)
{
	IFXASSERT (m_pOutputMeshGrp == NULL);

	// Allocate the necessary outputmeshes:
	IFXCreateComponent(CID_IFXMeshGroup, IID_IFXMeshGroup, (void**)&m_pOutputMeshGrp);
	m_pOutputMeshGrp->Allocate(pInputMeshGrp->GetNumMeshes());

	m_uNumOutMeshes = pInputMeshGrp->GetNumMeshes();
	m_pOutMshVtxCnt = new U32 [m_uNumOutMeshes];

	// Iterate over each output mesh:
	U32 uMeshNum;
	for (uMeshNum = 0; uMeshNum < m_uNumOutMeshes; uMeshNum++)
	{
		// Get the input IFXMesh
		IFXMesh *pMeshIn          = 0;
		pInputMeshGrp->GetMesh(uMeshNum, pMeshIn);
		IFXVertexAttributes attributeFlags  = pMeshIn->GetAttributes();

		// Figure out what percentage of the total mesh group this one mesh represents:
		// Store the vertex mesh count incase of need to reallocate the output mesh later.
		m_pOutMshVtxCnt[uMeshNum] = pMeshIn->GetNumVertices();
		F32 fShare = (((F32) m_pOutMshVtxCnt[uMeshNum] ) / ((F32) m_uNumBaseVertices));
		U32 uNumAllocatedVertices = (U32) (fShare * m_uOutMshInitial_ratio * m_puInteger[MaxVertexAllocation]);
		U32 uNumAllocatedFaces    = (U32) (fShare * m_uOutMshInitial_ratio * m_puInteger[MaxTriangleAllocation]);

		// Allocate an output IFXMesh:
		IFXMesh *pMeshNew = 0;
		IFXCreateComponent(CID_IFXMesh, IID_IFXMesh, (void**)&pMeshNew);

		// Now allocate the output vertex and face data, plug into the mesh group:
		pMeshNew->Allocate(attributeFlags, uNumAllocatedVertices, uNumAllocatedFaces);
		m_pOutputMeshGrp->SetMesh(uMeshNum, pMeshNew);
		IFXRELEASE(pMeshIn);
		IFXRELEASE(pMeshNew);
	}

	m_bReallocateOutputMesh = IFX_FALSE;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::ReallocateOutputMesh ()
{
	IFXRESULT rc = IFX_OK;
	// Iterate over each input mesh:
	U32 uMeshNum;
	for (uMeshNum = 0; uMeshNum < m_uNumOutMeshes; uMeshNum++)
	{
		// Figure out what percentage of the total allowable mesh group dimensions
		// this one mesh represents:
		F32 fShare = ((F32) m_pOutMshVtxCnt[uMeshNum]) / ((F32) m_uNumBaseVertices);
		U32 uNumAllocatedVertices = (U32) (fShare * m_puInteger[MaxVertexAllocation]);
		U32 uNumAllocatedFaces    = (U32) (fShare * m_puInteger[MaxTriangleAllocation]);

		// See if each output mesh is out of space, if so, then re-allocate
		IFXMesh *pMeshOut = 0;
		m_pOutputMeshGrp->GetMesh(uMeshNum, pMeshOut);
		if (IFXSUCCESS(rc) && ((pMeshOut->GetNumVertices() == pMeshOut->GetMaxNumVertices()) ||
			(pMeshOut->GetNumFaces() == pMeshOut->GetMaxNumFaces())))

		{
			U32 uNumFacesIncreased = 2* pMeshOut->GetNumFaces();// + m_numOutFacesIncr;
			U32 uNumVertsIncreased = 2* pMeshOut->GetNumVertices();// + m_numOutVertsIncr;

			if ((uNumFacesIncreased < uNumAllocatedFaces) &&
				(uNumVertsIncreased < uNumAllocatedVertices))
			{
				rc = pMeshOut->IncreaseSize(uNumFacesIncreased, uNumVertsIncreased);
			}
			else
				rc = IFX_E_UNDEFINED;
		}

		IFXRELEASE(pMeshOut);

	}

	m_bReallocateOutputMesh = IFX_FALSE;

	return rc;
}

IFXRESULT IFXSubdivisionManager::DeallocateOutputMesh()
{
	IFXRELEASE(m_pOutputMeshGrp);

	if (m_pOutMshVtxCnt)
	{
		delete m_pOutMshVtxCnt;
		m_pOutMshVtxCnt = NULL;
	}

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::SetBool  (BooleanProperty property,  BOOL bValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_BOOLEAN_PROPERTIES)
		return IFX_E_UNDEFINED;

	m_pbBoolean[property] = bValue;

	if (property == Adaptive)
	{
		if (bValue == IFX_FALSE)
		{
			m_bUpdateRequired = IFX_TRUE;
			ResetAll();
		}
	}

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::GetBool  (BooleanProperty property,  BOOL *pbValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_BOOLEAN_PROPERTIES)
		return IFX_E_UNDEFINED;

	if (!pbValue)
		return IFX_E_UNDEFINED;

	*pbValue = m_pbBoolean[property];

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::SetInteger (IntegerProperty property,  U32 uValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_INTEGER_PROPERTIES)
		return IFX_E_UNDEFINED;

	if (property == MaxComputeDepth)
	{
		if (m_puInteger[MaxComputeDepth] != uValue)
			m_bUpdateRequired = IFX_TRUE;

	}

	m_puInteger[property] = uValue;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::GetInteger (IntegerProperty property,  U32 *puValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_INTEGER_PROPERTIES)
		return IFX_E_UNDEFINED;

	if (!puValue)
		return IFX_E_UNDEFINED;

	*puValue = m_puInteger[property];

	return IFX_OK;
}

// Returns IFX_TRUE if floating point value is ok.

IFXRESULT IFXSubdivisionManager::SetFloat (FloatProperty property,  F32 fValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_FLOAT_PROPERTIES)
		return IFX_E_UNDEFINED;

	CheckRangeAndScaling (property, &fValue);

	m_pfFloat[property] = fValue;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::GetFloat (FloatProperty property,  F32 *pfValue)
{
	// Validate parameters:
	if (property >= MAX_NUM_FLOAT_PROPERTIES)
		return IFX_E_UNDEFINED;

	if (!pfValue)
		return IFX_E_UNDEFINED;

	*pfValue = m_pfFloat[property];

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::SetAdaptiveMetric (IFXAdaptiveMetric *pAdaptiveMetric)
{
	if (!pAdaptiveMetric)
		return IFX_E_UNDEFINED;

	m_pAdaptiveMetric = pAdaptiveMetric;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::ConsolidateLevel (U32 uLevel)
{
	if (m_bDontUpdate)
		return IFX_OK;

	m_bDontUpdate = IFX_TRUE;

	U32 f;
	for (f = 0; f < m_uNumBaseTriangles; f++)
	{
		m_pBaseTriangle[f].ConsolidateLevel(uLevel);
	}

	m_bDontUpdate = IFX_FALSE;

	return IFX_OK;
}

void IFXSubdivisionManager::Update()
{
	// If max depth is 0, don't bother doing anything:

	U32 f;

	if (m_pbBoolean[Adaptive])
	{
		// 1) Apply user specified adaptive subdiv metric and subdivide if directed:
		for (f = 0; f < m_uNumBaseTriangles; f++)
			m_pBaseTriangle[f].BreadthFirstEvaluate(this);

		// 2) Consolidate any triangles marked for consolidation:
		for (f = 0; f < m_uNumBaseTriangles; f++)
			m_pBaseTriangle[f].Consolidate(this);

		// 3) Generate any required crack fill triangles:
		if (m_pbBoolean[CrackFilling] && m_puInteger[MaxRenderDepth])
			for (f = 0; f < m_uNumBaseTriangles; f++)
				m_pBaseTriangle[f].FindCracks(this);
	}
	else // Subdivide uniformly:
	{
		if (m_puInteger[CurrentComputeDepth] == m_puInteger[MaxComputeDepth])
			return;

		for (f = 0; f < m_uNumBaseTriangles; f++)
			m_pBaseTriangle[f].SubdivideLeaves(this);

		if (m_puInteger[CurrentComputeDepth] > m_puInteger[MaxComputeDepth])
			m_puInteger[CurrentComputeDepth]--;
		else
			m_puInteger[CurrentComputeDepth]++;

	}
}

// Treverse the mesh group and count up all of the vertices and faces.
// Then allocate base mesh data of the total size.
void IFXSubdivisionManager::AllocateBaseMesh (IFXMeshGroup *pMeshGrp, U32 *pFaceOffsetTable)
{
	U32 uMeshNum;

	m_uNumBaseTriangles = 0;
	m_uNumBaseVertices  = 0;

	for (uMeshNum = 0; uMeshNum < pMeshGrp->GetNumMeshes(); uMeshNum++)
	{
		pFaceOffsetTable[uMeshNum] = m_uNumBaseTriangles;

		// Get the 1st mesh:
		IFXMesh  *pMesh = 0;
		pMeshGrp->GetMesh(uMeshNum, pMesh);

		// Copy dimensions
		m_uNumBaseTriangles += pMesh->GetNumFaces();
		m_uNumBaseVertices  += pMesh->GetNumVertices();

		IFXRELEASE(pMesh);
	}

	// Allocate memory for base triangle and base corners:
	m_pBaseTriangle = new  IFXTQTBaseTriangle [m_uNumBaseTriangles]  ;
	m_ppBaseVertex  = new  IFXTQTVertex* [m_uNumBaseVertices];

	// Initialize all base vertex pointers to null:
	U32 bv;
	for (bv= 0; bv < m_uNumBaseVertices; bv++)
		m_ppBaseVertex[bv] = NULL;
}

void IFXSubdivisionManager::CopyVertexData(IFXMeshGroup *pMeshGrp)
{
	IFXASSERT(pMeshGrp);
	// Traverse each mesh, collecting faces and vertices to initilize the base mesh:
	U32 uMeshNum;
	U32 uVertexOffset = 0;
	U32 uNumberOfMeshes = pMeshGrp->GetNumMeshes();
	for (uMeshNum = 0; uMeshNum < uNumberOfMeshes; uMeshNum++)
	{
		// Get a mesh:
		IFXMesh *pMesh = 0;
		pMeshGrp->GetMesh(uMeshNum, pMesh);
		IFXVertexAttributes vAttributes = pMesh->GetAttributes();

		U32 uNumMeshVertices  = pMesh->GetNumVertices();

		// Get a vertex iterator to the mesh vertices:
		IFXVertexIter vertexIter;
		pMesh->GetVertexIter(vertexIter);

		// 1) Copy vertex data, full version:
		U32 v;
		for (v = 0; v < uNumMeshVertices; v++)
		{
			// Allocate a base vertex, if not already allocated:
			if (NULL == m_ppBaseVertex[uVertexOffset + v])
			{
				m_ppBaseVertex[uVertexOffset + v] = m_pVertexAllocator->Allocate();
			}

			m_ppBaseVertex[uVertexOffset + v]->m_position =  *vertexIter.GetPosition();
			m_ppBaseVertex[uVertexOffset + v]->m_normal =  *vertexIter.GetNormal();
			if (vAttributes.m_uData.m_uNumTexCoordLayers)
			{
				m_ppBaseVertex[uVertexOffset + v]->m_texCoord = *vertexIter.GetTexCoord();
			}
			vertexIter.Next();
		}

		// Update offsets:
		uVertexOffset += uNumMeshVertices;

		IFXRELEASE(pMesh);
	} // End of mesh traversal.

	m_bUpdateRequired = IFX_TRUE;
}

IFXRESULT IFXSubdivisionManager::InitMesh( IFXMeshGroup*    pMeshGrp,
										  IFXNeighborMesh* pNeighborMesh )
{
	if ( (!pMeshGrp) || (!pNeighborMesh) )
		return IFX_E_INVALID_POINTER;

	// If we have already done InitMesh(), we should be done.
	if(m_pBaseTriangle)
		return IFX_OK;

	// Make sure that all attribute discontinuites are marked in the neighbor mesh:
	IFXRESULT result = pNeighborMesh->MarkAttributeDiscontinuities(*pMeshGrp);

	U32 uNumberOfMeshes = pMeshGrp->GetNumMeshes();
	U32 *pFaceOffsetTable = new U32 [ uNumberOfMeshes * sizeof (U32)]; /// @todo: Should sizeof U32 be here?

	if (!pFaceOffsetTable)
	{
		return IFX_E_UNDEFINED;
	}

	// Allocate free lists of triangles and vertices:
	result = AllocateFreeLists();
	if (result != IFX_OK)
	{
		IFXDELETE_ARRAY( pFaceOffsetTable );
		return result;
	}

	// Determine size and allocate space for the base (control) mesh:
	AllocateBaseMesh (pMeshGrp, pFaceOffsetTable);

	// Big base meshes aren't worth subdividing and eat too much memory:
	if (m_uNumBaseTriangles > m_uMaxBaseMeshSize)
	{
		IFXDELETE_ARRAY( pFaceOffsetTable );
		return IFX_E_SUBDIVMGR_LARGE_MESHES_UNSUPPORTED;
	}

	// copy the vertex data to initialize the base mesh.
	// note that this originally was part of the loop below
	// but by encapsulating it, this functionality can be
	// re-used to update a dynamically changing mesh.

	CopyVertexData(pMeshGrp);

	// Traverse each mesh, collecting faces to initilize the base mesh:
	U32 uMeshNum;
	U32 uVertexOffset = 0;
	U32 uFaceOffset   = 0;
	for (uMeshNum = 0; uMeshNum < uNumberOfMeshes; uMeshNum++)
	{
		// Get a mesh:
		IFXMesh *pMesh = 0;
		pMeshGrp->GetMesh(uMeshNum, pMesh);

		U32 uNumMeshFaces   = pMesh->GetNumFaces();
		U32 uNumMeshVertices  = pMesh->GetNumVertices();

		IFXFaceIter faceIter;
		pMesh->GetFaceIter(faceIter);

		IFXTQTAddress emptyAddress;
		emptyAddress.Reset();

		IFXNeighborFace *pNeighborFaceArray = pNeighborMesh->GetNeighborFaceArray(uMeshNum);

		IFXFace*            pFace = NULL;
		IFXNeighborFace*    pNeighborFace = NULL;
		U32                 uMeshIndex[3], puNeighborFaceIndex[3];
		IFXCornerFlags*     ppNeighborFlags[3] = {NULL, NULL, NULL};
		IFXTQTBaseTriangle* pNeighborTriangles[3] = {NULL, NULL, NULL};

		// 2) Copy face data:
		U32 f;
		for (f = 0; f < uNumMeshFaces; f++)
		{
			pFace = faceIter.Next();

			U32 a,b,c;
			a = pFace->VertexA();
			b = pFace->VertexB();
			c = pFace->VertexC();

			m_pBaseTriangle[uFaceOffset + f].Init
				(&m_pBaseTriangle[uFaceOffset + f], 0, emptyAddress, IFXTQTTriangle::TipUp,
				m_ppBaseVertex[uVertexOffset + a],
				m_ppBaseVertex[uVertexOffset + b],
				m_ppBaseVertex[uVertexOffset + c]);

			// Grab the 3 neighbor indices:
			pNeighborFace = &pNeighborFaceArray[f];

			pNeighborFace->GetNeighbor(0, &uMeshIndex[0], &puNeighborFaceIndex[0],
				&ppNeighborFlags[0]);
			pNeighborFace->GetNeighbor(1, &uMeshIndex[1], &puNeighborFaceIndex[1],
				&ppNeighborFlags[1]);
			pNeighborFace->GetNeighbor(2, &uMeshIndex[2], &puNeighborFaceIndex[2],
				&ppNeighborFlags[2]);

			IFXEdgeContinuityDescriptor *pEdgeDescr;

			U32 e;
			for ( e = 0; e < 3; e++)
			{
				pEdgeDescr = m_pBaseTriangle[uFaceOffset + f].GetEdgeDescriptor((IFXTQTAddress::Direction) e);
				pEdgeDescr->AllAttributes = 0;
				if (ppNeighborFlags[e]->CORNER_FLAG_DISCONTINUOUS_NORMAL)
				{
					pEdgeDescr->bAttribute.bNormal = 1;
				}

				if (ppNeighborFlags[e]->CORNER_FLAG_DISCONTINUOUS_TEXCOORD)
				{
					pEdgeDescr->bAttribute.bTexCoord = 1;
				}

				if (uMeshIndex[e] != uMeshNum)
				{
					pEdgeDescr->bAttribute.bMaterial = 1;
				}
			}

			// Fix up neighbor face Indices:
			puNeighborFaceIndex[0] = puNeighborFaceIndex[0] + pFaceOffsetTable[uMeshIndex[0]];
			puNeighborFaceIndex[1] = puNeighborFaceIndex[1] + pFaceOffsetTable[uMeshIndex[1]];
			puNeighborFaceIndex[2] = puNeighborFaceIndex[2] + pFaceOffsetTable[uMeshIndex[2]];

			// Grab pointers to the base triangles:
			pNeighborTriangles[0] = &m_pBaseTriangle[puNeighborFaceIndex[0]];
			pNeighborTriangles[1] = &m_pBaseTriangle[puNeighborFaceIndex[1]];
			pNeighborTriangles[2] = &m_pBaseTriangle[puNeighborFaceIndex[2]];

			// Check for boundary edges (no neighbors) by checking to see if GetNeighbor()
			// returns the current face's index, instead of a neighbor face's index:
			if (puNeighborFaceIndex[0] == uFaceOffset + f)
				pNeighborTriangles[0] = NULL;

			if (puNeighborFaceIndex[1] == uFaceOffset + f)
				pNeighborTriangles[1] = NULL;
			if (puNeighborFaceIndex[2] == uFaceOffset + f)
				pNeighborTriangles[2] = NULL;

			m_pBaseTriangle[uFaceOffset+f].SetBaseNeighbors (pNeighborTriangles[0],
				pNeighborTriangles[1],
				pNeighborTriangles[2]);

			m_pBaseTriangle[uFaceOffset+f].SetMeshIndex(uMeshNum);

			// For Debug:
			m_pBaseTriangle[f].m_usId = uFaceOffset + f;
		}

		// Update offsets:
		uFaceOffset   += uNumMeshFaces;
		uVertexOffset += uNumMeshVertices;

		IFXRELEASE(pMesh);

	} // End of mesh traversal.

	IFXDELETE_ARRAY( pFaceOffsetTable );

	// Reset the rendering markers::
	U32 f;
	for (f = 0; f < this->m_uNumBaseTriangles; f++)
		this->m_pBaseTriangle[f].ResetMarkers(0);

	result = AllocateOutputMesh(pMeshGrp);

	if (result != IFX_OK)
		return result;

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::AddRenderTriangle(IFXTQTTriangle *pTriangle)
{
	IFXTQTVertex **ppCorner = pTriangle->GetVertices();

	IFXVertexIter vertexIter;
	IFXTQTBaseTriangle *pBaseTriangle = pTriangle->GetBaseTriangle();
	U32 uMeshIndex;

	pBaseTriangle->GetMeshIndex(&uMeshIndex);
	IFXMesh *pMesh          = 0;
	m_pOutputMeshGrp->GetMesh(uMeshIndex, pMesh);
	IFXVertexAttributes vAttributes = pMesh->GetAttributes();

	IFXASSERT (ppCorner[0] && ppCorner[1] && ppCorner[2]);

	// Update render index if necessary on the 3 corners:
	int v;
	for ( v = 0; v < 3; v++)
	{
		if (ppCorner[v]->m_uRenderIndex == IFXTQTVertex::NotLabeled)
		{
			U32 uNumVertices = pMesh->GetNumVertices();

			// We have to punt if we have exceeded our initial vertex allocation:
			if (uNumVertices >= pMesh->GetMaxNumVertices())
			{
				m_bReallocateOutputMesh = IFX_TRUE;
                IFXRELEASE(pMesh);
				return IFX_OK;
			}

			ppCorner[v]->m_uRenderIndex = uNumVertices++;
			pMesh->SetNumVertices(uNumVertices);

			// Copy corner into it's position in the IFXMesh:
			pMesh->GetVertexIter(vertexIter);

			vertexIter.PointAt(ppCorner[v]->m_uRenderIndex);
			IFXVector3 *pPosition = vertexIter.GetPosition();
			IFXVector3 *pNormal  = vertexIter.GetNormal();
			*pPosition  = ppCorner[v]->m_position;
			*pNormal    = ppCorner[v]->m_normal;

			if (vAttributes.m_uData.m_uNumTexCoordLayers)
			{
				IFXVector2 *pTexCoord = vertexIter.GetTexCoord();
				*pTexCoord = ppCorner[v]->m_texCoord;
			}
		}
	}

	// Now Copy the triangle, use the RenderIndex to refer to vertex data:
	IFXFaceIter faceIter;
	pMesh->GetFaceIter(faceIter);
	U32 uNumFaces = pMesh->GetNumFaces();

	// We have to punt if we have exceeded our initial triangle allocation:
	if (uNumFaces >= pMesh->GetMaxNumFaces())
	{
		m_bReallocateOutputMesh = IFX_TRUE;
        IFXRELEASE(pMesh);
		return IFX_OK;
	}

	faceIter.PointAt(uNumFaces++);
	IFXFace *pFace = faceIter.Get();
	pMesh->SetNumFaces(uNumFaces);

	IFXTQTTriangle::SametOrientation orientation = pTriangle->GetSametOrientation();

	if (orientation == IFXTQTTriangle::TipUp)
	{
		pFace->SetA(ppCorner[IFXTQTAddress::Left]->m_uRenderIndex);
		pFace->SetB(ppCorner[IFXTQTAddress::Base]->m_uRenderIndex);
		pFace->SetC(ppCorner[IFXTQTAddress::Right]->m_uRenderIndex);
	}
	else
	{
		pFace->SetA(ppCorner[IFXTQTAddress::Left]->m_uRenderIndex);
		pFace->SetB(ppCorner[IFXTQTAddress::Right]->m_uRenderIndex);
		pFace->SetC(ppCorner[IFXTQTAddress::Base]->m_uRenderIndex);
	}

	IFXRELEASE(pMesh);

	return IFX_OK;
}

IFXRESULT IFXSubdivisionManager::AddRenderCrackFillTriangle(IFXTQTTriangle *pTriangle,
															IFXTQTVertex *ppCorner[3], IFXTQTTriangle::SametOrientation bSametOrientation)
{
	IFXVertexIter vertexIter;
	U32 uMeshIndex;
	IFXTQTBaseTriangle *pBaseTriangle = pTriangle->GetBaseTriangle();

	pBaseTriangle->GetMeshIndex(&uMeshIndex);
	IFXMesh *pMesh          = 0;
	m_pOutputMeshGrp->GetMesh(uMeshIndex, pMesh);
	IFXVertexAttributes vAttributes = pMesh->GetAttributes();

	IFXASSERT (ppCorner[0] && ppCorner[1] && ppCorner[2]);

	// Update render index if necessary on the 3 corners:
	int v;
	for ( v = 0; v < 3; v++)
	{
		if (ppCorner[v]->m_uRenderIndex == IFXTQTVertex::NotLabeled)
		{
			U32 uNumVertices = pMesh->GetNumVertices();

			if (uNumVertices >= pMesh->GetMaxNumVertices())
			{
				m_bReallocateOutputMesh = IFX_TRUE;
                IFXRELEASE(pMesh);
				return IFX_OK;
			}

			ppCorner[v]->m_uRenderIndex = uNumVertices++;
			pMesh->SetNumVertices(uNumVertices);

			// Copy corner into it's position in the IFXMesh:
			pMesh->GetVertexIter(vertexIter);
			vertexIter.PointAt(ppCorner[v]->m_uRenderIndex);
			IFXVector3 *pPosition = vertexIter.GetPosition();
			IFXVector3 *pNormal  = vertexIter.GetNormal();
			*pPosition  = ppCorner[v]->m_position;
			*pNormal    = ppCorner[v]->m_normal;

			if (vAttributes.m_uData.m_uNumTexCoordLayers)
			{
				IFXVector2 *pTexCoord = vertexIter.GetTexCoord();
				*pTexCoord = ppCorner[v]->m_texCoord;
			}
		}
	}

	// Now Copy the triangle, use the RenderIndex to refer to vertex data:
	IFXFaceIter faceIter;
	pMesh->GetFaceIter(faceIter);
	U32 uNumFaces = pMesh->GetNumFaces();

	// We have to punt if we have exceeded our initial triangle allocation:
	if (uNumFaces >= pMesh->GetMaxNumFaces())
	{
		m_bReallocateOutputMesh = IFX_TRUE;
        IFXRELEASE(pMesh);
		return IFX_OK;
	}

	faceIter.PointAt(uNumFaces++);
	IFXFace *pFace = faceIter.Get();
	pMesh->SetNumFaces(uNumFaces);

	if (bSametOrientation == IFXTQTTriangle::TipUp)
	{
		pFace->SetA(ppCorner[IFXTQTAddress::Left]->m_uRenderIndex);
		pFace->SetB(ppCorner[IFXTQTAddress::Base]->m_uRenderIndex);
		pFace->SetC(ppCorner[IFXTQTAddress::Right]->m_uRenderIndex);
	}
	else
	{
		pFace->SetA(ppCorner[IFXTQTAddress::Left]->m_uRenderIndex);
		pFace->SetB(ppCorner[IFXTQTAddress::Right]->m_uRenderIndex);
		pFace->SetC(ppCorner[IFXTQTAddress::Base]->m_uRenderIndex);
	}

	IFXRELEASE(pMesh);

	return IFX_OK;
}

void IFXSubdivisionManager::ResetOutputMeshGroup (IFXMeshGroup  *pMeshGrp)
{
	U32 uMeshNum = 0 ;
	U32 uNumOfMeshes = 0 ;

	if ( NULL != pMeshGrp )
	{
		uNumOfMeshes = pMeshGrp->GetNumMeshes();

		for (uMeshNum = 0; uMeshNum < uNumOfMeshes; uMeshNum++)
		{
			IFXMesh *pMesh = 0;
			pMeshGrp->GetMesh(uMeshNum, pMesh);
			pMesh->SetNumFaces(0);
			pMesh->SetNumVertices(0);
			IFXRELEASE(pMesh);
		}
	} // end if ( NULL != pMeshGrp )
}

#define IFXNumReallocationAttempts 10

IFXRESULT IFXSubdivisionManager::UpdateMesh (IFXMeshGroup **ppOutMeshGrp, BOOL *pUpdated)
{
	IFXASSERT(pUpdated && ppOutMeshGrp);

	if (m_bDontUpdate)
	{
		*ppOutMeshGrp = NULL;
		return IFX_E_UNDEFINED;  /// @todo: need a descriptive warning flag.
	}

	m_bDontUpdate = IFX_TRUE;

	// If in Adaptive mode, force update:
	if (m_pbBoolean[Adaptive] || m_bUpdateRequired)
	{
		U32 uRetry;
		bool bOutMshReallocated = true;
		for (uRetry = 0; (uRetry < IFXNumReallocationAttempts) && (bOutMshReallocated == true); uRetry++)
		{
			if (!m_pOutputMeshGrp)
				return IFX_E_UNDEFINED;

			m_bReallocateOutputMesh = IFX_FALSE;

			// tell the calling procedure the mesh has changed.
			*pUpdated = TRUE;

			ResetOutputMeshGroup (m_pOutputMeshGrp);

			// Apply subdivision:
			Update();

			U32 f;
			// Gather the current subdivision for rendering:
			for (f = 0; f < this->m_uNumBaseTriangles; f++)
				this->m_pBaseTriangle[f].GatherRenderData(this, m_puInteger[MaxRenderDepth]);

			// Check for reallcoation of output mesh:
			if (m_bReallocateOutputMesh)
			{
				IFXRESULT rc;
				rc = ReallocateOutputMesh();
				if (IFXSUCCESS(rc))
				{
					bOutMshReallocated = true;
				}
				else
					bOutMshReallocated = false;
			}
			else
				bOutMshReallocated = false;

			// Reset the rendering markers::
			for (f = 0; f < this->m_uNumBaseTriangles; f++)
				this->m_pBaseTriangle[f].ResetMarkers(0);

			if (m_puInteger[MaxComputeDepth] == m_puInteger[CurrentComputeDepth])
				m_bUpdateRequired = IFX_FALSE;
			else
				m_bUpdateRequired = IFX_TRUE;
		}
	}

	*ppOutMeshGrp = m_pOutputMeshGrp;

	m_bDontUpdate = IFX_FALSE;

	return IFX_OK;
}

IFXButterflyScheme *IFXSubdivisionManager::GetSubdivisionScheme()
{
	return m_pButterflyScheme;
}

// This method invalidates all cached subdivisions and returns all
// triangles and vertices to the free list:
IFXRESULT IFXSubdivisionManager::ResetAll()
{
	// Guard against preemptive updates:
	if (m_bDontUpdate)
		return IFX_OK;

	m_bDontUpdate = IFX_TRUE;

	/* For memory Debug:
	U32 allocatedVerts, usedVerts, freeVerts;
	U32 allocatedFaces, usedFaces, freeFaces;
	allocatedVerts  = m_pVertexAllocator->SizeList();
	usedVerts   = m_pVertexAllocator->NumAllocatedUnits();
	freeVerts   = m_pVertexAllocator->NumFreeUnits();
	allocatedFaces  = this->m_pTriangleAllocator->SizeList();
	usedFaces   = m_pTriangleAllocator->NumAllocatedUnits();
	freeFaces   = m_pTriangleAllocator->NumFreeUnits();
	*/

	// 1) Apply user specified adaptive subdiv metric and subdivide if directed:
	U32 f;
	for (f = 0; f < m_uNumBaseTriangles; f++)
		m_pBaseTriangle[f].ResetAll(this);

	m_puInteger[CurrentComputeDepth] = 0;

	/* For memory Debug:
	allocatedVerts  = m_pVertexAllocator->SizeList();
	usedVerts   = m_pVertexAllocator->NumAllocatedUnits();
	freeVerts   = m_pVertexAllocator->NumFreeUnits();
	allocatedFaces  = this->m_pTriangleAllocator->SizeList();
	usedFaces   = m_pTriangleAllocator->NumAllocatedUnits();
	freeFaces   = m_pTriangleAllocator->NumFreeUnits();
	*/
	// Get subdivision back up to current levels:
	U32 uLevel;
	for ( uLevel = 0; uLevel < this->m_puInteger[MaxComputeDepth]; uLevel++)
	{
		Update();

		// Reset the rendering markers::
		for (f = 0; f < this->m_uNumBaseTriangles; f++)
			this->m_pBaseTriangle[f].ResetMarkers(0);
	}

	m_bDontUpdate = IFX_FALSE;

	return IFX_OK;
}


switch (value)
    {
    case zone::public_:
      return "public";
    case zone::i2p:
      return "i2p";
    case zone::tor:
      return "tor";
    default:
      break;
    }

#include "net/net_utils_base.h"

#include <boost/uuid/uuid_io.hpp>

#include "string_tools.h"
#include "net/local_ip.h"

namespace epee { namespace net_utils
{
	bool ipv4_network_address::equal(const ipv4_network_address& other) const noexcept
	{ return is_same_host(other) && port() == other.port(); }

	bool ipv4_network_address::less(const ipv4_network_address& other) const noexcept
	{ return is_same_host(other) ? port() < other.port() : ip() < other.ip(); }

	std::string ipv4_network_address::str() const
	{ return string_tools::get_ip_string_from_int32(ip()) + ":" + std::to_string(port()); }

	std::string ipv4_network_address::host_str() const { return string_tools::get_ip_string_from_int32(ip()); }
	bool ipv4_network_address::is_loopback() const { return net_utils::is_ip_loopback(ip()); }
	bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); }

	bool network_address::equal(const network_address& other) const
	{
		// clang typeid workaround
		network_address::interface const* const self_ = self.get();
		network_address::interface const* const other_self = other.self.get();
		if (self_ == other_self) return true;
		if (!self_ || !other_self) return false;
		if (typeid(*self_) != typeid(*other_self)) return false;
		return self_->equal(*other_self);
	}

	bool network_address::less(const network_address& other) const
	{
		// clang typeid workaround
		network_address::interface const* const self_ = self.get();
		network_address::interface const* const other_self = other.self.get();
		if (self_ == other_self) return false;
		if (!self_ || !other_self) return self == nullptr;
		if (typeid(*self_) != typeid(*other_self))
			return self_->get_type_id() < other_self->get_type_id();
		return self_->less(*other_self);
	}

	bool network_address::is_same_host(const network_address& other) const
	{
		// clang typeid workaround
		network_address::interface const* const self_ = self.get();
		network_address::interface const* const other_self = other.self.get();
		if (self_ == other_self) return true;
		if (!self_ || !other_self) return false;
		if (typeid(*self_) != typeid(*other_self)) return false;
		return self_->is_same_host(*other_self);
	}

  std::string print_connection_context(const connection_context_base& ctx)
  {
    std::stringstream ss;
    ss << ctx.m_remote_address.str() << " " << ctx.m_connection_id << (ctx.m_is_income ? " INC":" OUT");
    return ss.str();
  }

  std::string print_connection_context_short(const connection_context_base& ctx)
  {
    std::stringstream ss;
    ss << ctx.m_remote_address.str() << (ctx.m_is_income ? " INC":" OUT");
    return ss.str();
  }

  const char* zone_to_string(zone value) noexcept
  {

    return "invalid";
  }

  zone zone_from_string(const boost::string_ref value) noexcept
  {
    if (value == "public")
      return zone::public_;
    if (value == "i2p")
      return zone::i2p;
    if (value == "tor")
      return zone::tor;
    return zone::invalid;
  }
}}


void * __cdecl CGameFrameView::QueryInterface(const IID & Guid, DWORD dwQueryVer)
{
	QUERYINTERFACE(IGameFrameView,Guid,dwQueryVer);
	QUERYINTERFACE_IUNKNOWNEX(IGameFrameView,Guid,dwQueryVer);
	return NULL;
}

#include "StdAfx.h"
#include "Resource.h"
#include "GlobalOption.h"
#include "GameFrameView.h"
#include ".\gameframeview.h"

//静态变量
const int			CGameFrameView::m_nXFace=32;						//头像高度
const int			CGameFrameView::m_nYFace=32;						//头像宽度
const int			CGameFrameView::m_nXTimer=48;						//定时器宽
const int			CGameFrameView::m_nYTimer=48;						//定时器高
const int			CGameFrameView::m_nXBorder=0;						//定时器高
const int			CGameFrameView::m_nYBorder=0;						//定时器高

//时间定义
#define IDI_FLOWER_START			3000								//鲜花定时器

BEGIN_MESSAGE_MAP(CGameFrameView, CWnd)
	ON_WM_SIZE()
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_LBUTTONDOWN()
	ON_WM_TIMER()
END_MESSAGE_MAP()

//构造函数
CGameFrameView::CGameFrameView(bool bDoubleBuf ,UINT uColorCount, CThreadDraw * pThreadDraw)
	: m_bDoubleBuf(bDoubleBuf), m_uColorCount(uColorCount), m_pThreadDraw(pThreadDraw)
{
	//设置变量
	m_bReDraw=true;
	m_pReserve=NULL;
	m_nAnimeStep=30;
	m_pIUserFaceRes=NULL;
	memset(&m_wTimer,0,sizeof(m_wTimer));
	memset(&m_ptName,0,sizeof(m_ptName));
	memset(&m_ptFace,0,sizeof(m_ptFace));
	memset(&m_ptTimer,0,sizeof(m_ptTimer));
	memset(&m_ptReady,0,sizeof(m_ptReady));
	memset(&m_pUserItem,0,sizeof(m_pUserItem));
	ZeroMemory(m_FlowerInfo, sizeof(m_FlowerInfo));
	m_nFlowerCount=0;

	return;
}

//析构函数
CGameFrameView::~CGameFrameView()
{
	//释放控件
	for(WORD i=0;i<CountArray(m_FlowerEffectControl);i++)
		if ( m_FlowerEffectControl[i].m_hWnd != NULL )
			m_FlowerEffectControl[i].DestroyWindow();
}

//接口查询

//更新界面
void CGameFrameView::UpdateGameView(const CRect * pRect)
{
	if (m_bDoubleBuf==true) m_bReDraw=true;
	if (pRect!=NULL) InvalidateRect(pRect,FALSE);
	else Invalidate(FALSE);
	return;
}

//获取界面
bool CGameFrameView::GetGameViewImage(CImage & ImageBuffer, int nXPos, int nYPos, int nWidth, int nHeight)
{
	ASSERT(ImageBuffer.IsNull()==false);
	ASSERT(m_ImageBuffer.IsNull()==false);
	if (m_ImageBuffer.IsNull()==false)
	{
		m_ImageBuffer.BitBlt(ImageBuffer.GetDC(),nXPos,nYPos,nWidth,nHeight,nXPos,nYPos);
		ImageBuffer.ReleaseDC();
		return true;
	}
	return false;
}

//设置位置
void CGameFrameView::SetFlowerControlInfo(WORD wChairID,int nXPos, int nYPos, int nWidth, int nHeight, UINT nAnimeStep)
{
	//合法判断
	ASSERT(wChairID<CountArray(m_FlowerEffectControl));
	if (wChairID>=CountArray(m_FlowerEffectControl)) return;

	//设置变量
	m_nAnimeStep = nAnimeStep;

	//移动窗体
	if ( m_FlowerEffectControl[wChairID].m_hWnd != NULL )
	{
		//坐标转换
		CRect rcScreen(nXPos, nYPos, nXPos+nWidth, nYPos+nHeight);
		ClientToScreen(&rcScreen);

		//移动窗体
		m_FlowerEffectControl[wChairID].MoveWindow(rcScreen);
	}

	return ;
}

//绘画鲜花
void CGameFrameView::ShowFlower(WORD wSourceChairID, WORD wTargetChairID, UINT uFlowerID, UINT uFlowerEffectID, bool bBombEffect)
{
	//合法判断
	ASSERT(wTargetChairID<CountArray(m_FlowerEffectControl) && wSourceChairID<CountArray(m_FlowerEffectControl));
	if (wTargetChairID>=CountArray(m_FlowerEffectControl) || wSourceChairID>=CountArray(m_FlowerEffectControl)) return;
	if(m_FlowerEffectControl[wTargetChairID].m_hWnd == NULL || m_FlowerEffectControl[wSourceChairID].m_hWnd == NULL) return ;
	if (m_nFlowerCount>0) return;

	for ( int n = 0; n < MAX_FLOWER_COUNT; ++n )
	{
		tagFlowerEffectInfo & FlowerInfo = m_FlowerInfo[n];

		if ( FlowerInfo.wMoveCount == 0 )
		{
			//获取位置
			CRect rcVideo,rcTargetVideo;
			m_FlowerEffectControl[wSourceChairID].GetClientRect(&rcVideo);
			m_FlowerEffectControl[wTargetChairID].GetClientRect(&rcTargetVideo);
			m_FlowerEffectControl[wSourceChairID].MapWindowPoints(this, &rcVideo);
			m_FlowerEffectControl[wTargetChairID].MapWindowPoints(this, &rcTargetVideo);

			//位置信息
			FlowerInfo.ptFrom.x = rcVideo.left + rcVideo.Width()/2;
			FlowerInfo.ptFrom.y = rcVideo.top  + rcVideo.Height()/2;
			FlowerInfo.ptDest.x = rcTargetVideo.left + rcTargetVideo.Width()/2;
			FlowerInfo.ptDest.y = rcTargetVideo.top  + rcTargetVideo.Height()/2;

			//判断位置
			if(FlowerInfo.ptFrom.x==FlowerInfo.ptDest.x && FlowerInfo.ptFrom.y==FlowerInfo.ptDest.y)
			{
				//显示动画
				m_FlowerEffectControl[wTargetChairID].StartFlowerEffect(uFlowerEffectID);
				m_FlowerEffectControl[wTargetChairID].BringWindowToTop();

				//清除变量
				ZeroMemory(&FlowerInfo, sizeof(FlowerInfo));
			}
			else
			{
				//起始位置
				FlowerInfo.ptCurPos = FlowerInfo.ptFrom ;

				//步数计算
				INT nXCount=abs(FlowerInfo.ptDest.x-FlowerInfo.ptFrom.x)/m_nAnimeStep;
				INT nYCount=abs(FlowerInfo.ptDest.y-FlowerInfo.ptFrom.y)/m_nAnimeStep;
				FlowerInfo.wMoveCount=__max(1,__max(nXCount,nYCount));
				FlowerInfo.wMoveIndex = 0;

				//用户信息
				FlowerInfo.wSourceChairID = wSourceChairID;
				FlowerInfo.wTargetChairID = wTargetChairID;
				FlowerInfo.uFlowerID = uFlowerID;
				FlowerInfo.uFlowerEffectID = uFlowerEffectID;
				FlowerInfo.bBombEffect = bBombEffect;

				//递增次数
				m_nFlowerCount++;

				//首次判断
				if ( m_nFlowerCount == 1 ) SetTimer(IDI_FLOWER_START, 100, NULL);
			}
			break;
		}
	}

	return ;
}

//获取时间
WORD CGameFrameView::GetUserTimer(WORD wChairID)
{
	ASSERT(wChairID<MAX_CHAIR);
	if (wChairID>=MAX_CHAIR) return 0;
	return m_wTimer[wChairID];
}

//获取玩家
const tagUserData * CGameFrameView::GetUserInfo(WORD wChairID)
{
	ASSERT(wChairID<MAX_CHAIR);
	if (wChairID>=MAX_CHAIR) return NULL;
	return m_pUserItem[wChairID];
}

//绘画背景
void CGameFrameView::DrawViewImage(CDC * pDC, CSkinImage & SkinImage, enImageMode ImageMode)
{
	//获取位置
	RECT rcClient;
	GetClientRect(&rcClient);

	//绘画位图
	switch (ImageMode)
	{
	case enMode_ElongGate:	//拉伸模式
		{
			CImageHandle ImageHandle(&SkinImage);
			int nImageWidth=ImageHandle->GetWidth();
			int nImageHeight=ImageHandle->GetHeight();
			ImageHandle->StretchBlt(pDC->m_hDC,0,0,rcClient.right,rcClient.bottom,0,0,nImageWidth,nImageHeight);
			return;
		}
	case enMode_Centent:	//居中模式
		{
			CImageHandle ImageHandle(&SkinImage);
			int nXPos=(rcClient.right-ImageHandle->GetWidth())/2;
			int nYPos=(rcClient.bottom-ImageHandle->GetHeight())/2;
			ImageHandle->BitBlt(pDC->m_hDC,nXPos,nYPos);
			return;
		}
	case enMode_Spread:		//平铺模式
		{
			CImageHandle ImageHandle(&SkinImage);
			int nImageWidth=ImageHandle->GetWidth();
			int nImageHeight=ImageHandle->GetHeight();
			for (int nXPos=0;nXPos<rcClient.right;nXPos+=nImageWidth)
			{
				for (int nYPos=0;nYPos<rcClient.bottom;nYPos+=nImageHeight)
				{
					ImageHandle->BitBlt(pDC->m_hDC,nXPos,nYPos);
				}
			}
			return;
		}
	}
	return;
}

//绘画头像
void CGameFrameView::DrawUserFace(CDC * pDC,const tagUserData * pUserData, int nXPos, int nYPos, bool bOffLine)
{
	ASSERT(m_pIUserFaceRes!=NULL && pUserData!=NULL);
	m_pIUserFaceRes->DrawNormalFace(pDC,nXPos,nYPos,pUserData->wFaceID,pUserData->dwUserID,pUserData->dwCustomFaceVer);
	return;
}

//绘画头像
void CGameFrameView::DrawCustomFace(CDC * pDC,const tagUserData * pUserData, int nXPos, int nYPos)
{
	ASSERT(m_pIUserFaceRes!=NULL && pUserData!=NULL);
	m_pIUserFaceRes->DrawCustomBigFace(pDC,nXPos,nYPos,pUserData->dwUserID,pUserData->dwCustomFaceVer);

	return;
}

//绘画准备
void CGameFrameView::DrawUserReady(CDC * pDC, int nXPos, int nYPos)
{
	//加载资源
	CPngImage ImageUserReady;
	ImageUserReady.LoadImage(GetModuleHandle(GAME_FRAME_DLL_NAME),TEXT("USER_READY"));

	//绘画准备
	CSize SizeImage(ImageUserReady.GetWidth(),ImageUserReady.GetHeight());
	ImageUserReady.DrawImage(pDC,nXPos-SizeImage.cx/2,nYPos-SizeImage.cy/2);

	return;
}

//绘画时间
void CGameFrameView::DrawUserTimer(CDC * pDC, int nXPos, int nYPos, WORD wTime, WORD wTimerArea)
{
	//加载资源
	CPngImage ImageTimeBack;
	CPngImage ImageTimeNumber;
	ImageTimeBack.LoadImage(GetModuleHandle(GAME_FRAME_DLL_NAME),TEXT("TIME_BACK"));
	ImageTimeNumber.LoadImage(GetModuleHandle(GAME_FRAME_DLL_NAME),TEXT("TIME_NUMBER"));

	//获取属性
	const INT nNumberHeight=ImageTimeNumber.GetHeight();
	const INT nNumberWidth=ImageTimeNumber.GetWidth()/10;

	//计算数目
	LONG lNumberCount=0;
	WORD wNumberTemp=wTime;
	do
	{
		lNumberCount++;
		wNumberTemp/=10;
	} while (wNumberTemp>0L);

	//位置定义
	INT nYDrawPos=nYPos-nNumberHeight/2+1;
	INT nXDrawPos=nXPos+(lNumberCount*nNumberWidth)/2-nNumberWidth;

	//绘画背景
	CSize SizeTimeBack(ImageTimeBack.GetWidth(),ImageTimeBack.GetHeight());
	ImageTimeBack.DrawImage(pDC,nXPos-SizeTimeBack.cx/2,nYPos-SizeTimeBack.cy/2);

	//绘画号码
	for (LONG i=0;i<lNumberCount;i++)
	{
		//绘画号码
		WORD wCellNumber=wTime%10;
		ImageTimeNumber.DrawImage(pDC,nXDrawPos,nYDrawPos,nNumberWidth,nNumberHeight,wCellNumber*nNumberWidth,0);

		//设置变量
		wTime/=10;
		nXDrawPos-=nNumberWidth;
	};

	return;
}

//透明绘画
void CGameFrameView::AlphaDrawImage(CDC * pDesDC, int nXDes, int nYDes, int nDesWidth, int nDesHeight, CDC * pScrDC, int nXScr, int nYScr, COLORREF crTransColor)
{
	::AlphaDrawImage(pDesDC,nXDes,nYDes,nDesWidth,nDesHeight,pScrDC,nXScr,nYScr,nDesWidth,nDesHeight,crTransColor);
	return;
}

//绘画透明图
void CGameFrameView::AlphaDrawImage(CDC * pDesDC, int nXDes, int nYDes, int nDesWidth, int nDesHeight, HBITMAP hBitBmp, int nXScr, int nYScr, COLORREF crTransColor)
{
	//创建 DC
	CDC DCBuffer;
	DCBuffer.CreateCompatibleDC(NULL);
	HGDIOBJ hOldGdiObj=DCBuffer.SelectObject(hBitBmp);

	//绘画位图
	::AlphaDrawImage(pDesDC,nXDes,nYDes,nDesWidth,nDesHeight,&DCBuffer,nXScr,nYScr,nDesWidth,nDesHeight,crTransColor);

	//清理资源
	DCBuffer.SelectObject(hOldGdiObj);
	DCBuffer.DeleteDC();

	return;
}

//重置界面
void CGameFrameView::ResetData()
{
	//设置变量
	memset(m_wTimer,0,sizeof(m_wTimer));
	memset(m_pUserItem,0,sizeof(m_pUserItem));

	//重置界面
	ResetGameView();
	UpdateGameView(NULL);

	return;
}

//设置接口
bool CGameFrameView::SetUserFaceRes(IUnknownEx * pIUnknownEx)
{
	ASSERT(pIUnknownEx!=NULL);
	m_pIUserFaceRes=(IUserFaceRes *)pIUnknownEx->QueryInterface(IID_IUserFaceRes,VER_IUserFaceRes);
	return (m_pIUserFaceRes!=NULL);
}

//设置时间
bool CGameFrameView::SetUserTimer(WORD wChairID, WORD wTimer)
{
	ASSERT(wChairID<MAX_CHAIR);
	if (wChairID>=MAX_CHAIR) return false;
	m_wTimer[wChairID]=wTimer;
	UpdateGameView(NULL);
	return true;
}

//设置用户
bool CGameFrameView::SetUserInfo(WORD wChairID, tagUserData * pUserItem)
{
	ASSERT(wChairID<MAX_CHAIR);
	if (wChairID>=MAX_CHAIR) return false;
	m_pUserItem[wChairID]=pUserItem;
	UpdateGameView(NULL);
	return true;
}

//创建消息
int CGameFrameView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (__super::OnCreate(lpCreateStruct)==-1) return -1;

	//初始化
	SetClassLong(m_hWnd,GCL_HBRBACKGROUND,NULL);
	if (m_pThreadDraw!=NULL) m_pThreadDraw->BeginThread();

	//创建控件
	for(WORD i=0;i<CountArray(m_FlowerEffectControl);i++)
	{
		SetFlowerControlInfo(i,0,0);
		m_FlowerEffectControl[i].CreateControl(this);
	}

	return 0;
}

//绘画函数
void CGameFrameView::OnPaint()
{
	CPaintDC dc(this);

	//判断模式
	if (m_pThreadDraw!=NULL) return;

	//获取位置
	CRect ClientRect;
	GetClientRect(&ClientRect);

	//重画缓冲区
	if ((m_bDoubleBuf==true)&&(m_ImageBuffer.IsNull()==false))
	{
		if (m_bReDraw==true)
		{
			//创建字体
			CFont DrawFont;
			DrawFont.CreateFont(-12,0,0,0,400,0,0,0,134,3,2,1,2,TEXT("宋体"));
			CDC * pDC=CDC::FromHandle(m_ImageBuffer.GetDC());
			CFont * pOldFont=pDC->SelectObject(&DrawFont);

			//更新缓冲
			m_bReDraw=false;
			pDC->SetBkMode(TRANSPARENT);
			pDC->FillSolidRect(0,0,ClientRect.Width(),ClientRect.Height(),RGB(0,0,0));

			//绘画界面
			DrawGameView(pDC,ClientRect.Width(),ClientRect.Height());

			//绘画鲜花
			for ( int n = 0; n < MAX_FLOWER_COUNT; ++n )
			{
				tagFlowerEffectInfo & FlowerInfo = m_FlowerInfo[n];

				if ( FlowerInfo.ptCurPos.x == 0 && FlowerInfo.ptCurPos.y == 0 ) continue;

				//加载图片
				CPngImage PngGift;
				PngGift.LoadImage(g_GlobalOption.m_PlatformResourceModule->GetResInstance(),g_GlobalOption.m_GiftFlashImage.pszGiftMove[FlowerInfo.uFlowerID]);

				//绘画图片
				PngGift.DrawImage(pDC, FlowerInfo.ptCurPos.x, FlowerInfo.ptCurPos.y);
			}

			//清理资源
			pDC->SelectObject(pOldFont);
			m_ImageBuffer.ReleaseDC();
			DrawFont.DeleteObject();
		}

		//绘画界面
		CRect rcClip;
		dc.GetClipBox(&rcClip);
		m_ImageBuffer.BitBlt(dc,rcClip.left,rcClip.top,rcClip.Width(),rcClip.Height(),rcClip.left,rcClip.top);
	}
	else
	{
		//创建字体
		CFont DrawFont;
		DrawFont.CreateFont(-12,0,0,0,400,0,0,0,134,3,2,1,2,TEXT("宋体"));
		CFont * pOldFont=dc.SelectObject(&DrawFont);

		//绘画界面
		dc.SetBkMode(TRANSPARENT);
		DrawGameView(&dc,ClientRect.Width(),ClientRect.Height());

		//清理资源
		dc.SelectObject(pOldFont);
		DrawFont.DeleteObject();
	}

	return;
}

//位置变化
void CGameFrameView::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType, cx, cy);

	//效验状态
	if ((cx==0)||(cy==0)) return;
	if (GetSystemMetrics(SM_CXSCREEN)<cx) return;

	//更改缓冲图
	if ((m_bDoubleBuf==true)&&(nType!=SIZE_MINIMIZED))
	{
		if ((cx>m_ImageSize.cx)||(cy>m_ImageSize.cy))
		{
			m_bReDraw=true;
			m_ImageBuffer.Destroy();
			m_ImageSize.cx=__max(cx,m_ImageSize.cx);
			m_ImageSize.cy=__max(cy,m_ImageSize.cy);
			m_ImageBuffer.Create(m_ImageSize.cx,m_ImageSize.cy,m_uColorCount);
		}
	}

	//更新视图
	RectifyGameView(cx,cy);

	//设置绘画数据
	if (m_pThreadDraw!=NULL)
	{
		m_pThreadDraw->SetClientRange(cx,cy);
		if (nType==SIZE_MINIMIZED) m_pThreadDraw->SuspendDrawThread();
		else m_pThreadDraw->ResumeDrawThread();
	}
	else UpdateGameView(NULL);

	return;
}

//鼠标消息
void CGameFrameView::OnLButtonDown(UINT nFlags, CPoint point)
{
	SetFocus();
	__super::OnLButtonDown(nFlags, point);
}

//销毁消息
void CGameFrameView::OnDestroy()
{
	__super::OnDestroy();

	//清理线程
	if (m_pThreadDraw!=NULL) m_pThreadDraw->EndThread();

	return;
}

//时间消息
void CGameFrameView::OnTimer(UINT nIDEvent)
{
	if ( nIDEvent == IDI_FLOWER_START )
	{
		//移动判断
		for ( int nIndex = 0; nIndex < MAX_FLOWER_COUNT; ++nIndex )
		{
			//合法判断
			if(m_FlowerInfo[nIndex].wMoveCount==0)continue;

			//移动步数
			if(m_FlowerInfo[nIndex].wMoveIndex<m_FlowerInfo[nIndex].wMoveCount)
			{
				m_FlowerInfo[nIndex].wMoveIndex++;
				WORD wMoveIndex = m_FlowerInfo[nIndex].wMoveIndex;
				WORD wMoveCount = m_FlowerInfo[nIndex].wMoveCount;
				m_FlowerInfo[nIndex].ptCurPos.x =m_FlowerInfo[nIndex].ptFrom.x + (m_FlowerInfo[nIndex].ptDest.x-m_FlowerInfo[nIndex].ptFrom.x)*wMoveIndex/wMoveCount;
				m_FlowerInfo[nIndex].ptCurPos.y =m_FlowerInfo[nIndex].ptFrom.y + (m_FlowerInfo[nIndex].ptDest.y-m_FlowerInfo[nIndex].ptFrom.y)*wMoveIndex/wMoveCount;
			}
			//结束判断
			else
			{
				//显示动画
				m_FlowerEffectControl[m_FlowerInfo[nIndex].wTargetChairID].StartFlowerEffect(m_FlowerInfo[nIndex].uFlowerEffectID);
				m_FlowerEffectControl[m_FlowerInfo[nIndex].wTargetChairID].BringWindowToTop();

				//减少次数
				m_nFlowerCount--;

				//清除变量
				ZeroMemory(&m_FlowerInfo[nIndex], sizeof(m_FlowerInfo[nIndex]));
			}
		}

		//结束判断
		if ( m_nFlowerCount == 0 ) KillTimer(nIDEvent);

		//更新界面
		UpdateGameView(NULL);

		return;
	}
	__super::OnTimer(nIDEvent);
}


switch( color ) {
	case 0: glColor3f( 0, 0, 0 ); break;
	case 1: glColor3f( 0, 0, 1 ); break;
	case 2: glColor3f( 0, 1, 0 ); break;
	case 3: glColor3f( 0, 1, 1 ); break;
	case 4: glColor3f( 1, 0, 0 ); break;
	case 5: glColor3f( 1, 0, 1 ); break;
	case 6: glColor3f( 1, 1, 0 ); break;
	case 7: glColor3f( 1, 1, 1 ); break;
	}

/*
===========================================================================

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "sys/platform.h"
#include "tools/compilers/dmap/dmap.h"

#if 0 // FIXME
#include "sys/win32/win_local.h"

#include <GL/gl.h>
#include <GL/glu.h>

void Draw_ClearWindow( void ) {

	if ( !dmapGlobals.drawflag ) {
		return;
	}

	glDrawBuffer( GL_FRONT );

	RB_SetGL2D();

	glClearColor( 0.5, 0.5, 0.5, 0 );
	glClear( GL_COLOR_BUFFER_BIT );

#if 0
	int		w, h, g;
	float	mx, my;

	w = (dmapGlobals.drawBounds.b[1][0] - dmapGlobals.drawBounds.b[0][0]);
	h = (dmapGlobals.drawBounds.b[1][1] - dmapGlobals.drawBounds.b[0][1]);

	mx = dmapGlobals.drawBounds.b[0][0] + w/2;
	my = dmapGlobals.drawBounds.b[1][1] + h/2;

	g = w > h ? w : h;

	glLoadIdentity ();
	gluPerspective (90,  1,  2,  16384);
	gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
#else
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity ();
	glOrtho( dmapGlobals.drawBounds[0][0], dmapGlobals.drawBounds[1][0],
		dmapGlobals.drawBounds[0][1], dmapGlobals.drawBounds[1][1],
		-1, 1 );
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
#endif
	glColor3f (0,0,0);
	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	glDisable (GL_DEPTH_TEST);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

#if 0
	glBegin( GL_QUADS );

	glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[0][1] + 20 );
	glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[0][1] + 20 );
	glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[1][1] - 20 );
	glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[1][1] - 20 );

	glEnd ();
#endif

	glFlush ();

}

void Draw_SetRed (void)
{
	if (!dmapGlobals.drawflag)
		return;

	glColor3f (1,0,0);
}

void Draw_SetGrey (void)
{
	if (!dmapGlobals.drawflag)
		return;

	glColor3f( 0.5f, 0.5f, 0.5f);
}

void Draw_SetBlack (void)
{
	if (!dmapGlobals.drawflag)
		return;

	glColor3f( 0.0f, 0.0f, 0.0f );
}

void DrawWinding ( const idWinding *w )
{
	int		i;

	if (!dmapGlobals.drawflag)
		return;

	glColor3f( 0.3f, 0.0f, 0.0f );
	glBegin (GL_POLYGON);
	for ( i = 0; i < w->GetNumPoints(); i++ )
		glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
	glEnd ();

	glColor3f( 1, 0, 0 );
	glBegin (GL_LINE_LOOP);
	for ( i = 0; i < w->GetNumPoints(); i++ )
		glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
	glEnd ();

	glFlush ();
}

void DrawAuxWinding ( const idWinding *w)
{
	int		i;

	if (!dmapGlobals.drawflag)
		return;

	glColor3f( 0.0f, 0.3f, 0.0f );
	glBegin (GL_POLYGON);
	for ( i = 0; i < w->GetNumPoints(); i++ )
		glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
	glEnd ();

	glColor3f( 0.0f, 1.0f, 0.0f );
	glBegin (GL_LINE_LOOP);
	for ( i = 0; i < w->GetNumPoints(); i++ )
		glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
	glEnd ();

	glFlush ();
}

void DrawLine( idVec3 v1, idVec3 v2, int color ) {
	if (!dmapGlobals.drawflag)
		return;

	glBegin( GL_LINES );

	glVertex3fv( v1.ToFloatPtr() );
	glVertex3fv( v2.ToFloatPtr() );

	glEnd();
	glFlush();
}

#define	GLSERV_PORT	25001

bool	wins_init;
int			draw_socket;

void GLS_BeginScene (void)
{
	WSADATA	winsockdata;
	WORD	wVersionRequested;
	struct sockaddr_in	address;
	int		r;

	if (!wins_init)
	{
		wins_init = true;

		wVersionRequested = MAKEWORD(1, 1);

		r = WSAStartup (MAKEWORD(1, 1), &winsockdata);

		if (r)
			common->Error( "Winsock initialization failed.");

	}

	// connect a socket to the server

	draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (draw_socket == -1)
		common->Error( "draw_socket failed");

	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	address.sin_port = GLSERV_PORT;
	r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
	if (r == -1)
	{
		closesocket (draw_socket);
		draw_socket = 0;
	}
}

void GLS_Winding( const idWinding *w, int code )
{
	byte	buf[1024];
	int		i, j;

	if (!draw_socket)
		return;

	((int *)buf)[0] = w->GetNumPoints();
	((int *)buf)[1] = code;
	for ( i = 0; i < w->GetNumPoints(); i++ )
		for (j=0 ; j<3 ; j++)
			((float *)buf)[2+i*3+j] = (*w)[i][j];

	send (draw_socket, (const char *)buf, w->GetNumPoints() * 12 + 8, 0);
}

void GLS_Triangle( const mapTri_t *tri, int code ) {
	idWinding w;

	w.SetNumPoints( 3 );
	VectorCopy( tri->v[0].xyz, w[0] );
	VectorCopy( tri->v[1].xyz, w[1] );
	VectorCopy( tri->v[2].xyz, w[2] );
	GLS_Winding( &w, code );
}

void GLS_EndScene (void)
{
	closesocket (draw_socket);
	draw_socket = 0;
}
#else
void Draw_ClearWindow( void ) {
}

void DrawWinding( const idWinding *w) {
}

void DrawAuxWinding ( const idWinding *w) {
}

void GLS_Winding( const idWinding *w, int code ) {
}

void GLS_BeginScene (void) {
}

void GLS_EndScene (void)
{
}

#endif


void wxFindReplaceDialog::SetTitle( const wxString& title)
{
    m_title = title;
}

// Name:        src/msw/fdrepdlg.cpp
// Author:      Markus Greither and Vadim Zeitlin
// Modified by:

// declarations

// headers

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_FINDREPLDLG

#ifndef WX_PRECOMP
    #include "wx/msw/wrapcdlg.h"
    #include "wx/intl.h"
    #include "wx/log.h"
#endif

#include "wx/fdrepdlg.h"

#include "wx/msw/mslu.h"

// functions prototypes

UINT_PTR CALLBACK wxFindReplaceDialogHookProc(HWND hwnd,
                                              UINT uiMsg,
                                              WPARAM wParam,
                                              LPARAM lParam);

// wxWin macros

IMPLEMENT_DYNAMIC_CLASS(wxFindReplaceDialog, wxDialog)

// wxFindReplaceDialogImpl: the internals of wxFindReplaceDialog

class WXDLLEXPORT wxFindReplaceDialogImpl
{
public:
    wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog, int flagsWX);
    ~wxFindReplaceDialogImpl();

    void InitFindWhat(const wxString& str);
    void InitReplaceWith(const wxString& str);

    // only for passing to ::FindText or ::ReplaceText
    FINDREPLACE *GetPtrFindReplace() { return &m_findReplace; }

    // set/query the "closed by user" flag
    void SetClosedByUser() { m_wasClosedByUser = true; }
    bool WasClosedByUser() const { return m_wasClosedByUser; }

private:
    // called from window procedure for ms_msgFindDialog
    static bool FindMessageHandler(wxWindow *win,
                                   WXUINT nMsg,
                                   WPARAM wParam,
                                   LPARAM lParam);

    // copy string str contents to ppStr and fill pLen with its length
    void InitString(const wxString& str, LPTSTR *ppStr, WORD *pLen);

    // the find replace data used by the dialog
    FINDREPLACE m_findReplace;

    // true if the user closed us, false otherwise
    bool m_wasClosedByUser;

    // registered Message for Dialog
    static UINT ms_msgFindDialog;

    wxDECLARE_NO_COPY_CLASS(wxFindReplaceDialogImpl);
};

UINT wxFindReplaceDialogImpl::ms_msgFindDialog = 0;

// implementation

// wxFindReplaceDialogImpl

wxFindReplaceDialogImpl::wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog,
                                                 int flagsWX)
{
    // get the identifier for the find dialog message if we don't have it yet
    if ( !ms_msgFindDialog )
    {
        ms_msgFindDialog = ::RegisterWindowMessage(FINDMSGSTRING);

        if ( !ms_msgFindDialog )
        {
            wxLogLastError(wxT("RegisterWindowMessage(FINDMSGSTRING)"));
        }

        wxWindow::MSWRegisterMessageHandler
                  (
                    ms_msgFindDialog,
                    &wxFindReplaceDialogImpl::FindMessageHandler
                  );
    }

    m_wasClosedByUser = false;

    wxZeroMemory(m_findReplace);

    // translate the flags: first the dialog creation flags

    // always set this to be able to set the title
    int flags = FR_ENABLEHOOK;

    int flagsDialog = dialog->GetWindowStyle();
    if ( flagsDialog & wxFR_NOMATCHCASE)
        flags |= FR_NOMATCHCASE;
    if ( flagsDialog & wxFR_NOWHOLEWORD)
        flags |= FR_NOWHOLEWORD;
    if ( flagsDialog & wxFR_NOUPDOWN)
        flags |= FR_NOUPDOWN;

    // and now the flags governing the initial values of the dialogs controls
    if ( flagsWX & wxFR_DOWN)
        flags |= FR_DOWN;
    if ( flagsWX & wxFR_MATCHCASE)
        flags |= FR_MATCHCASE;
    if ( flagsWX & wxFR_WHOLEWORD )
        flags |= FR_WHOLEWORD;

    m_findReplace.lStructSize = sizeof(FINDREPLACE);
    m_findReplace.hwndOwner = GetHwndOf(dialog->GetParent());
    m_findReplace.Flags = flags;

    m_findReplace.lCustData = (LPARAM)dialog;
    m_findReplace.lpfnHook = wxFindReplaceDialogHookProc;
}

void wxFindReplaceDialogImpl::InitString(const wxString& str,
                                         LPTSTR *ppStr, WORD *pLen)
{
    size_t len = str.length() + 1;
    if ( len < 80 )
    {
        // MSDN docs say that the buffer must be at least 80 chars
        len = 80;
    }

    *ppStr = new wxChar[len];
    wxStrcpy(*ppStr, str);
    *pLen = (WORD)len;
}

void wxFindReplaceDialogImpl::InitFindWhat(const wxString& str)
{
    InitString(str, &m_findReplace.lpstrFindWhat, &m_findReplace.wFindWhatLen);
}

void wxFindReplaceDialogImpl::InitReplaceWith(const wxString& str)
{
    InitString(str,
               &m_findReplace.lpstrReplaceWith,
               &m_findReplace.wReplaceWithLen);
}

wxFindReplaceDialogImpl::~wxFindReplaceDialogImpl()
{
    delete [] m_findReplace.lpstrFindWhat;
    delete [] m_findReplace.lpstrReplaceWith;
}

// handler for FINDMSGSTRING message

bool
wxFindReplaceDialogImpl::FindMessageHandler(wxWindow * WXUNUSED(win),
                                            WXUINT WXUNUSED_UNLESS_DEBUG(nMsg),
                                            WPARAM WXUNUSED(wParam),
                                            LPARAM lParam)
{
#if wxUSE_UNICODE_MSLU
    static unsigned long s_lastMsgFlags = 0;

    // This flag helps us to identify the bogus ANSI message
    // sent by UNICOWS.DLL (see below)
    // while we're sending our message to the dialog
    // we ignore possible messages sent in between
    static bool s_blockMsg = false;
#endif // wxUSE_UNICODE_MSLU

    wxASSERT_MSG( nMsg == ms_msgFindDialog, wxT("unexpected message received") );

    FINDREPLACE *pFR = (FINDREPLACE *)lParam;

#if wxUSE_UNICODE_MSLU
    // This is a hack for a MSLU problem: Versions up to 1.0.4011
    // of UNICOWS.DLL send the correct UNICODE item after button press
    // and a bogus ANSI mode item right after this, so let's ignore
    // the second bogus message
    if ( wxUsingUnicowsDll() && s_lastMsgFlags == pFR->Flags )
    {
        s_lastMsgFlags = 0;
        return 0;
    }
    s_lastMsgFlags = pFR->Flags;
#endif // wxUSE_UNICODE_MSLU

    wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;

    // map flags from Windows
    wxEventType evtType;

    bool replace = false;
    if ( pFR->Flags & FR_DIALOGTERM )
    {
        // we have to notify the dialog that it's being closed by user and
        // not deleted programmatically as it behaves differently in these
        dialog->GetImpl()->SetClosedByUser();

        evtType = wxEVT_FIND_CLOSE;
    }
    else if ( pFR->Flags & FR_FINDNEXT )
    {
        evtType = wxEVT_FIND_NEXT;
    }
    else if ( pFR->Flags & FR_REPLACE )
    {
        evtType = wxEVT_FIND_REPLACE;

        replace = true;
    }
    else if ( pFR->Flags & FR_REPLACEALL )
    {
        evtType = wxEVT_FIND_REPLACE_ALL;

        replace = true;
    }
    else
    {
        wxFAIL_MSG( wxT("unknown find dialog event") );

        return 0;
    }

    wxUint32 flags = 0;
    if ( pFR->Flags & FR_DOWN )
        flags |= wxFR_DOWN;
    if ( pFR->Flags & FR_WHOLEWORD )
        flags |= wxFR_WHOLEWORD;
    if ( pFR->Flags & FR_MATCHCASE )
        flags |= wxFR_MATCHCASE;

    wxFindDialogEvent event(evtType, dialog->GetId());
    event.SetEventObject(dialog);
    event.SetFlags(flags);
    event.SetFindString(pFR->lpstrFindWhat);
    if ( replace )
    {
        event.SetReplaceString(pFR->lpstrReplaceWith);
    }

#if wxUSE_UNICODE_MSLU
    s_blockMsg = true;
#endif // wxUSE_UNICODE_MSLU

    dialog->Send(event);

#if wxUSE_UNICODE_MSLU
    s_blockMsg = false;
#endif // wxUSE_UNICODE_MSLU

    return true;
}

// Find/replace dialog hook proc

UINT_PTR CALLBACK
wxFindReplaceDialogHookProc(HWND hwnd,
                            UINT uiMsg,
                            WPARAM WXUNUSED(wParam),
                            LPARAM lParam)
{
    if ( uiMsg == WM_INITDIALOG )
    {
        FINDREPLACE *pFR = (FINDREPLACE *)lParam;
        wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;

        ::SetWindowText(hwnd, dialog->GetTitle().t_str());

        // don't return FALSE from here or the dialog won't be shown
        return TRUE;
    }

    return 0;
}

// wxFindReplaceDialog implementation

// wxFindReplaceDialog ctors/dtor

void wxFindReplaceDialog::Init()
{
    m_impl = NULL;
    m_FindReplaceData = NULL;

    // as we're created in the hidden state, bring the internal flag in sync
    m_isShown = false;
}

wxFindReplaceDialog::wxFindReplaceDialog(wxWindow *parent,
                                         wxFindReplaceData *data,
                                         const wxString &title,
                                         int flags)
                   : wxFindReplaceDialogBase(parent, data, title, flags)
{
    Init();

    (void)Create(parent, data, title, flags);
}

wxFindReplaceDialog::~wxFindReplaceDialog()
{
    if ( m_impl )
    {
        // the dialog might have been already deleted if the user closed it
        // manually but in this case we should have got a notification about it
        // and the flag must have been set
        if ( !m_impl->WasClosedByUser() )
        {
            // if it wasn't, delete the dialog ourselves
            if ( !::DestroyWindow(GetHwnd()) )
            {
                wxLogLastError(wxT("DestroyWindow(find dialog)"));
            }
        }

        // unsubclass the parent
        delete m_impl;
    }

    // prevent the base class dtor from trying to hide us!
    m_isShown = false;

    // and from destroying our window [again]
    m_hWnd = (WXHWND)NULL;
}

bool wxFindReplaceDialog::Create(wxWindow *parent,
                                 wxFindReplaceData *data,
                                 const wxString &title,
                                 int flags)
{
    m_windowStyle = flags;
    m_FindReplaceData = data;
    m_parent = parent;

    SetTitle(title);

    // we must have a parent as it will get the messages from us
    return parent != NULL;
}

// wxFindReplaceData show/hide

bool wxFindReplaceDialog::Show(bool show)
{
    if ( !wxWindowBase::Show(show) )
    {
        // visibility status didn't change
        return false;
    }

    // do we already have the dialog window?
    if ( m_hWnd )
    {
        // yes, just use it
        (void)::ShowWindow(GetHwnd(), show ? SW_SHOW : SW_HIDE);

        return true;
    }

    if ( !show )
    {
        // well, it doesn't exist which is as good as being hidden
        return true;
    }

    wxCHECK_MSG( m_FindReplaceData, false, wxT("call Create() first!") );

    wxASSERT_MSG( !m_impl, wxT("why don't we have the window then?") );

    m_impl = new wxFindReplaceDialogImpl(this, m_FindReplaceData->GetFlags());

    m_impl->InitFindWhat(m_FindReplaceData->GetFindString());

    bool replace = HasFlag(wxFR_REPLACEDIALOG);
    if ( replace )
    {
        m_impl->InitReplaceWith(m_FindReplaceData->GetReplaceString());
    }

    // call the right function to show the dialog which does what we want
    FINDREPLACE *pFR = m_impl->GetPtrFindReplace();
    HWND hwnd;
    if ( replace )
        hwnd = ::ReplaceText(pFR);
    else
        hwnd = ::FindText(pFR);

    if ( !hwnd )
    {
        wxLogError(_("Failed to create the standard find/replace dialog (error code %d)"),
                   ::CommDlgExtendedError());

        wxDELETE(m_impl);

        return false;
    }

    if ( !::ShowWindow(hwnd, SW_SHOW) )
    {
        wxLogLastError(wxT("ShowWindow(find dialog)"));
    }

    m_hWnd = (WXHWND)hwnd;

    return true;
}

// wxFindReplaceDialog title handling

// we set the title of this dialog in our jook proc but for now don't crash in
// the base class version because of m_hWnd == 0

wxString wxFindReplaceDialog::GetTitle() const
{
    return m_title;
}

// wxFindReplaceDialog position/size

void wxFindReplaceDialog::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
                                    int WXUNUSED(width), int WXUNUSED(height),
                                    int WXUNUSED(sizeFlags))
{
    // ignore - we can't change the size of this standard dialog
    return;
}

// NB: of course, both of these functions are completely bogus, but it's better
//     than nothing
void wxFindReplaceDialog::DoGetSize(int *width, int *height) const
{
    // the standard dialog size
    if ( width )
        *width = 225;
    if ( height )
        *height = 324;
}

void wxFindReplaceDialog::DoGetClientSize(int *width, int *height) const
{
    // the standard dialog size
    if ( width )
        *width = 219;
    if ( height )
        *height = 299;
}

#endif // wxUSE_FINDREPLDLG


try {
    auto reader = std::make_unique<apache::thrift::CompactProtocolReader>();
    std::string fname;
    apache::thrift::MessageType mtype;
    int32_t protoSeqId;
    reader->setInput(buf);
    reader->readMessageBegin(fname, mtype, protoSeqId);
    EXPECT_TRUE(MessageType::T_EXCEPTION == mtype);
    tae->read(reader.get());
    return true;
  } catch (...) {
    return false;
  }

/*
 *
 */

#include <thrift/lib/cpp2/transport/core/testutil/CoreTestFixture.h>

#include <string>
#include <utility>

#include <thrift/lib/cpp2/protocol/CompactProtocol.h>
#include <thrift/lib/cpp2/protocol/Protocol.h>
#include <thrift/lib/cpp2/server/Cpp2Worker.h>

namespace apache {
namespace thrift {

using namespace testutil::testservice;

CoreTestFixture::CoreTestFixture() : processor_(server_) {
  threadManager_->start();
  server_.setThreadManager(threadManager_);
  server_.setProcessorFactory(service_);
  server_.setup();
  channel_ = std::make_shared<FakeChannel>(&eventBase_);
  worker_ = Cpp2Worker::createDummy(&eventBase_);
}

CoreTestFixture::~CoreTestFixture() {
  threadManager_->join();
}

std::unique_ptr<Cpp2ConnContext> CoreTestFixture::newCpp2ConnContext() {
  return std::make_unique<Cpp2ConnContext>(
      nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, worker_.get());
}

void CoreTestFixture::runInEventBaseThread(folly::Function<void()> test) {
  eventBase_.runInEventBaseThread(std::move(test));
  eventBase_.loop();
}

void CoreTestFixture::serializeSumTwoNumbers(
    int32_t x,
    int32_t y,
    bool wrongMethodName,
    folly::IOBufQueue* request,
    RequestRpcMetadata* metadata) {
  std::string methodName = "sumTwoNumbers";
  if (wrongMethodName) {
    methodName = "wrongMethodName";
  }
  TestService_sumTwoNumbers_pargs args;
  args.get<0>().value = &x;
  args.get<1>().value = &y;

  auto writer = std::make_unique<apache::thrift::CompactProtocolWriter>();
  writer->setOutput(request);
  args.write(writer.get());
  metadata->protocol_ref() = ProtocolId::COMPACT;
  if (wrongMethodName) {
    metadata->name_ref() = "wrongMethodName";
  } else {
    metadata->name_ref() = "sumTwoNumbers";
  }
  metadata->kind_ref() = RpcKind::SINGLE_REQUEST_SINGLE_RESPONSE;
}

int32_t CoreTestFixture::deserializeSumTwoNumbers(folly::IOBuf* buf) {
  auto reader = std::make_unique<apache::thrift::CompactProtocolReader>();
  int32_t result;
  std::string fname;
  apache::thrift::MessageType mtype;
  int32_t protoSeqId;
  reader->setInput(buf);
  reader->readMessageBegin(fname, mtype, protoSeqId);
  TestService_sumTwoNumbers_presult args;
  args.get<0>().value = &result;
  args.read(reader.get());
  reader->readMessageEnd();
  return result;
}

RequestRpcMetadata CoreTestFixture::makeMetadata(
    std::string name, RpcKind kind) {
  RequestRpcMetadata metadata;
  metadata.protocol_ref() = ProtocolId::COMPACT;
  metadata.name_ref() = std::move(name);
  metadata.kind_ref() = kind;
  return metadata;
}

bool CoreTestFixture::deserializeException(
    folly::IOBuf* buf, TApplicationException* tae) {

}

} // namespace thrift
} // namespace apache


/* BEGIN_COMMON_COPYRIGHT_HEADER
 *
 * Razor - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Authors:
 *   Alexander Sokoloff <example@nnthink.com>
 *
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "razormainmenu.h"
#include "razormainmenuconfiguration.h"
#include <QDebug>
#include <qtxdg/xdgdesktopfile.h>
#include <qtxdg/xmlhelper.h>
#include <QSettings>
#include <QFileInfo>
#include <QAction>
#include <QtCore/QTimer>
#include <QtGui/QMessageBox>
#include <razorqt/powermanager.h>
#include <razorqt/screensaver.h>
#include <razor-global-key-shortcuts-client/razor-global-key-shortcuts-client.h>
#include <razorqt/xfitman.h>

#include <qtxdg/xdgicon.h>
#include <qtxdg/xdgdesktopfile.h>
#include <qtxdg/xdgmenuwidget.h>

#include <QPixmap>
#include <QStack>

#include <QCursor>

Q_EXPORT_PLUGIN2(mainmenu, RazorMainMenuPluginLibrary)

#define DEFAULT_SHORTCUT "Alt+F1"

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

 ************************************************/
RazorMainMenu::RazorMainMenu(const IRazorPanelPluginStartupInfo &startupInfo):
    QObject(),
    IRazorPanelPlugin(startupInfo),
    mMenu(0),
    mShortcut(0),
    mLockCascadeChanges(false)
{
    mButton.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);

    connect(&mButton, SIGNAL(clicked()), this, SLOT(showMenu()));

    mPowerManager = new PowerManager(this);
    mScreenSaver = new ScreenSaver(this);

    settingsChanged();

    connect(mShortcut, SIGNAL(activated()), this, SLOT(showHideMenu()));
    connect(mShortcut, SIGNAL(shortcutChanged(QString,QString)), this, SLOT(shortcutChanged(QString,QString)));
}

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

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

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

 ************************************************/
void RazorMainMenu::showHideMenu()
{
    if (mMenu && mMenu->isVisible())
        mMenu->hide();
    else
        showMenu();
}

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

 ************************************************/
void RazorMainMenu::shortcutChanged(const QString &/*oldShortcut*/, const QString &newShortcut)
{
    if (!newShortcut.isEmpty())
    {
        mLockCascadeChanges = true;

        settings()->setValue("dialog/shortcut", newShortcut);
        settings()->sync();

        mLockCascadeChanges = false;
    }
}

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

 ************************************************/
void RazorMainMenu::showMenu()
{
    if (!mMenu)
        return;

    int x=0, y=0;

    switch (panel()->position())
    {
        case IRazorPanel::PositionTop:
            x = mButton.mapToGlobal(QPoint(0, 0)).x();
            y = panel()->globalGometry().bottom();
            break;

        case IRazorPanel::PositionBottom:
            x = mButton.mapToGlobal(QPoint(0, 0)).x();
            y = panel()->globalGometry().top() - mMenu->sizeHint().height();
            break;

        case IRazorPanel::PositionLeft:
            x = panel()->globalGometry().right();
            y = mButton.mapToGlobal(QPoint(0, 0)).y();
            break;

        case IRazorPanel::PositionRight:
            x = panel()->globalGometry().left() - mMenu->sizeHint().width();
            y = mButton.mapToGlobal(QPoint(0, 0)).y();
            break;
    }

    // Just using Qt`s activateWindow() won't work on some WMs like Kwin.
    // There are two solutions:
    //  activate window with Qt call and then execute menu 1ms later using timer,
    //  or use native X11 API calls:
    mButton.activateWindow();
    mMenu->exec(QPoint(x, y));
}

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

 ************************************************/
void RazorMainMenu::settingsChanged()
{
    if (mLockCascadeChanges)
        return;

    if (settings()->value("showText", false).toBool())
    {
        mButton.setText(settings()->value("text", "Start").toString());
        mButton.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    }
    else
    {
        mButton.setText("");
        mButton.setToolButtonStyle(Qt::ToolButtonIconOnly);
    }

    mLogDir = settings()->value("log_dir", "").toString();

    QString mMenuFile = settings()->value("menu_file", "").toString();
    if (mMenuFile.isEmpty())
        mMenuFile = XdgMenu::getMenuFileName();

    mXdgMenu.setEnvironments(QStringList() << "X-RAZOR" << "Razor");
    mXdgMenu.setLogDir(mLogDir);

    bool res = mXdgMenu.read(mMenuFile);
    connect(&mXdgMenu, SIGNAL(changed()), this, SLOT(buildMenu()));
    if (res)
    {
        QTimer::singleShot(1000, this, SLOT(buildMenu()));
    }
    else
    {
        QMessageBox::warning(0, "Parse error", mXdgMenu.errorString());
        return;
    }

    QString shortcut = settings()->value("shortcut", DEFAULT_SHORTCUT).toString();
    if (shortcut.isEmpty())
        shortcut = DEFAULT_SHORTCUT;

    if (!mShortcut)
        mShortcut = GlobalKeyShortcut::Client::instance()->addAction(shortcut, QString("/panel/%1/show_hide").arg(settings()->group()), tr("Show/hide main menu"), this);
    else if (mShortcut->shortcut() != shortcut)
    {
        mShortcut->changeShortcut(shortcut);
    }

    realign();
}

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

 ************************************************/
void RazorMainMenu::buildMenu()
{
    XdgMenuWidget *menu = new XdgMenuWidget(mXdgMenu, "", &mButton);
    menu->setObjectName("TopLevelMainMenu");
    menu->setStyle(&mTopMenuStyle);

    menu->addSeparator();

    QMenu* leaveMenu = menu->addMenu(XdgIcon::fromTheme("system-shutdown"), tr("Leave"));
    leaveMenu->addActions(mPowerManager->availableActions());
    menu->addActions(mScreenSaver->availableActions());

    QMenu *oldMenu = mMenu;
    mMenu = menu;
    delete oldMenu;
}

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

 ************************************************/
QDialog *RazorMainMenu::configureDialog()
{
    return new RazorMainMenuConfiguration(*settings(), DEFAULT_SHORTCUT);
}

#undef DEFAULT_SHORTCUT


try
        {
            AppendInitializer(std::static_pointer_cast<InstantiableBase>(CreateCollisionScene(s, false)), ret);
        }
        catch (std::exception& e)
        {
            WARNING_NAMED("Setup::GetInitializers", "CollisionScene '" << s << "' not built. Won't be able to instantiate.");
            continue;
        }

// All rights reserved.

#include <type_traits>

#include <exotica_core/dynamics_solver.h>
#include <exotica_core/scene.h>
#include <exotica_core/server.h>
#include <exotica_core/setup.h>
#include <exotica_core/tools.h>

namespace exotica
{
SetupPtr Setup::singleton_initialiser_ = nullptr;

void Setup::Destroy()
{
    Server::Destroy();
    if (singleton_initialiser_) singleton_initialiser_.reset();
}

void Setup::PrintSupportedClasses()
{
    HIGHLIGHT("Registered solvers:");
    std::vector<std::string> solvers = Instance()->solvers_.getDeclaredClasses();
    for (const std::string& s : solvers)
    {
        HIGHLIGHT(" '" << s << "'");
    }
    HIGHLIGHT("Registered problems:");
    std::vector<std::string> problems = Instance()->problems_.GetDeclaredClasses();
    for (const std::string& s : problems)
    {
        HIGHLIGHT(" '" << s << "'");
    }
    HIGHLIGHT("Registered task maps:");
    std::vector<std::string> maps = Instance()->maps_.getDeclaredClasses();
    for (const std::string& s : maps)
    {
        HIGHLIGHT(" '" << s << "'");
    }
    HIGHLIGHT("Registered collision scenes:");
    std::vector<std::string> scenes = Instance()->collision_scenes_.getDeclaredClasses();
    for (const std::string& s : scenes)
    {
        HIGHLIGHT(" '" << s << "'");
    }
    HIGHLIGHT("Registered dynamics solvers:");
    std::vector<std::string> dynamics_solvers = Instance()->dynamics_solvers_.getDeclaredClasses();
    for (const std::string& s : dynamics_solvers)
    {
        HIGHLIGHT(" '" << s << "'");
    }
}

void AppendInitializer(std::shared_ptr<InstantiableBase> it, std::vector<Initializer>& ret)
{
    std::vector<Initializer> temps = it->GetAllTemplates();
    for (Initializer& i : temps)
    {
        bool found = false;
        for (Initializer& j : ret)
        {
            if (i.GetName() == j.GetName())
            {
                found = true;
                break;
            }
        }
        if (!found)
        {
            ret.push_back(i);
        }
    }
}

std::vector<Initializer> Setup::GetInitializers()
{
    std::vector<Initializer> ret = Scene().GetAllTemplates();
    std::vector<std::string> solvers = Instance()->solvers_.getDeclaredClasses();
    for (const std::string& s : solvers)
    {
        try
        {
            AppendInitializer(std::static_pointer_cast<InstantiableBase>(CreateSolver(s, false)), ret);
        }
        catch (std::exception& e)
        {
            WARNING_NAMED("Setup::GetInitializers", "Solver '" << s << "' not built. Won't be able to instantiate.");
            continue;
        }
    }
    std::vector<std::string> maps = Instance()->maps_.getDeclaredClasses();
    for (const std::string& s : maps)
    {
        try
        {
            AppendInitializer(std::static_pointer_cast<InstantiableBase>(CreateMap(s, false)), ret);
        }
        catch (std::exception& e)
        {
            WARNING_NAMED("Setup::GetInitializers", "TaskMap '" << s << "' not built. Won't be able to instantiate.");
            continue;
        }
    }
    std::vector<std::string> collision_scenes = Instance()->collision_scenes_.getDeclaredClasses();
    for (const std::string& s : collision_scenes)
    {

    }
    std::vector<std::string> dynamics_solvers = Instance()->dynamics_solvers_.getDeclaredClasses();
    for (const std::string& s : dynamics_solvers)
    {
        try
        {
            AppendInitializer(std::static_pointer_cast<InstantiableBase>(CreateDynamicsSolver(s, false)), ret);
        }
        catch (std::exception& e)
        {
            WARNING_NAMED("Setup::GetInitializers", "DynamicsSolver '" << s << "' not built. Won't be able to instantiate.");
            continue;
        }
    }
    return ret;
}

std::vector<std::string> Setup::GetSolvers() { return Instance()->solvers_.getDeclaredClasses(); }
std::vector<std::string> Setup::GetProblems() { return Instance()->problems_.GetDeclaredClasses(); }
std::vector<std::string> Setup::GetMaps() { return Instance()->maps_.getDeclaredClasses(); }
std::vector<std::string> Setup::GetCollisionScenes() { return Instance()->collision_scenes_.getDeclaredClasses(); }
std::vector<std::string> Setup::GetDynamicsSolvers() { return Instance()->dynamics_solvers_.getDeclaredClasses(); }
Setup::Setup() : solvers_("exotica_core", "exotica::MotionSolver"),
                 maps_("exotica_core", "exotica::TaskMap"),
                 problems_(PlanningProblemFac::Instance()),
                 collision_scenes_("exotica_core", "exotica::CollisionScene"),
                 dynamics_solvers_("exotica_core", "exotica::DynamicsSolver")
{
}
}  // namespace exotica


Tensor pdist(const Tensor& self, const double p) {
  TORCH_CHECK(self.dim() == 2,
      "pdist only supports 2D tensors, got: ", self.dim(), "D");
  TORCH_CHECK(at::isFloatingType(self.scalar_type()), "pdist only supports floating-point dtypes");
  TORCH_CHECK(p >= 0, "pdist only supports non-negative p values");
  return at::_pdist_forward(self.contiguous(), p);
}

#include <ATen/ATen.h>
#include <ATen/Dispatch.h>
#include <ATen/NativeFunctions.h>
#include <ATen/NamedTensorUtils.h>
#include <ATen/ExpandUtils.h>
#include <ATen/native/Distance.h>

namespace at { namespace native {

DEFINE_DISPATCH(pdist_forward_stub);
DEFINE_DISPATCH(pdist_backward_stub);
DEFINE_DISPATCH(cdist_stub);
DEFINE_DISPATCH(cdist_backward_stub);

Tensor pairwise_distance(const Tensor& x1, const Tensor& x2, double p, double eps, bool keepdim) {
  return at::norm(x1 - x2 + eps, p, 1, keepdim);
}

// This is to guarantee that the contiguous memory is passed to the backward pass

Tensor euclidean_dist_out(const Tensor& x1, const Tensor& x2) {
  Tensor x1_norm = x1.pow(2).sum(-1, true);
  Tensor x1_pad = at::ones_like(x1_norm, LEGACY_CONTIGUOUS_MEMORY_FORMAT);
  Tensor x2_norm = x2.pow(2).sum(-1, true);
  Tensor x2_pad = at::ones_like(x2_norm, LEGACY_CONTIGUOUS_MEMORY_FORMAT);
  Tensor x1_ = at::cat({x1.mul(-2), x1_norm, x1_pad}, -1);
  Tensor x2_ = at::cat({x2, x2_pad, x2_norm}, -1);
  Tensor result = x1_.matmul(x2_.transpose(-2, -1));
  result.clamp_min_(0).sqrt_();
  return result;
}

static Tensor cdist_impl(const Tensor& x1, const Tensor& x2, const double p, c10::optional<int64_t> compute_mode) {
  TORCH_CHECK(at::isFloatingType(x1.scalar_type()), "cdist only supports floating-point dtypes, X1 got: ", x1.scalar_type());
  auto device1 = x1.type().device_type();
  TORCH_CHECK(device1 == kCPU || device1 == kCUDA, "cdist only supports CPU and CUDA devices, X1 got: ", device1);
  TORCH_CHECK(at::isFloatingType(x1.scalar_type()), "cdist only supports floating-point dtypes, X2 got: ", x2.scalar_type());
  auto device2 = x2.type().device_type();
  TORCH_CHECK(device2 == kCPU || device2 == kCUDA, "cdist only supports CPU and CUDA devices, X2 got: ", device2);
  TORCH_CHECK(p >= 0, "cdist only supports non-negative p values");
  TORCH_CHECK(device1 == device2, "X1 and X2 must have the same device type. X1: ", device1, " X2: ", device2);
  TORCH_CHECK(!x1.is_cuda() || x1.get_device() == x2.get_device(), "device of X1 (", x1.get_device(), ") must match device of X2 (", x2.get_device(), ")");
  int64_t c1 = x1.size(-1);
  int64_t c2 = x2.size(-1);
  // 0 - default value. If p = 2 and r1 > 25 or r2 > 25 (these values are based on performance metrics),
  // it will try to compute distance using matrix multiplication approach
  // 1 - force to use matrix multiplication for p = 2
  // 2 - do not use matrix multiplication for p = 2
  int64_t mode = compute_mode.value_or(0);
  TORCH_CHECK(mode >= 0 && mode <= 2, "possible modes: 0, 1, 2, but was: ", mode);

  int64_t r1 = x1.size(-2);
  int64_t r2 = x2.size(-2);
  auto dim1 = x1.dim();
  auto dim2 = x2.dim();

  //For batch calculation we expand all dimensions(except the last two) to one, with size that equals to product of them.
  //The last two dimensions will stay the same
  IntArrayRef batch_tensor1(x1.sizes().data(), dim1 - 2);
  IntArrayRef batch_tensor2(x2.sizes().data(), dim2 - 2);
  std::vector<int64_t> expand_batch_portion = infer_size(batch_tensor1, batch_tensor2);
  std::vector<int64_t> tensor1_expand_size(expand_batch_portion);
  tensor1_expand_size.insert(tensor1_expand_size.end(), {r1, c1});
  std::vector<int64_t> tensor2_expand_size(expand_batch_portion);
  tensor2_expand_size.insert(tensor2_expand_size.end(), {r2, c2});

  int expand_batch_product = std::accumulate(expand_batch_portion.begin(), expand_batch_portion.end(), 1, std::multiplies<int64_t>());
  std::vector<int64_t> tensor1_view{expand_batch_product, r1, c1};
  std::vector<int64_t> tensor2_view{expand_batch_product, r2, c2};

  Tensor tensor1_expanded = x1.expand(tensor1_expand_size).contiguous().view(tensor1_view);
  Tensor tensor2_expanded = x2.expand(tensor2_expand_size).contiguous().view(tensor2_view);

  std::vector<int64_t> output_shape(expand_batch_portion);
  output_shape.insert(output_shape.end(), {r1, r2});

  Tensor result;
  if (r1 == 0 || r2 == 0) {
    result = at::empty(output_shape, x1.options());
  } else if (c1 == 0) {
    result = at::zeros(output_shape, x1.options());
  } else if (p == 2 && (mode == 1 || (mode == 0 && (r1 > 25 || r2 > 25)))) {
    Tensor dist = (expand_batch_product == 1) ? euclidean_dist_out(x1, x2) :
                  euclidean_dist_out(tensor1_expanded, tensor2_expanded);
    result = dist.view(output_shape);
  } else {
    result = at::empty(output_shape, x1.options());
    cdist_stub(device1, result, tensor1_expanded, tensor2_expanded, p);
  }
  return result;
}

Tensor cdist(const Tensor& x1, const Tensor& x2, const double p, c10::optional<int64_t> compute_mode) {
  TORCH_CHECK(x1.dim() >= 2, "cdist only supports at least 2D tensors, X1 got: ", x1.dim(), "D");
  TORCH_CHECK(x2.dim() >= 2, "cdist only supports at least 2D tensors, X2 got: ", x2.dim(), "D");
  TORCH_CHECK(x1.size(-1) == x2.size(-1), "X1 and X2 must have the same number of columns. X1: ", x1.size(-1), " X2: ", x2.size(-1));
#ifdef BUILD_NAMEDTENSOR
  auto maybe_outnames = namedinference::compute_cdist_outnames(x1, x2);
#endif
  auto result = [&]() {
#ifdef BUILD_NAMEDTENSOR
    NoNamesGuard guard;
#endif
    return cdist_impl(x1, x2, p, compute_mode);
  }();
#ifdef BUILD_NAMEDTENSOR
  namedinference::propagate_names_if_nonempty(result, maybe_outnames);
#endif
  return result;
}

Tensor _cdist_backward(const Tensor& grad, const Tensor& x1, const Tensor& x2, const double p, const Tensor& cdist) {
  TORCH_CHECK(x1.is_contiguous(), "_cdist_backward requires X1 to be contiguous");
  TORCH_CHECK(x2.is_contiguous(), "_cdist_backward requires X2 to be contiguous");
  TORCH_CHECK(cdist.is_contiguous(), "_cdist_backward requires dist to be contiguous");
  TORCH_CHECK(grad.is_contiguous(), "_cdist_backward requires grad to be contiguous");
  int64_t n = x1.size(-2);
  int64_t m = x1.size(-1);
  auto device1 = x1.type().device_type();
  TORCH_CHECK(device1 == kCPU || device1 == kCUDA, "_cdist_backward only supports CPU and CUDA devices, X1 got: ", device1);
  auto device2 = x2.type().device_type();
  TORCH_CHECK(device2 == kCPU || device2 == kCUDA, "_cdist_backward only supports CPU and CUDA devices, X2 got: ", device2);
  IntArrayRef batch_tensor1(x1.sizes().data(), std::max<int64_t>(x1.dim() - 2, 0));
  int batch_product = std::accumulate(batch_tensor1.begin(), batch_tensor1.end(), 1, std::multiplies<int64_t>());
  Tensor grad_x1 = at::empty_like(x1, x1.options(), LEGACY_CONTIGUOUS_MEMORY_FORMAT).view({batch_product, n, m});
  cdist_backward_stub(device1, grad_x1, grad, x1, x2, p, cdist);
  return grad_x1;
}

Tensor _pdist_forward(const Tensor& self, const double p) {
  TORCH_CHECK(self.is_contiguous(), "_pdist_forward requires contiguous input");
  auto device = self.type().device_type();
  TORCH_CHECK(device == kCPU || device == kCUDA, "_pdist_forward only supports CPU and CUDA devices, got: ", device);
  Tensor result = at::empty({0}, self.options(), LEGACY_CONTIGUOUS_MEMORY_FORMAT);
  if (self.size(0) <= 1) {
    result.resize_({0});
  } else {
    int64_t n = self.size(0);
    int64_t c = n * (n - 1) / 2;
    result.resize_({c});
    if (self.size(1) == 0) {
      result.fill_(0);
    } else {
      pdist_forward_stub(device, result, self, p);
    }
  }
  return result;
}

Tensor _pdist_backward(const Tensor& grad, const Tensor& self, const double p, const Tensor& pdist) {
  TORCH_CHECK(self.is_contiguous(), "_pdist_backward requires self to be contiguous");
  TORCH_CHECK(pdist.is_contiguous(), "_pdist_backward requires pdist to be contiguous");
  auto device = self.type().device_type();
  TORCH_CHECK(device == kCPU || device == kCUDA, "_pdist_backward only supports CPU and CUDA devices, got: ", device);
  Tensor result = at::empty_like(self, LEGACY_CONTIGUOUS_MEMORY_FORMAT);
  pdist_backward_stub(device, result, grad, self, p, pdist);
  return result;
}

Tensor cosine_similarity(const Tensor& x1, const Tensor& x2, int64_t dim, double eps) {
  // Follow scipy impl to improve numerical precision
  // Use x / sqrt(x * x) instead of x / (sqrt(x) * sqrt(x))
  Tensor w12 = at::sum(x1 * x2, dim);
  Tensor w1 = at::sum(x1 * x1, dim);
  Tensor w2 = at::sum(x2 * x2, dim);
  Tensor n12 = (w1 * w2).clamp_min_(eps * eps).sqrt_();
  return w12.div_(n12);
}

}}  // namespace at::native


void CChessCountInfo::OnDestroy()
{
	//清理资源
	m_Brush.DeleteObject();

	__super::OnDestroy();
}

#include "Stdafx.h"
#include "Resource.h"
#include "ChessCountInfo.h"

BEGIN_MESSAGE_MAP(CChessCountInfo, CDialog)
	ON_WM_PAINT()
	ON_WM_DESTROY()
	ON_WM_CTLCOLOR()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

//构造函数
CChessCountInfo::CChessCountInfo() : CDialog(IDD_CHESS_COUNT)
{
	//设置变量
	m_fBlackCount=0;
	m_fWhiteCount=0;
	m_fCommomCount=0;

	//加载资源
	m_ImageBack.SetLoadInfo(IDB_CHESS_COUNT_INFO,AfxGetInstanceHandle());

	return;
}

//析构函数
CChessCountInfo::~CChessCountInfo()
{
}

//控件绑定
void CChessCountInfo::DoDataExchange(CDataExchange* pDX)
{
	__super::DoDataExchange(pDX);
	DDX_Control(pDX, IDOK, m_btOk);
	DDX_Control(pDX, IDCANCEL, m_btCancel);
}

//初始化函数
BOOL CChessCountInfo::OnInitDialog()
{
	__super::OnInitDialog();

	//创建画刷
	m_Brush.CreateSolidBrush(RGB(245,238,221));

	//设置标题
	SetWindowText(TEXT("点目结果："));

	//移动窗口
	CImageHandle BackImageHandle(&m_ImageBack);
	SetWindowPos(NULL,0,0,m_ImageBack.GetWidth(),m_ImageBack.GetHeight(),SWP_NOZORDER|SWP_NOMOVE);

	//设置按钮
	m_btOk.SetButtonImage(IDB_COUNT_BT_ACCEPT,AfxGetInstanceHandle(),false);
	m_btCancel.SetButtonImage(IDB_COUNT_BT_CONTINUE,AfxGetInstanceHandle(),false);

	return TRUE;
}

//确定消息
void CChessCountInfo::OnOK()
{
	//发送消息
	AfxGetMainWnd()->PostMessage(IDM_CHESS_COUNT_RESPONSION,1,1);

	//关闭窗口
	DestroyWindow();

	return;
}

//取消消息
void CChessCountInfo::OnCancel()
{
	//发送消息
	AfxGetMainWnd()->PostMessage(IDM_CHESS_COUNT_RESPONSION,0,0);

	//关闭窗口
	DestroyWindow();

	return;
}

//设置信息
void CChessCountInfo::SetChessCountInfo(float fBlackCount, float fWhiteCount, float fCommomCount)
{
	//设置变量
	m_fBlackCount=fBlackCount;
	m_fWhiteCount=fWhiteCount;
	m_fCommomCount=fCommomCount;

	//更新界面
	if (GetSafeHwnd()!=NULL) Invalidate(FALSE);

	return;
}

//重画函数
void CChessCountInfo::OnPaint()
{
	CPaintDC dc(this);

	//绘画背景
	CImageHandle BackImageHandle(&m_ImageBack);
	m_ImageBack.BitBlt(dc,0,0);

	//设置 DC
	dc.SetBkMode(TRANSPARENT);
	dc.SetTextColor(RGB(10,10,10));
	dc.SelectObject(CSkinResourceManager::GetDefaultFont());

	//绘画信息
	TCHAR szBuffer[64];
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fBlackCount);
	dc.TextOut(133,57,szBuffer,lstrlen(szBuffer));
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fWhiteCount);
	dc.TextOut(133,83,szBuffer,lstrlen(szBuffer));
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fCommomCount);
	dc.TextOut(133,110,szBuffer,lstrlen(szBuffer));

	return;
}

//绘画背景
BOOL CChessCountInfo::OnEraseBkgnd(CDC * pDC)
{
	Invalidate(FALSE);
	UpdateWindow();

	return TRUE;
}

//销毁消息

//鼠标消息
void CChessCountInfo::OnLButtonDown(UINT nFlags, CPoint point)
{
	__super::OnLButtonDown(nFlags,point);

	//消息模拟
	PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));

	return;
}

//控件颜色
HBRUSH CChessCountInfo::OnCtlColor(CDC * pDC, CWnd * pWnd, UINT nCtlColor)
{
	switch (nCtlColor)
	{
	case CTLCOLOR_DLG:
	case CTLCOLOR_STATIC:
		{
			pDC->SetBkMode(TRANSPARENT);
			pDC->SetTextColor(RGB(10,10,10));
			pDC->SetBkColor(RGB(245,238,221));
			return m_Brush;
		}
	}
	return __super::OnCtlColor(pDC,pWnd,nCtlColor);
}


switch (nCtlColor)
	{
	case CTLCOLOR_DLG:
	case CTLCOLOR_STATIC:
		{
			pDC->SetBkMode(TRANSPARENT);
			pDC->SetTextColor(RGB(10,10,10));
			pDC->SetBkColor(RGB(245,238,221));
			return m_Brush;
		}
	}

#include "Stdafx.h"
#include "Resource.h"
#include "ChessCountInfo.h"

BEGIN_MESSAGE_MAP(CChessCountInfo, CDialog)
	ON_WM_PAINT()
	ON_WM_DESTROY()
	ON_WM_CTLCOLOR()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

//构造函数
CChessCountInfo::CChessCountInfo() : CDialog(IDD_CHESS_COUNT)
{
	//设置变量
	m_fBlackCount=0;
	m_fWhiteCount=0;
	m_fCommomCount=0;

	//加载资源
	m_ImageBack.SetLoadInfo(IDB_CHESS_COUNT_INFO,AfxGetInstanceHandle());

	return;
}

//析构函数
CChessCountInfo::~CChessCountInfo()
{
}

//控件绑定
void CChessCountInfo::DoDataExchange(CDataExchange* pDX)
{
	__super::DoDataExchange(pDX);
	DDX_Control(pDX, IDOK, m_btOk);
	DDX_Control(pDX, IDCANCEL, m_btCancel);
}

//初始化函数
BOOL CChessCountInfo::OnInitDialog()
{
	__super::OnInitDialog();

	//创建画刷
	m_Brush.CreateSolidBrush(RGB(245,238,221));

	//设置标题
	SetWindowText(TEXT("点目结果："));

	//移动窗口
	CImageHandle BackImageHandle(&m_ImageBack);
	SetWindowPos(NULL,0,0,m_ImageBack.GetWidth(),m_ImageBack.GetHeight(),SWP_NOZORDER|SWP_NOMOVE);

	//设置按钮
	m_btOk.SetButtonImage(IDB_COUNT_BT_ACCEPT,AfxGetInstanceHandle(),false);
	m_btCancel.SetButtonImage(IDB_COUNT_BT_CONTINUE,AfxGetInstanceHandle(),false);

	return TRUE;
}

//确定消息
void CChessCountInfo::OnOK()
{
	//发送消息
	AfxGetMainWnd()->PostMessage(IDM_CHESS_COUNT_RESPONSION,1,1);

	//关闭窗口
	DestroyWindow();

	return;
}

//取消消息
void CChessCountInfo::OnCancel()
{
	//发送消息
	AfxGetMainWnd()->PostMessage(IDM_CHESS_COUNT_RESPONSION,0,0);

	//关闭窗口
	DestroyWindow();

	return;
}

//设置信息
void CChessCountInfo::SetChessCountInfo(float fBlackCount, float fWhiteCount, float fCommomCount)
{
	//设置变量
	m_fBlackCount=fBlackCount;
	m_fWhiteCount=fWhiteCount;
	m_fCommomCount=fCommomCount;

	//更新界面
	if (GetSafeHwnd()!=NULL) Invalidate(FALSE);

	return;
}

//重画函数
void CChessCountInfo::OnPaint()
{
	CPaintDC dc(this);

	//绘画背景
	CImageHandle BackImageHandle(&m_ImageBack);
	m_ImageBack.BitBlt(dc,0,0);

	//设置 DC
	dc.SetBkMode(TRANSPARENT);
	dc.SetTextColor(RGB(10,10,10));
	dc.SelectObject(CSkinResourceManager::GetDefaultFont());

	//绘画信息
	TCHAR szBuffer[64];
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fBlackCount);
	dc.TextOut(133,57,szBuffer,lstrlen(szBuffer));
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fWhiteCount);
	dc.TextOut(133,83,szBuffer,lstrlen(szBuffer));
	_snprintf(szBuffer,sizeof(szBuffer),TEXT("%0.1f"),m_fCommomCount);
	dc.TextOut(133,110,szBuffer,lstrlen(szBuffer));

	return;
}

//绘画背景
BOOL CChessCountInfo::OnEraseBkgnd(CDC * pDC)
{
	Invalidate(FALSE);
	UpdateWindow();

	return TRUE;
}

//销毁消息
void CChessCountInfo::OnDestroy()
{
	//清理资源
	m_Brush.DeleteObject();

	__super::OnDestroy();
}

//鼠标消息
void CChessCountInfo::OnLButtonDown(UINT nFlags, CPoint point)
{
	__super::OnLButtonDown(nFlags,point);

	//消息模拟
	PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));

	return;
}

//控件颜色
HBRUSH CChessCountInfo::OnCtlColor(CDC * pDC, CWnd * pWnd, UINT nCtlColor)
{

	return __super::OnCtlColor(pDC,pWnd,nCtlColor);
}