//compares the vector (start,destA) with (start,destB). returns a value between 0 (perfect agreement) and 60 (polar opposite direction)
int angularDeviation(int iStartX, int iStartY, int iDestAX, int iDestAY, int iDestBX, int iDestBY)
{
	//undefined
	if (iStartX==iDestAX && iStartY==iDestAY)
		return 0;
	if (iStartX==iDestBX && iStartY==iDestBY)
		return 0;

	int iStartXHex = xToHexspaceX(iStartX,iStartY);
	int iDestAXHex = xToHexspaceX(iDestAX,iDestAY);
	int iDestBXHex = xToHexspaceX(iDestBX,iDestBY);
	int iDXA = dxWrap(iDestAXHex - iStartXHex);
	int iDYA = dyWrap(iDestAY - iStartY);
	int iDXB = dxWrap(iDestBXHex - iStartXHex);
	int iDYB = dyWrap(iDestBY - iStartY);

	//reconstruct the Z coordinate
	int iStartZ = -iStartXHex-iStartY;
	int iDestAZ = -iDestAXHex-iDestAY;
	int iDestBZ = -iDestBXHex-iDestBY;
	int iDZA = iDestAZ - iStartZ;
	int iDZB = iDestBZ - iStartZ;

	float fRawDotProduct = (float) iDXA *iDXB + iDYA * iDYB + iDZA * iDZB;
	float fNormA2 = (float) iDXA*iDXA + iDYA*iDYA + iDZA*iDZA;
	float fNormB2 = (float) iDXB*iDXB + iDYB*iDYB + iDZB*iDZB;

	//this should be between -1 and +1
	float fNormDotProduct = fRawDotProduct / sqrtf( fNormA2*fNormB2 );
	//this should be between 0 and 60
	return (int)((fNormDotProduct-1)*(-30));
}
int plotCityXY(const CvCity* pCity, const CvPlot* pPlot)
{

	int iDX;
	int iWrappedDX = dxWrap(pPlot->getX() - pCity->getX());
	int iWrappedDY = dyWrap(pPlot->getY() - pCity->getY());
	int iDY = iWrappedDY;

	// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
	int iCityHexX = xToHexspaceX(pCity->getX(), pCity->getY());
	int iPlotHexX = xToHexspaceX(pCity->getX() + iWrappedDX, pCity->getY() + iWrappedDY);

	iDX = dxWrap(iPlotHexX - iCityHexX);

#if defined(MOD_GLOBAL_CITY_WORKING)
	if(hexDistance(iDX, iDY) > pCity->getWorkPlotDistance())
#else
	if(hexDistance(iDX, iDY) > CITY_PLOTS_RADIUS)
#endif
	{
		return -1;
	}
	else
	{
#if defined(MOD_GLOBAL_CITY_WORKING)
		// Regardless of the working radius, we need to offset into the array by the maximum radius
		return GC.getXYCityPlot((iDX + MAX_CITY_RADIUS), (iDY + MAX_CITY_RADIUS));
#else
		return GC.getXYCityPlot((iDX + CITY_PLOTS_RADIUS), (iDY + CITY_PLOTS_RADIUS));
#endif
	}
}
DirectionTypes estimateDirection(int iStartX, int iStartY, int iDestX, int iDestY)
{
	int iStartXHex = xToHexspaceX(iStartX,iStartY);
	int iDestXHex = xToHexspaceX(iDestX,iDestY);

	int iDX = dxWrap(iDestXHex - iStartXHex);
	int iDY = dyWrap(iDestY - iStartY);

	//undefined
	if (iDX==0 && iDY==0)
		return NO_DIRECTION;

	//reconstruct the Z coordinate
	int iStartZ = -iStartXHex-iStartY;
	int iDestZ = -iDestXHex-iDestY;
	int iDZ = iDestZ - iStartZ;

	float maximum = 0;
	int maximumIndex = -1;
	for(int i=0; i<6; i++)
	{
		float dotProduct = iDX * hexspaceDirections[i][0] + iDY * hexspaceDirections[i][1] + iDZ * hexspaceDirections[i][2];
		if(dotProduct > maximum)
		{
			maximum = dotProduct;
			maximumIndex = i;
		}
	}

	return (DirectionTypes)maximumIndex;
}
int plotDistance(int iX1, int iY1, int iX2, int iY2)
{
	int iX1H = xToHexspaceX(iX1,iY1);
	int iX2H = xToHexspaceX(iX2,iY2);
	//reconstruct the Z coordinate
	int iZ1H = -iX1H-iY1;
	int iZ2H = -iX2H-iY2;

	int iDX = dxWrap(iX2H - iX1H);
	int iDY = dyWrap(iY2 - iY1);
	int iDZ = dxWrap(iZ2H - iZ1H); //this is by design, dx and dz have the same range

	return (abs(iDX) + abs(iDY) + abs(iDZ)) / 2;
}
int getRingIterationIndex(const CvPlot* pCenter, const CvPlot* pPlot)
{
	int iWrappedDX = dxWrap(pPlot->getX() - pCenter->getX());
	int iWrappedDY = dyWrap(pPlot->getY() - pCenter->getY());

	// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
	int iCenterHexX = xToHexspaceX(pCenter->getX(), pCenter->getY());
	int iPlotHexX = xToHexspaceX(pCenter->getX() + iWrappedDX, pCenter->getY() + iWrappedDY);

	int iDX = dxWrap(iPlotHexX - iCenterHexX);
	int iDY = iWrappedDY;

	// Regardless of the working radius, we need to offset into the array by the maximum radius
	return GC.getRingIterationIndexHex((iDX + MAX_CITY_RADIUS), (iDY + MAX_CITY_RADIUS));
}
CvPlot* plotXY(int iX, int iY, int iDX, int iDY)
{
	// convert the start coord to hex-space coordinates
	int iStartHexX = xToHexspaceX(iX, iY);

	int iPlotHexX = iStartHexX + iDX;
	int iPlotY = iY + iDY; // Y is the same in both coordinate systems

	// convert from hex-space coordinates to the storage array
	iPlotHexX = hexspaceXToX(iPlotHexX, iPlotY);

	return GC.getMap().plot(iPlotHexX , iPlotY);
}
int plotCityXY(const CvCity* pCity, const CvPlot* pPlot)
{

	int iDX;
	int iWrappedDX = dxWrap(pPlot->getX() - pCity->getX());
	int iWrappedDY = dyWrap(pPlot->getY() - pCity->getY());
	int iDY = iWrappedDY;

	// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
	int iCityHexX = xToHexspaceX(pCity->getX(), pCity->getY());
	int iPlotHexX = xToHexspaceX(pCity->getX() + iWrappedDX, pCity->getY() + iWrappedDY);

	iDX = dxWrap(iPlotHexX - iCityHexX);

	if(hexDistance(iDX, iDY) > CITY_PLOTS_RADIUS)
	{
		return -1;
	}
	else
	{
		return GC.getXYCityPlot((iDX + CITY_PLOTS_RADIUS), (iDY + CITY_PLOTS_RADIUS));
	}
}
void CvDistanceMap::Dump(const char* filename)
{
	ofstream out(filename);
	if (out)
	{
		out << "#x,y,hx,hz,water,distance,id,owner\n"; 
		for (int i=0; i<GC.getMap().numPlots(); i++)
		{
			CvPlot* pPlot =  GC.getMap().plotByIndexUnchecked(i);
			int xHex = xToHexspaceX(pPlot->getX(),pPlot->getY());
			int zHex = -xHex-pPlot->getY();

			out << pPlot->getX() << "," << pPlot->getY() << "," << xHex << "," << zHex << "," << (pPlot->isWater() ? 1 : 0) << "," 
				<< GetClosestFeatureDistance(*pPlot) << "," << GetClosestFeatureID(*pPlot) << "," << GetClosestFeatureOwner(*pPlot) << "\n";
		}
	}
	out.close();

}
CvPlot* plotDirection(int iX, int iY, DirectionTypes eDirection)
{
#if defined(MOD_BALANCE_CORE)
	return GC.getMap().getNeighborUnchecked(iX,iY,eDirection);
#else
	if(eDirection == NO_DIRECTION)
	{
		return GC.getMap().plot(iX, iY);
	}
	else
	{
		// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
		iX = xToHexspaceX(iX , iY);
		iX += GC.getPlotDirectionX()[eDirection];
		iY += GC.getPlotDirectionY()[eDirection];

		// convert from hex-space coordinates to the storage array
		iX = hexspaceXToX(iX, iY);

		return GC.getMap().plot(iX, iY);
	}
#endif
}
/// This function will return the CvPlot associated with the Index (0 to 36) of a City at iX,iY.  The lower the Index the closer the Plot is to the City (roughly)
CvPlot* iterateRingPlots(int iX, int iY, int iIndex)
{
	int iDeltaHexX = 0;
	int iDeltaHexY = 0;

	if(iIndex < MAX_CITY_PLOTS)
	{
		iDeltaHexX = GC.getCityPlotX()[iIndex]; // getCityPlotX now uses hex-space coords
		iDeltaHexY = GC.getCityPlotY()[iIndex];
	}
	else
	{
		// loop till we find the ring this is on
		int iThisRing = 0;
		int iHighestValueOnThisRing = 0;
		int iLowestValueOnThisRing = 0;
		while(iHighestValueOnThisRing < iIndex)
		{
			iThisRing++;
			iLowestValueOnThisRing = iHighestValueOnThisRing + 1;
			iHighestValueOnThisRing += iThisRing*6;
		}
		// determine what side of the hex we are on
		int iDiff = (iIndex - iLowestValueOnThisRing);
		int iSide = iDiff / iThisRing;
		int iOffset = iDiff % iThisRing;

		switch(iSide)
		{
		case 0:
			iDeltaHexX = 0 + iOffset;
			iDeltaHexY = iThisRing - iOffset;
			break;
		case 1:
			iDeltaHexX = iThisRing;
			iDeltaHexY = 0 - iOffset;
			break;
		case 2:
			iDeltaHexX = iThisRing - iOffset;
			iDeltaHexY = -iThisRing;
			break;
		case 3:
			iDeltaHexX = 0 - iOffset;
			iDeltaHexY = -iThisRing + iOffset;
			break;
		case 4:
			iDeltaHexX = -iThisRing;
			iDeltaHexY = 0 + iOffset;
			break;
		case 5:
			iDeltaHexX = -iThisRing + iOffset;
			iDeltaHexY = iThisRing;
			break;
		default:
			return 0;
		}

	}
	// convert the city coord to hex-space coordinates
	int iCityHexX = xToHexspaceX(iX, iY);

	int iPlotHexX = iCityHexX + iDeltaHexX;
	int iPlotY = iY + iDeltaHexY; // Y is the same in both coordinate systems

	// convert from hex-space coordinates to the storage array
	int iPlotX = hexspaceXToX(iPlotHexX, iPlotY);

	return GC.getMap().plot(iPlotX , iPlotY);
}