using System;
using System.Drawing;
//using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
//using System.Threading;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


//-1,-1       +1,-1
//
//
//       0,0
//
//
//-1,+1       +1,+1
namespace DarkStrideToolbox
{
	#region Namespace Resources
	public class PreVertexBuffer2D
	{
		public CustomVertex.TransformedTextured v0, v1, v2, v3; // Four corners of  quad
	};
	public class PreVertexBuffer
	{
		public CustomVertex.PositionColoredTextured v0, v1, v2, v3; // Four corners of  quad
	};
	#endregion
	
	public class DSGraphicsWrapper
	{
		#region Properties
		//These variables are used in the 2d Texture rendering to prevent having to do a complex calc
		//every time through the rendering
		private double m_n2DSpriteScalingPreCalculation_X = 0;
		private double m_n2DSpriteScalingPreCalculation_CalcSrcWidth = 0;
		private double m_n2DSpriteScalingPreCalculation_CalcTrgWidth = 0;
		private double m_n2DSpriteScalingPreCalculation_Y = 0;
		private double m_n2DSpriteScalingPreCalculation_CalcSrcHeight = 0;
		private double m_n2DSpriteScalingPreCalculation_CalcTrgHeight = 0;

		private long m_nPolygonsRendered = 0;
		private System.Windows.Forms.Control m_oWinForm = null;

		private Microsoft.DirectX.Direct3D.Device m_oDirect3DDevice = null;

		private DSSortedList m_oPreVertexBuffers = new DSSortedList();
		private DSSortedList m_oVertexBuffers = new DSSortedList();

		//03/10/2005 Chris Hill  DX9c uses a Font not a GraphicsFont class, damn MS.
		//Font for drawing text
		//private GraphicsFont m_oFont = null;
		private Microsoft.DirectX.Direct3D.Font m_oFontArial = null;
		private string m_cFontName = "Arial";

		//This is the amount of space taken up by one character of the standard font.
		private Vector2 m_oFontSize = new Vector2();
		
		private bool m_bHasFocus = true;

		private Microsoft.DirectX.Direct3D.Sprite m_oSpriteEngine = null;

		private System.Windows.Forms.Form m_oTopmostForm = null;
		private Vector2 m_o2DWorldViewUpperLeft;

		//07/14/2005 Chris Hill  We cache this because apparently calculating it costs us about 20fps.
		private string m_sBackBufferSize = "";
		//private int m_nScreenWidth = -1;
		//private int m_nScreenHeight = -1;
		#endregion	


		public DSGraphicsWrapper(){}


		public void Initialize( System.Windows.Forms.Control oCtrl )
		{
			PresentParameters presentParams = null;


			//Save our form and device
			m_oWinForm = oCtrl;

			//Setup our event handling for the form
			this.TopmostForm.Disposed 	+= new System.EventHandler( this.Disposed );
			this.TopmostForm.Deactivate += new System.EventHandler( this.Deactivate );
			this.TopmostForm.Activated 	+= new System.EventHandler( this.Activated );
			this.TopmostForm.Resize 	+= new System.EventHandler( this.Resize );

			//Create our graphics engine
			presentParams = new PresentParameters();
			presentParams.Windowed = true;
			presentParams.BackBufferCount = 1;
			presentParams.MultiSample = Microsoft.DirectX.Direct3D.MultiSampleType.None;
			presentParams.MultiSampleQuality = 0;
			presentParams.SwapEffect = SwapEffect.Discard;
			presentParams.EnableAutoDepthStencil = true;
			presentParams.AutoDepthStencilFormat = Microsoft.DirectX.Direct3D.DepthFormat.D16;
			presentParams.PresentFlag = PresentFlag.None;
			presentParams.BackBufferWidth  = oCtrl.ClientRectangle.Right - oCtrl.ClientRectangle.Left;
			presentParams.BackBufferHeight = oCtrl.ClientRectangle.Bottom - oCtrl.ClientRectangle.Top;
			presentParams.BackBufferFormat = Microsoft.DirectX.Direct3D.Format.X8R8G8B8;
			presentParams.FullScreenRefreshRateInHz = 0;
			presentParams.PresentationInterval = PresentInterval.Immediate;

			m_oDirect3DDevice = new Microsoft.DirectX.Direct3D.Device(
									0, DeviceType.Hardware, oCtrl, 
									CreateFlags.HardwareVertexProcessing | CreateFlags.PureDevice, 
									presentParams);

			m_oSpriteEngine = new Microsoft.DirectX.Direct3D.Sprite( m_oDirect3DDevice );


			//Get some basic information setup, for now we can call resize to make it easy but that
			//may need to change as we go forward.
			Resize( null,null );


			//03/10/2005 Chris Hill  Have to switch objects, damn DX9c.
			//Initalize our font
			//m_oFont = new GraphicsFont( "Courier New", System.Drawing.FontStyle.Regular,10 );
			// Pass in DrawTextFormat.NoClip so we don't have to calc the bottom/right of the rect
			// Initialize all of the fonts
			m_oFontArial = new Microsoft.DirectX.Direct3D.Font(
									m_oDirect3DDevice, 14, 0, FontWeight.Regular,
									1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, 
									PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, m_cFontName );
			m_oFontSize.X = 8;
			m_oFontSize.Y = 17;
		}


		public virtual void Disposed(Object sender, System.EventArgs e)
		{
			if( m_oDirect3DDevice != null )
			{
				m_oDirect3DDevice.Dispose();
				m_oDirect3DDevice = null;
			}				
		}

		public virtual void Deactivate(Object sender, System.EventArgs e)
		{
			
			m_bHasFocus = false;
		}
		public virtual void Activated(Object sender, System.EventArgs e)
		{
			m_bHasFocus = true;
		}

		public virtual void Resize(Object sender, System.EventArgs e)
		{
			//07/29/2005 Chris Hill  PresentationParameters.BackBufferWidth/Height seems to be inconsistant
			//in what it returns.  So for now we'll make our own storage system.
			if( m_oDirect3DDevice != null )
			{
				//m_nScreenWidth = m_oDirect3DDevice.PresentationParameters.BackBufferWidth;
				//m_nScreenHeight = m_oDirect3DDevice.PresentationParameters.BackBufferHeight;

				//Get some basic information
				m_sBackBufferSize = String.Format( "{0}x{1}",
									m_oDirect3DDevice.PresentationParameters.BackBufferWidth.ToString(),
									m_oDirect3DDevice.PresentationParameters.BackBufferHeight.ToString() );
			}
		}


		#region Color Functions
		public int GetRGBA( int nRGB, int nAlpha )
		{
			int nRetVal = 0;	
			int nRed = 0;
			int nGreen = 0;
			int nBlue = 0;

			nRed = GetRed( nRGB );
			nGreen = GetGreen( nRGB );
			nBlue = GetBlue( nRGB );

			nRetVal = GetRGBA( nRed,nGreen,nBlue,nAlpha );

			return( nRetVal );
		}
		public int GetRGBA( int nRed,int nGreen, int nBlue, int nAlpha )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetRGBA";
			int nRetVal = 0;			

			try
			{
				nRetVal = (
					(((nAlpha)&0xff)<<24) | 
					(((nRed)&0xff)<<16) | 
					(((nGreen)&0xff)<<8) |
					((nBlue)&0xff)
					);
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		public int GetRGB( int nRed,int nGreen, int nBlue )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetRGB";
			int nRetVal = 0;			

			try
			{
				nRetVal = (
					(((0xff)<<24) |
					(((nRed)&0xff)<<16) |
					(((nGreen)&0xff)<<8) | 
					((nBlue)&0xff))
					);
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		public int GetBlue( int nColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetBlue";
			int nRetVal = 0;			

			try
			{
				nRetVal = ((byte)(nColor));
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		public int GetGreen( int nColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetGreen";
			int nRetVal = 0;			

			try
			{
				//nRetVal = ((byte)(((word)(nColor)) >> 8));
				nRetVal = ((byte)((nColor) >> 8));
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		public int GetRed( int nColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetRed";
			int nRetVal = 0;			

			try
			{
				nRetVal = ((byte)((nColor)>>16));
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		public int GetAlpha( int nColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.GetAlpha";
			int nRetVal = 0;			

			try
			{
				nRetVal = ((byte)((nColor)>>24));
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( nRetVal );
		}
		#endregion

		#region Render Functions
		public Vector3 RayPick( Point o2DPoint,ref Vector3 vRayPickOrigin,ref Vector3 vRayPickDir )
		{
			const string sRoutineName = "DarkStrideToolbox.DSGraphicsWrapper.RayPick";	
			Vector3 vRayPick;
			Microsoft.DirectX.Direct3D.Surface oSurface = null;
			Microsoft.DirectX.Direct3D.SurfaceDescription oSurf;
			Matrix matViewMatrix;
			float nDelta = 0;
			Vector3 vHitPoint = new Vector3();

			try
			{
				vRayPickDir = new Vector3( 0,0,0 );
				vRayPickOrigin = new Vector3( 0,0,0 );
				oSurface = m_oDirect3DDevice.GetBackBuffer( 0,0, Microsoft.DirectX.Direct3D.BackBufferType.Mono );
				oSurf = oSurface.Description;

				//Get the pick ray from the mouse position
				//Compute the vector of the pick ray in screen space
				vRayPick.X =  ( ( ( 2.0f * o2DPoint.X ) / oSurf.Width  ) - 1 ) / m_oDirect3DDevice.Transform.Projection.M11;
				vRayPick.Y = -( ( ( 2.0f * o2DPoint.Y ) / oSurf.Height ) - 1 ) / m_oDirect3DDevice.Transform.Projection.M22;
				vRayPick.Z =  1.0f;

				//Get the inverse view matrix
				matViewMatrix = m_oDirect3DDevice.Transform.View;
				matViewMatrix.Invert();

				//Transform the screen space pick ray into 3D space
				vRayPickDir.X  = vRayPick.X*matViewMatrix.M11 + vRayPick.Y*matViewMatrix.M21 + vRayPick.Z*matViewMatrix.M31;
				vRayPickDir.Y  = vRayPick.X*matViewMatrix.M12 + vRayPick.Y*matViewMatrix.M22 + vRayPick.Z*matViewMatrix.M32;
				vRayPickDir.Z  = vRayPick.X*matViewMatrix.M13 + vRayPick.Y*matViewMatrix.M23 + vRayPick.Z*matViewMatrix.M33;
				vRayPickDir.Normalize();

				vRayPickOrigin.X = matViewMatrix.M41;
				vRayPickOrigin.Y = matViewMatrix.M42;
				vRayPickOrigin.Z = matViewMatrix.M43;

				//Calc origin as intersection with near frustum
				vRayPickOrigin += vRayPickDir*1;//NEAR_Z;

				nDelta = -vRayPickOrigin.Y/vRayPickDir.Y;
				vHitPoint = vRayPickDir;
				vHitPoint.Multiply( nDelta );
				vHitPoint = vRayPickOrigin + vHitPoint;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
			return( vHitPoint );
		}

		public void RenderTexture2D( string sTextureKey, System.Drawing.Rectangle oSourceRect, System.Drawing.Rectangle oTargetRect,
									 Vector2 vRotateAround, double nAngle, double nPercentTransparent,
									 bool bRelativeToUpperLeftCorner, int nColorARGB )
		{
			System.Drawing.Rectangle oClippingRegion = new System.Drawing.Rectangle( 0,0,0,0 );

			RenderTexture2D( sTextureKey, oSourceRect, oTargetRect, oClippingRegion, false, vRotateAround, 
							 nAngle, nPercentTransparent, bRelativeToUpperLeftCorner, nColorARGB );
		}
		public void RenderTexture2D( string sTextureKey, System.Drawing.Rectangle oSourceRect, System.Drawing.Rectangle oTargetRect,
									 System.Drawing.Rectangle oClippingRegion,
									 Vector2 vRotateAround, double nAngle, double nPercentTransparent,
									 bool bRelativeToUpperLeftCorner, int nColorARGB )
		{
			RenderTexture2D( sTextureKey, oSourceRect, oTargetRect, oClippingRegion, true, vRotateAround, 
							 nAngle, nPercentTransparent, bRelativeToUpperLeftCorner, nColorARGB );
		}
		private void RenderTexture2D( string sTextureKey, System.Drawing.Rectangle oSourceRect, System.Drawing.Rectangle oTargetRect,
			 						  System.Drawing.Rectangle oClippingRegion, bool bUseClipRegion,
									  Vector2 vRotateAround, double nAngle, double nPercentTransparent,
									  bool bRelativeToUpperLeftCorner, int nColorARGB )
		{
			bool bRender = true;
			Vector2 vNewTrgRectPos, vNewTrgRectSize;
			Vector2 vNewSrcRectPos, vNewSrcRectSize;
			System.Drawing.Rectangle oNewTrgRect;
			System.Drawing.Rectangle oNewSrcRect;
			Microsoft.DirectX.Direct3D.ColorValue oColor;
			LoadedTexture oTexture = null;
			double nAngleToCenterAtZeroDegrees = 0;
			double nNewAngle = 0;
			double nHypothonuse = 0;
			double nCenterStartX = 0;
			double nCenterStartY = 0;
			double nCenterStopX = 0;
			double nCenterStopY = 0;
			float fScaleX = 0;
			float fScaleY = 0;
			float nPercntLost = 0;
			float nTrgAmountLost = 0;
			float nSrcAmountLost = 0;
			int nCompositeColor = 0;


			oTexture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( sTextureKey );

			//If the rectangle they passed in was empty then default it for them
			if( oSourceRect == System.Drawing.Rectangle.Empty )
			{
				//All graphics have to be powers of two for dimensions, so we created the "Real Pic" 
				//properties to represent the real borders.  Respect that.
				vNewSrcRectPos = new Vector2( oTexture.m_nRealPicLeft,oTexture.m_nRealPicTop );
				vNewSrcRectSize = new Vector2(	oTexture.Size.X,oTexture.Size.Y );
			}
			else
			{
				vNewSrcRectPos = new Vector2( oSourceRect.X,oSourceRect.Y );
				vNewSrcRectSize = new Vector2( oSourceRect.Width,oSourceRect.Height );
			}

			//If the rectangle they passed in was empty then default it for them
			if( oTargetRect == System.Drawing.Rectangle.Empty )
			{
				//All graphics have to be powers of two for dimensions, so we created the "Real Pic" 
				//properties to represent the real borders.  Respect that.
				vNewTrgRectPos = new Vector2( oTexture.m_nRealPicLeft,oTexture.m_nRealPicTop );
				vNewTrgRectSize = new Vector2(	oTexture.Size.X,oTexture.Size.Y );
			}
			else
			{
				vNewTrgRectPos = new Vector2( oTargetRect.X,oTargetRect.Y );
				vNewTrgRectSize = new Vector2( oTargetRect.Width,oTargetRect.Height );
			}

			//Adjust the target size if they pass in zero
			if( vNewTrgRectSize.X == 0 && vNewTrgRectSize.Y == 0 )
			{
				vNewTrgRectSize = vNewSrcRectSize;
			}

			//If this is supposed to be relative to the screen then adjust it
			if( bRelativeToUpperLeftCorner == false )
			{
				vNewTrgRectPos -= m_o2DWorldViewUpperLeft;
			}

			//07/27/2005 Chris Hill  Setup the transparancy.  However some things may choose to pass in 
			//their own transparancy figures.  So if the transparancy isn't 0 then apply it.
			if( nPercentTransparent != 0 )
			{
				oColor = Microsoft.DirectX.Direct3D.ColorValue.FromArgb( nColorARGB );
				oColor.Alpha = 1.0f - (float)nPercentTransparent;
				nCompositeColor = oColor.ToArgb();
			}
			else
			{
				nCompositeColor = nColorARGB;
			}

			//Now impliment clipping...
			if( bUseClipRegion == true && oClippingRegion != System.Drawing.Rectangle.Empty )
			{
				if( vNewTrgRectPos.X < oClippingRegion.X )
				{
					nTrgAmountLost = oClippingRegion.X - vNewTrgRectPos.X;
					nPercntLost = nTrgAmountLost / vNewTrgRectSize.X;
					nSrcAmountLost = (int)( vNewSrcRectSize.X * nPercntLost );
					if( nPercntLost > 1 )
					{
						bRender = false;
					}
					else 
					{
						vNewSrcRectPos.X += nSrcAmountLost;
						vNewSrcRectSize.X -= nSrcAmountLost;
						vNewTrgRectSize.X -= nTrgAmountLost;
						vNewTrgRectPos.X = oClippingRegion.X;
					}
				}
				if( vNewTrgRectPos.X + vNewTrgRectSize.X > oClippingRegion.Right )
				{
					nTrgAmountLost = vNewTrgRectPos.X + vNewTrgRectSize.X - oClippingRegion.Right;
					nPercntLost = nTrgAmountLost / vNewTrgRectSize.X;
					nSrcAmountLost = (int)( vNewSrcRectSize.X * nPercntLost );
					if( nPercntLost > 1 )
					{
						bRender = false;
					}
					else 
					{
						vNewSrcRectSize.X -= nSrcAmountLost;
						vNewTrgRectSize.X -= nTrgAmountLost;
					}
				}
				if( vNewTrgRectPos.Y < oClippingRegion.Y )
				{
					nTrgAmountLost = oClippingRegion.Y - vNewTrgRectPos.Y;
					nPercntLost = nTrgAmountLost / vNewTrgRectSize.Y;
					nSrcAmountLost = (int)( vNewSrcRectSize.Y * nPercntLost );
					if( nPercntLost > 1 )
					{
						bRender = false;
					}
					else 
					{
						vNewSrcRectPos.Y += nSrcAmountLost;
						vNewSrcRectSize.Y -= nSrcAmountLost;
						vNewTrgRectSize.Y -= nTrgAmountLost;
						vNewTrgRectPos.Y = oClippingRegion.Y;
					}
				}
				if( vNewTrgRectPos.Y + vNewTrgRectSize.Y > oClippingRegion.Bottom )
				{
					nTrgAmountLost = vNewTrgRectPos.Y + vNewTrgRectSize.Y - oClippingRegion.Bottom;
					nPercntLost = nTrgAmountLost / vNewTrgRectSize.Y;
					nSrcAmountLost = (int)( vNewSrcRectSize.Y * nPercntLost );
					if( nPercntLost > 1 )
					{
						bRender = false;
					}
					else
					{
						vNewSrcRectSize.Y -= nSrcAmountLost;
						vNewTrgRectSize.Y -= nTrgAmountLost;
					}
				}
			}

			fScaleX = (float)vNewTrgRectSize.X / (float)vNewSrcRectSize.X;
			fScaleY = (float)vNewTrgRectSize.Y / (float)vNewSrcRectSize.Y;

			//Don't draw the item if its totally off screen
			if( ( vNewTrgRectPos.X + vNewTrgRectSize.X > 0 || vNewTrgRectPos.X < m_oDirect3DDevice.Viewport.Width )
				&&
				( vNewTrgRectPos.Y + vNewTrgRectSize.Y > 0 || vNewTrgRectPos.Y < m_oDirect3DDevice.Viewport.Height ) 
				&&
				bRender == true )
			{
				//Now do our final float to int conversion.  This helps us avoid repeated float loss.
				oNewTrgRect = new Rectangle( (int)vNewTrgRectPos.X,(int)vNewTrgRectPos.Y,(int)vNewTrgRectSize.X,(int)vNewTrgRectSize.Y );
				oNewSrcRect = new Rectangle( (int)vNewSrcRectPos.X,(int)vNewSrcRectPos.Y,(int)vNewSrcRectSize.X,(int)vNewSrcRectSize.Y );

				this.PolygonsRendered++;

				//Apply scaling based on global scale values (set in SetTileDestinationSize())
				//Notice that we are modifying the scale to correct to the next pixel.  D3D raster rules state that the 
				//midpoint of a pixel must be covered to draw to the upper left pixel.  To gaurauntee that 
				//no seams occur, this call will modify the destination size by adding a pixel width and height per scale multiplier (rounded 
				//to the nearest pixel)
				if( m_n2DSpriteScalingPreCalculation_CalcSrcWidth != oNewSrcRect.Width ||
					m_n2DSpriteScalingPreCalculation_CalcSrcHeight != oNewSrcRect.Height ||
					m_n2DSpriteScalingPreCalculation_CalcTrgWidth != oNewTrgRect.Width ||
					m_n2DSpriteScalingPreCalculation_CalcTrgHeight != oNewTrgRect.Height )
				{
					m_n2DSpriteScalingPreCalculation_CalcSrcWidth = oNewSrcRect.Width;
					m_n2DSpriteScalingPreCalculation_CalcSrcHeight = oNewSrcRect.Height;
					m_n2DSpriteScalingPreCalculation_CalcTrgWidth = oNewTrgRect.Width;
					m_n2DSpriteScalingPreCalculation_CalcTrgHeight = oNewTrgRect.Height;

					//Apply scaling based on global scale values (set in SetTileDestinationSize())
					//Notice that we are modifying the scale to correct to the next pixel.  D3D raster rules state that the 
					//midpoint of a pixel must be covered to draw to the upper left pixel.  To gaurauntee that 
					//no seams occur, this call will modify the destination size by adding a pixel width and height per scale multiplier (rounded 
					//to the nearest pixel)
					//m_n2DSpriteScalingPreCalculation_X = (oNewTrgRect.Width +(Math.Round((double)oNewTrgRect.Width / (double)oNewSrcRect.Width))) / oNewSrcRect.Width;
					//m_n2DSpriteScalingPreCalculation_Y = (oNewTrgRect.Height +(Math.Round((double)oNewTrgRect.Height / (double)oNewSrcRect.Height))) / oNewSrcRect.Height;
					m_n2DSpriteScalingPreCalculation_X = (double)oNewTrgRect.Width / (double)oNewSrcRect.Width;
					m_n2DSpriteScalingPreCalculation_Y = (double)oNewTrgRect.Height / (double)oNewSrcRect.Height;
				}

				m_oSpriteEngine.Transform = 
											Matrix.RotationZ( (float)nAngle ) * 
											Matrix.Scaling( (float)m_n2DSpriteScalingPreCalculation_X,
															(float)m_n2DSpriteScalingPreCalculation_Y, 1f);// *

				//Since the order of operations is to transform the position first, then apply the transform set in
				//sprite.Transform, the inverse of the matrix is needed to properly pre-position the sprite before the
				//entire batch is scaled approriately
				Matrix tInverse = Matrix.Invert(m_oSpriteEngine.Transform);
				//we lock the postions to integers for ease of transformation to screen coords
				Vector3 vPosition = new Vector3(vNewTrgRectPos.X,vNewTrgRectPos.Y,0);

				//Calculate the hypothonuse of the triangle
				if( nAngle != 0 )
				{
					if( vRotateAround.X == 0 )
					{
						nAngleToCenterAtZeroDegrees = 0;
					}
					else
					{
						nAngleToCenterAtZeroDegrees = Math.Atan( vRotateAround.Y / vRotateAround.X );
					}

					nNewAngle = nAngleToCenterAtZeroDegrees + nAngle;
					nHypothonuse = Math.Sqrt( vRotateAround.X * vRotateAround.X + vRotateAround.Y * vRotateAround.Y );
					nCenterStartX = vRotateAround.X;
					nCenterStartY = vRotateAround.Y;
					nCenterStopX = Math.Cos( nNewAngle ) * nHypothonuse;
					nCenterStopY = Math.Sin( nNewAngle ) * nHypothonuse;
					
					vPosition = new Vector3( (float)( vNewTrgRectPos.X + ( nCenterStartX - nCenterStopX ) ),
											(float)( vNewTrgRectPos.Y + ( nCenterStartY - nCenterStopY ) ),0 );
				}
				else
				{
					vPosition = new Vector3( (float)( vNewTrgRectPos.X ),
											 (float)( vNewTrgRectPos.Y ),0 );
				}
				//setting the center to 0,0,0 makes the upper left corner of the sprite the "origin" of the sprite for transformation
				//since we aren't rotating, this is acceptable
				Vector3 vAdjustedPosition = Vector3.TransformCoordinate(vPosition, tInverse);

				// Finally draw the sprite
				m_oSpriteEngine.Draw( oTexture.oTexture,oNewSrcRect, 
									  Vector3.Empty,
									  vAdjustedPosition, nCompositeColor );

				m_oSpriteEngine.Transform = Matrix.Identity;
			}
		}


		public void CreateVertexBuffer( string sKey,Vector3 vUpperLeft,Vector3 vLowerLeft,Vector3 vUpperRight,Vector3 vLowerRight )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.CreateVertexBuffer";
			PreVertexBuffer oPreVertexBuffer;
			VertexBuffer oVertexBuffer = null;  
			CustomVertex.PositionColoredTextured[] oaVertexes = null;

			try
			{
				//Create the pre VB
				oPreVertexBuffer = new PreVertexBuffer();
				oPreVertexBuffer.v0 = new CustomVertex.PositionColoredTextured( vUpperLeft ,Color.Black.ToArgb(),0.0f,1.0f );
				oPreVertexBuffer.v1 = new CustomVertex.PositionColoredTextured( vLowerLeft ,Color.Black.ToArgb(),0.0f,0.0f );
				oPreVertexBuffer.v2 = new CustomVertex.PositionColoredTextured( vUpperRight,Color.Black.ToArgb(),1.0f,1.0f );
				oPreVertexBuffer.v3 = new CustomVertex.PositionColoredTextured( vLowerRight,Color.Black.ToArgb(),1.0f,0.0f );


				//Create the vertex buffer
				DirectXException.IgnoreExceptions();
				//Create a quad for rendering each tree
				oVertexBuffer = new VertexBuffer( typeof(CustomVertex.PositionColoredTextured), 1*4, m_oDirect3DDevice, Usage.WriteOnly, CustomVertex.PositionColoredTextured.Format, Pool.Default );
				//Copy tree DSMesh data into VertexBuffer
				oaVertexes = new CustomVertex.PositionColoredTextured[ 1*4 ];
		
				oaVertexes[0] = oPreVertexBuffer.v0;
				oaVertexes[1] = oPreVertexBuffer.v1;
				oaVertexes[2] = oPreVertexBuffer.v2;
				oaVertexes[3] = oPreVertexBuffer.v3;

				oVertexBuffer.SetData( oaVertexes,0,0 );


				//Save our work
				m_oPreVertexBuffers.Add( sKey,oPreVertexBuffer );
				m_oVertexBuffers.Add( sKey,oVertexBuffer );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}

		public void RenderVertexBuffer( string sVertexBufferKey,string sTextureKey,Matrix matWorld )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderVertexBuffer";
			LoadedTexture oLoadedTexture = null;
			VertexBuffer oVertexBuffer = null;

			try
			{
				oLoadedTexture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( sTextureKey );
				oVertexBuffer  = (VertexBuffer)m_oVertexBuffers.GetByKey( sVertexBufferKey );

				RenderVertexBuffer( oVertexBuffer,oLoadedTexture,matWorld );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RenderVertexBuffer( string sVertexBufferKey,Matrix matWorld )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderVertexBuffer";
			VertexBuffer oVertexBuffer = null;

			try
			{
				oVertexBuffer  = (VertexBuffer)m_oVertexBuffers.GetByKey( sVertexBufferKey );
				RenderVertexBuffer( oVertexBuffer,null,matWorld );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RenderVertexBuffer( VertexBuffer oVertexBuffer,LoadedTexture oLoadedTexture,Matrix matWorld )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderVertexBuffer";
			Material oWhiteMaterial,oOrigMaterial;
			//Chris 9c
			//Microsoft.DirectX.Direct3D.RenderStates oOldRenderStates = new Microsoft.DirectX.Direct3D.RenderStates();

			try
			{
				if( oVertexBuffer == null )
				{
					throw new System.Exception( "Vertex Buffer cannot be null" );
				}
				else if( oLoadedTexture == null )
				{
					throw new System.Exception( "Loaded Texture cannot be null" );
				}

				//Chris 9c
				//Save our original settings
				//CopyRenderStates( m_oDirect3DDevice.RenderState,oOldRenderStates );
				oOrigMaterial = m_oDirect3DDevice.Material;
				bool bAlphaTestEnable = m_oDirect3DDevice.RenderState.AlphaTestEnable;
				bool bAlphaBlendEnable = m_oDirect3DDevice.RenderState.AlphaBlendEnable;

				//Configure the render state
				m_oDirect3DDevice.SetStreamSource( 0,oVertexBuffer,0 );
				m_oDirect3DDevice.VertexFormat = CustomVertex.PositionColoredTextured.Format;
				m_oDirect3DDevice.RenderState.AlphaTestEnable = true;
				m_oDirect3DDevice.RenderState.AlphaBlendEnable = true;

				//Setup our material
				oWhiteMaterial = new Material();
				oWhiteMaterial.Ambient = System.Drawing.Color.White;
				m_oDirect3DDevice.Material = oWhiteMaterial;

				// Set the tree texture
				m_oDirect3DDevice.SetTexture(0, oLoadedTexture.oTexture);
	
				m_oDirect3DDevice.Transform.World = matWorld;

				// Render the billboard
				m_oDirect3DDevice.DrawPrimitives( PrimitiveType.TriangleStrip,0,2 );

				//Restore our defaults
				//Chris 9c
				m_oDirect3DDevice.RenderState.AlphaTestEnable = bAlphaTestEnable;
				m_oDirect3DDevice.RenderState.AlphaBlendEnable = bAlphaBlendEnable;
				//CopyRenderStates( oOldRenderStates,m_oDirect3DDevice.RenderState );
				m_oDirect3DDevice.SetTexture( 0,null );
				m_oDirect3DDevice.Material = oOrigMaterial;
				m_oDirect3DDevice.Transform.World = Matrix.Identity;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RenderVertexBuffer( VertexBuffer oVertexBuffer,Matrix matWorld )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderVertexBuffer";

			try
			{
				RenderVertexBuffer( oVertexBuffer,null,matWorld );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}


		public void RenderText( double nX,double nY,Color cColor,string sText )
		{
			m_oFontArial.DrawText(	m_oSpriteEngine, sText, 
					new System.Drawing.Rectangle( Convert.ToInt32( nX ),Convert.ToInt32( nY ),0,0 ), 
					DrawTextFormat.NoClip,cColor.ToArgb() );
		}
		public void RenderText( double nX,double nY,Color cColor,string sText,System.Drawing.Rectangle oClipRegion )
		{
			if( nX < oClipRegion.Right && 
				nX + sText.Length * m_oFontSize.X > oClipRegion.Left &&
				nY < oClipRegion.Bottom && 
				nY + m_oFontSize.Y > oClipRegion.Top )
			{
				

				m_oFontArial.DrawText(	m_oSpriteEngine, sText, 
						new System.Drawing.Rectangle( Convert.ToInt32( nX ),Convert.ToInt32( nY ),0,0 ), 
						DrawTextFormat.NoClip,cColor.ToArgb() );
			}
		}

		public void RenderText(string text, double nX,double nY,double nWidth,double nHeight, bool shadow,BlendColor oFontColor,DrawTextFormat m_oTextFormat )
		{
			Rectangle oControlBox = new Rectangle( (int)nX,(int)nY,(int)nWidth,(int)nHeight );

			RenderText( text,oControlBox,shadow,oFontColor,m_oTextFormat );
		}
		public void RenderText(string text, System.Drawing.Rectangle rect,BlendColor oFontColor,DrawTextFormat m_oTextFormat) { this.RenderText(text,  rect, false,oFontColor,m_oTextFormat ); }
		public void RenderText(string text, System.Drawing.Rectangle rect, bool shadow,BlendColor oFontColor,DrawTextFormat m_oTextFormat )
		{
			RenderText( text,rect,System.Drawing.Rectangle.Empty,shadow,oFontColor,m_oTextFormat );
		}
		public void RenderText(string text, System.Drawing.Rectangle rect, System.Drawing.Rectangle oClipRegion, bool shadow,BlendColor oFontColor,DrawTextFormat m_oTextFormat )
		{
			// No need to draw fully transparant layers
			if (oFontColor.Current.Alpha == 0)
				return; // Nothing to do

			System.Drawing.Rectangle screenRect = rect;


			if( oClipRegion == System.Drawing.Rectangle.Empty ||
			    (
					rect.Left < oClipRegion.Right && rect.Right > oClipRegion.Left &&
					rect.Top < oClipRegion.Bottom && rect.Bottom > oClipRegion.Top
				)
			  )
			{
				// Get the font node here
				if( shadow )
				{
					// Render the text shadowed
					System.Drawing.Rectangle shadowRect = screenRect;
					shadowRect.Offset(1, 1);
					this.FontArial.DrawText( m_oSpriteEngine,text,shadowRect, m_oTextFormat, unchecked((int)0xff000000) );
				}
				this.FontArial.DrawText( m_oSpriteEngine,text,screenRect, m_oTextFormat, oFontColor.Current.ToArgb() );
			}
		}


		public void RenderLine3D( Vector3[] oaLinePoints,Color oLineColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderLine";
			Material matColor;
			Material matBlank;
			CustomVertex.PositionColored[] oaPoints;
			int nLength = 0;
			
			try
			{
				//Create the materials we will be using
				matColor = new Material();
				matColor.Diffuse = oLineColor;
				matColor.Ambient = oLineColor;
				matColor.Emissive = oLineColor;

				matBlank = new Material();
				matBlank.Diffuse = Color.White;
				matBlank.Ambient = Color.White;


				//Set the material color we need
				m_oDirect3DDevice.Material = matColor;

				//Set the vertex format that the vertex buffer uses
				m_oDirect3DDevice.VertexFormat = CustomVertex.PositionColored.Format;			
	
				//Now lets convert the vector array into a pisition colored array
				nLength = oaLinePoints.GetLength( 0 );
				oaPoints = new CustomVertex.PositionColored[ nLength ];

				for( int i=0 ; i<nLength ; i++ )
				{
					oaPoints[i] = new CustomVertex.PositionColored();

					
					oaPoints[i].X = oaLinePoints[i].X;
					oaPoints[i].Y = oaLinePoints[i].Y;
					oaPoints[i].Z = oaLinePoints[i].Z;
					oaPoints[i].Color	= Color.White.ToArgb();
				}

				m_oDirect3DDevice.DrawUserPrimitives( PrimitiveType.LineStrip,nLength-1,oaPoints );

				//Restore the state
				m_oDirect3DDevice.Material = matBlank;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		public void RenderLine2D( Vector3[] oaLinePoints,Color oLineColor )
		{
			const string sRoutineName = "DarkStrideToolbox.DXGraphcisWrapper.RenderLine";
			Material matColor;
			Material matBlank;
			CustomVertex.PositionColored[] oaPoints;
			int nLength = 0;
			
			try
			{
				//Create the materials we will be using
				matColor = new Material();
				matColor.Diffuse = oLineColor;
				matColor.Ambient = oLineColor;
				matColor.Emissive = oLineColor;

				matBlank = new Material();
				matBlank.Diffuse = Color.White;
				matBlank.Ambient = Color.White;


				//Set the material color we need
				m_oDirect3DDevice.Material = matColor;

				//Set the vertex format that the vertex buffer uses
				m_oDirect3DDevice.VertexFormat = CustomVertex.PositionColored.Format;			
	
				//Now lets convert the vector array into a pisition colored array
				nLength = oaLinePoints.GetLength( 0 );
				oaPoints = new CustomVertex.PositionColored[ nLength ];

				for( int i=0 ; i<nLength ; i++ )
				{
					oaPoints[i] = new CustomVertex.PositionColored();

					//Convert to our display system
					
					oaPoints[i].X = (float)( (float)oaLinePoints[i].X - ( this.ScreenWidth / 2.0f ) ) / ( this.ScreenWidth / 2.0f );
					oaPoints[i].Y = (float)( (float)oaLinePoints[i].Y - ( this.ScreenHeight / 2.0f ) ) / ( this.ScreenHeight / 2.0f );
					oaPoints[i].Z = 0;
					oaPoints[i].Color = Color.White.ToArgb();
				}

				m_oDirect3DDevice.DrawUserPrimitives( PrimitiveType.LineStrip,nLength-1,oaPoints );

				//Restore the state
				m_oDirect3DDevice.Material = matBlank;
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}



		public void RenderRect2D( System.Drawing.Rectangle oTargetRect,int nColorARGB )
		{
			RenderRect2D( oTargetRect,nColorARGB,0 );
		}
		public void RenderRect2D( System.Drawing.Rectangle oTargetRect,int nColorARGB,double nPercentTransparent )
		{
			RenderTexture2D( "System_NoTexture", Rectangle.Empty, oTargetRect,
							 new Vector2( 0,0 ),0,nPercentTransparent,true,nColorARGB );
		}



		public void Tile( System.Drawing.Rectangle oTargetRect,string sGraphicKey )
		{
			int nStartingX = 0;
			int nStartingY = 0;
			System.Drawing.Rectangle oTargetPos;
			LoadedTexture oTexture = null;


			oTexture = DSResourceManager.GetGlobalInstance().GetLoadedTexture( sGraphicKey );
			nStartingX = oTargetRect.X;
			nStartingY = oTargetRect.Y;


			//Now draw the joining peice
			while( nStartingY < oTargetRect.Bottom )
			{
				oTargetPos = new System.Drawing.Rectangle( nStartingX,nStartingY,
														   (int)oTexture.Size.X,(int)oTexture.Size.Y );

				//Draw our square
				RenderTexture2D( sGraphicKey,
								 System.Drawing.Rectangle.Empty, oTargetPos, oTargetRect,
								 new Vector2( 0,0 ), 0, 0, false, System.Drawing.Color.White.ToArgb() );

				//Advance our location
				nStartingX += oTargetPos.Width;
				//nStartingY += oTargetPos.Height;

				//Roll us into the next y if needed
				if( nStartingX >= oTargetRect.Right )
				{
					nStartingX = oTargetRect.X;
					nStartingY += oTargetPos.Height;
				}
			}
		}



		//This sets the viewpoint as the center of the world
		public void Set2DWorldViewArea( Vector2 oPointOfWorldAtCenterOfScreen )
		{
			const string sRoutineName = "DarkStrideToolbox.GameEngine.Set2DWorldViewArea";

			try
			{
				m_o2DWorldViewUpperLeft = new Vector2( oPointOfWorldAtCenterOfScreen.X - m_oDirect3DDevice.Viewport.Width / 2,
													   oPointOfWorldAtCenterOfScreen.Y - m_oDirect3DDevice.Viewport.Height / 2 );
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed.",oEx );
			}
		}
		#endregion


		#region Properties
		public Microsoft.DirectX.Direct3D.Device Direct3DDevice
		{
			get
			{
				return( m_oDirect3DDevice );
			}
		}

		public System.Windows.Forms.Control Form
		{
			get
			{
				return( m_oWinForm );
			}
		}
		public System.Windows.Forms.Form TopmostForm
		{
			get
			{
				System.Windows.Forms.Control oLoopObj = this.Form;


				if( m_oTopmostForm == null )
				{
					if( this.Form.Parent != null )
					{
						//Loop until we get to the topmost parent... which has to be the form
						oLoopObj = this.Form.Parent;
						while( oLoopObj.GetType().BaseType.ToString() != "System.Windows.Forms.Form" )
						{
							oLoopObj = oLoopObj.Parent;
						}
					}
					else if( oLoopObj.GetType().BaseType.ToString() != "System.Windows.Forms.Form" )
					{
						oLoopObj = null;
					}

					m_oTopmostForm = (System.Windows.Forms.Form)oLoopObj;
				}

				return( m_oTopmostForm );
			}
		}
		public DSSortedList VertexBuffers
		{
			get
			{
				return( m_oVertexBuffers );
			}
		}
		public Vector2 WorldViewUpperLeft
		{
			get
			{
				return( m_o2DWorldViewUpperLeft );
			}
			set
			{
				m_o2DWorldViewUpperLeft = value;
			}
		}
		public Vector2 FontSize
		{
			get
			{
				return( m_oFontSize );
			}
		}
		public Microsoft.DirectX.Direct3D.Font FontArial
		{
			get
			{
				return( m_oFontArial );
			}
		}
		public Microsoft.DirectX.Direct3D.Sprite SpriteEngine
		{
			get
			{
				return( m_oSpriteEngine );
			}
		}
		public bool HasFocus
		{
			get
			{
				return( m_bHasFocus );
			}
			set
			{
				m_bHasFocus = value;
			}
		}
		public long PolygonsRendered
		{
			get
			{
				return( m_nPolygonsRendered );
			}
			set
			{
				m_nPolygonsRendered = value;
			}
		}
		public string BackBufferSize
		{
			get
			{
				return( m_sBackBufferSize );
			}
		}
		public int ScreenWidth
		{
			get
			{
				//return( m_nScreenWidth );
				return( m_oDirect3DDevice.PresentationParameters.BackBufferWidth );
			}
		}
		public int ScreenHeight
		{
			get
			{
				//return( m_nScreenHeight );
				return( m_oDirect3DDevice.PresentationParameters.BackBufferHeight );
			}
		}
		#endregion
	}
}