/*
* Copyright (c) 2007-2009 SlimDX Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D9;
using SharpDX.DXGI;
using System.Diagnostics;

using Rectangle = System.Drawing.Rectangle;

namespace SampleFramework
{
    /// <summary>
    /// Handles the configuration and management of the graphics device.
    /// </summary>
    public class GraphicsDeviceManager : IDisposable
    {
        Game game;
        bool ignoreSizeChanges;
        bool deviceLost;
//        bool doNotStoreBufferSize;
//        bool renderingOccluded;

        int fullscreenWindowWidth;
        int fullscreenWindowHeight;
        int windowedWindowWidth;
        int windowedWindowHeight;
        WINDOWPLACEMENT windowedPlacement;
        long windowedStyle;
        bool savedTopmost;

#if TEST_Direct3D9Ex
		internal static Direct3DEx Direct3D9Object			// yyagi
#else
		internal static Direct3D Direct3D9Object
#endif
		{
            get;
            private set;
        }

        public DeviceSettings CurrentSettings
        {
            get;
            private set;
        }
        public bool IsWindowed
        {
            get { return CurrentSettings.Windowed; }
        }
        public int ScreenWidth
        {
            get { return CurrentSettings.BackBufferWidth; }
        }
        public int ScreenHeight
        {
            get { return CurrentSettings.BackBufferHeight; }
        }
        public Size ScreenSize
        {
            get { return new Size(CurrentSettings.BackBufferWidth, CurrentSettings.BackBufferHeight); }
        }
        public Direct3D9Manager Direct3D9
        {
            get;
            private set;
        }
        public string DeviceStatistics
        {
            get;
            private set;
        }
        public string DeviceInformation
        {
            get;
            private set;
        }

		public GraphicsDeviceManager( Game game )
		{
			if( game == null )
				throw new ArgumentNullException( "game" );

			this.game = game;

			game.Window.ScreenChanged += Window_ScreenChanged;
			game.Window.UserResized += Window_UserResized;

			game.FrameStart += game_FrameStart;
			game.FrameEnd += game_FrameEnd;

			Direct3D9 = new Direct3D9Manager( this );
		}

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

		public void ChangeDevice( DeviceSettings settings, DeviceSettings minimumSettings )
		{
			if( settings == null )
				throw new ArgumentNullException( "settings" );

			Enumeration9.MinimumSettings = minimumSettings;

			DeviceSettings validSettings = DeviceSettings.FindValidSettings( settings );

			var pp = validSettings.Direct3D9.PresentParameters;
			pp.DeviceWindowHandle = game.Window.Handle;
			validSettings.Direct3D9.PresentParameters = pp;

			CreateDevice( validSettings );
		}
        public void ChangeDevice(bool windowed, int desiredWidth, int desiredHeight)
        {
            DeviceSettings desiredSettings = new DeviceSettings();
            desiredSettings.Windowed = windowed;
            desiredSettings.BackBufferWidth = desiredWidth;
            desiredSettings.BackBufferHeight = desiredHeight;

            ChangeDevice(desiredSettings, null);
        }
        public void ChangeDevice(DeviceSettings settings)
        {
            ChangeDevice(settings, null);
        }

        public void ToggleFullScreen()
        {
            if (!EnsureDevice())
                throw new InvalidOperationException("No valid device.");

            DeviceSettings newSettings = CurrentSettings.Clone();

            newSettings.Windowed = !newSettings.Windowed;

            int width = newSettings.Windowed ? windowedWindowWidth : fullscreenWindowWidth;
            int height = newSettings.Windowed ? windowedWindowHeight : fullscreenWindowHeight;

            newSettings.BackBufferWidth = width;
            newSettings.BackBufferHeight = height;

            ChangeDevice(newSettings);
        }
        public bool EnsureDevice()
        {
            if (Direct3D9.Device != null && !deviceLost)
                return true;

            return false;
        }

		protected virtual void Dispose( bool disposing )
		{
			if( this.bDisposed )
				return;
			this.bDisposed = true;

			if( disposing )
				ReleaseDevice();

		}
		private bool bDisposed = false;

        void CreateDevice(DeviceSettings settings)
        {
            DeviceSettings oldSettings = CurrentSettings;
            CurrentSettings = settings;

            ignoreSizeChanges = true;

            bool keepCurrentWindowSize = false;
            if (settings.BackBufferWidth == 0 && settings.BackBufferHeight == 0)
                keepCurrentWindowSize = true;

            // handle the window state in Direct3D9 (it will be handled for us in DXGI)
                // check if we are going to windowed or fullscreen mode
			if( settings.Windowed )
			{
				if( oldSettings != null && !oldSettings.Windowed )
					NativeMethods.SetWindowLong( game.Window.Handle, WindowConstants.GWL_STYLE, (uint) windowedStyle );
			}
			else
			{
				if( oldSettings == null || oldSettings.Windowed )
				{
					savedTopmost = game.Window.TopMost;
					long style = NativeMethods.GetWindowLong( game.Window.Handle, WindowConstants.GWL_STYLE );
					style &= ~WindowConstants.WS_MAXIMIZE & ~WindowConstants.WS_MINIMIZE;
					windowedStyle = style;

					windowedPlacement = new WINDOWPLACEMENT();
					windowedPlacement.length = WINDOWPLACEMENT.Length;
					NativeMethods.GetWindowPlacement( game.Window.Handle, ref windowedPlacement );
				}

				// hide the window until we are done messing with it
				game.Window.Hide();
				NativeMethods.SetWindowLong( game.Window.Handle, WindowConstants.GWL_STYLE, (uint) ( WindowConstants.WS_POPUP | WindowConstants.WS_SYSMENU ) );

				WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
				placement.length = WINDOWPLACEMENT.Length;
				NativeMethods.GetWindowPlacement( game.Window.Handle, ref placement );

				// check if we are in the middle of a restore
				if( ( placement.flags & WindowConstants.WPF_RESTORETOMAXIMIZED ) != 0 )
				{
					// update the flags to avoid sizing issues
					placement.flags &= ~WindowConstants.WPF_RESTORETOMAXIMIZED;
					placement.showCmd = WindowConstants.SW_RESTORE;
					NativeMethods.SetWindowPlacement( game.Window.Handle, ref placement );
				}
			}

            if (settings.Windowed)
            {
                if (oldSettings != null && !oldSettings.Windowed)
                {
                    fullscreenWindowWidth = oldSettings.BackBufferWidth;
                    fullscreenWindowHeight = oldSettings.BackBufferHeight;
                }
            }
            else
            {
                if (oldSettings != null && oldSettings.Windowed)
                {
                    windowedWindowWidth = oldSettings.BackBufferWidth;
                    windowedWindowHeight = oldSettings.BackBufferHeight;
                }
            }

            // check if the device can be reset, or if we need to completely recreate it
            Result result = SharpDX.Direct3D9.ResultCode.Success;
            bool canReset = CanDeviceBeReset(oldSettings, settings);
            if (canReset)
                result = ResetDevice();

            if (result == SharpDX.Direct3D9.ResultCode.DeviceLost)
                deviceLost = true;
            else if (!canReset || result.Failure)
            {
                if (oldSettings != null)
                    ReleaseDevice();

                InitializeDevice();
            }

            UpdateDeviceInformation();

            // check if we changed from fullscreen to windowed mode
            if (oldSettings != null && !oldSettings.Windowed && settings.Windowed)
            {
                NativeMethods.SetWindowPlacement(game.Window.Handle, ref windowedPlacement);
                game.Window.TopMost = savedTopmost;
            }

            // check if we need to resize
            if (settings.Windowed && !keepCurrentWindowSize)
            {
                int width;
                int height;
                if (NativeMethods.IsIconic(game.Window.Handle))
                {
                    WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
                    placement.length = WINDOWPLACEMENT.Length;
                    NativeMethods.GetWindowPlacement(game.Window.Handle, ref placement);

                    // check if we are being restored
                    if ((placement.flags & WindowConstants.WPF_RESTORETOMAXIMIZED) != 0 && placement.showCmd == WindowConstants.SW_SHOWMINIMIZED)
                    {
                        NativeMethods.ShowWindow(game.Window.Handle, WindowConstants.SW_RESTORE);

                        Rectangle rect = NativeMethods.GetClientRectangle(game.Window.Handle);

                        width = rect.Width;
                        height = rect.Height;
                        NativeMethods.ShowWindow(game.Window.Handle, WindowConstants.SW_MINIMIZE);
                    }
                    else
                    {
                        NativeRectangle frame = new NativeRectangle();
                        NativeMethods.AdjustWindowRect(ref frame, (uint)windowedStyle, false);
                        int frameWidth = frame.right - frame.left;
                        int frameHeight = frame.bottom - frame.top;

                        width = placement.rcNormalPosition.right - placement.rcNormalPosition.left - frameWidth;
                        height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top - frameHeight;
                    }
                }
                else
                {
                    Rectangle rect = NativeMethods.GetClientRectangle(game.Window.Handle);
                    width = rect.Width;
                    height = rect.Height;
                }

                // check if we have a different desired size
                if (width != settings.BackBufferWidth ||
                    height != settings.BackBufferHeight)
                {
                    if (NativeMethods.IsIconic(game.Window.Handle))
                        NativeMethods.ShowWindow(game.Window.Handle, WindowConstants.SW_RESTORE);
                    if (NativeMethods.IsZoomed(game.Window.Handle))
                        NativeMethods.ShowWindow(game.Window.Handle, WindowConstants.SW_RESTORE);

                    NativeRectangle rect = new NativeRectangle();
                    rect.right = settings.BackBufferWidth;
                    rect.bottom = settings.BackBufferHeight;
                    NativeMethods.AdjustWindowRect(ref rect,
                        NativeMethods.GetWindowLong(game.Window.Handle, WindowConstants.GWL_STYLE), false);

                    NativeMethods.SetWindowPos(game.Window.Handle, IntPtr.Zero, 0, 0, rect.right - rect.left,
                        rect.bottom - rect.top, WindowConstants.SWP_NOZORDER | WindowConstants.SWP_NOMOVE);

                    Rectangle r = NativeMethods.GetClientRectangle(game.Window.Handle);
                    int clientWidth = r.Width;
                    int clientHeight = r.Height;

                    // check if the size was modified by Windows
                    if (clientWidth != settings.BackBufferWidth ||
                        clientHeight != settings.BackBufferHeight)
                    {
                        DeviceSettings newSettings = CurrentSettings.Clone();
                        newSettings.BackBufferWidth = 0;
                        newSettings.BackBufferHeight = 0;
                        if (newSettings.Direct3D9 != null)
                        {
							var pp = newSettings.Direct3D9.PresentParameters;
							pp.BackBufferWidth = GameWindowSize.Width;	// #23510 2010.10.31 add yyagi: to avoid setting BackBufferSize=ClientSize
							pp.BackBufferHeight = GameWindowSize.Height;   // #23510 2010.10.31 add yyagi: to avoid setting BackBufferSize=ClientSize
							newSettings.Direct3D9.PresentParameters = pp;
                        }

                        CreateDevice(newSettings);
                    }
                }
            }

            // if the window is still hidden, make sure it is shown
            if (!game.Window.Visible)
                NativeMethods.ShowWindow(game.Window.Handle, WindowConstants.SW_SHOW);

            // set the execution state of the thread
            if (!IsWindowed)
                NativeMethods.SetThreadExecutionState(WindowConstants.ES_DISPLAY_REQUIRED | WindowConstants.ES_CONTINUOUS);
            else
                NativeMethods.SetThreadExecutionState(WindowConstants.ES_CONTINUOUS);

            ignoreSizeChanges = false;
        }

		void Window_UserResized( object sender, EventArgs e )
		{
			if( ignoreSizeChanges || !EnsureDevice() || ( !IsWindowed ) )
				return;

			DeviceSettings newSettings = CurrentSettings.Clone();

			Rectangle rect = NativeMethods.GetClientRectangle( game.Window.Handle );
			if( rect.Width != newSettings.BackBufferWidth || rect.Height != newSettings.BackBufferHeight )
			{
				newSettings.BackBufferWidth = 0;
				newSettings.BackBufferHeight = 0;
				var pp = newSettings.Direct3D9.PresentParameters;
				pp.BackBufferWidth = GameWindowSize.Width;		// #23510 2010.10.31 add yyagi: to avoid setting BackBufferSize=ClientSize
				pp.BackBufferHeight = GameWindowSize.Height;   // 
				newSettings.Direct3D9.PresentParameters = pp;
				CreateDevice( newSettings );
			}
		}
		void Window_ScreenChanged( object sender, EventArgs e )
		{
			if( !EnsureDevice() || !CurrentSettings.Windowed || ignoreSizeChanges )
				return;

			IntPtr windowMonitor = NativeMethods.MonitorFromWindow( game.Window.Handle, WindowConstants.MONITOR_DEFAULTTOPRIMARY );

			DeviceSettings newSettings = CurrentSettings.Clone();
			int adapterOrdinal = GetAdapterOrdinal( windowMonitor );
			if( adapterOrdinal == -1 )
				return;
			newSettings.Direct3D9.AdapterOrdinal = adapterOrdinal;

			newSettings.BackBufferWidth = 0;								// #23510 2010.11.1 add yyagi to avoid to reset to 640x480 for the first time in XP.
			newSettings.BackBufferHeight = 0;                               //
			var pp = newSettings.Direct3D9.PresentParameters;
			pp.BackBufferWidth = GameWindowSize.Width;		//
			pp.BackBufferHeight = GameWindowSize.Height;   //
			newSettings.Direct3D9.PresentParameters = pp;

			CreateDevice( newSettings);
		}

		void game_FrameEnd( object sender, EventArgs e )
		{
			Result result = SharpDX.Direct3D9.ResultCode.Success;

			try
			{
				//result = Direct3D9.Device.TestCooperativeLevel();
				Direct3D9.Device.Present();
			}
			catch			// #23842 2011.1.6 yyagi: catch D3D9Exception to avoid unexpected termination by changing VSyncWait in fullscreen.
			{
				deviceLost = true;
			}

			if( result == SharpDX.Direct3D9.ResultCode.DeviceLost )
				deviceLost = true;
		}
        void game_FrameStart(object sender, CancelEventArgs e)
        {
            if (Direct3D9.Device == null )
            {
                e.Cancel = true;
                return;
            }

			//if (!game.IsActive || deviceLost)		// #23568 2010.11.3 yyagi: separate conditions to support valiable sleep value when !IsActive.
			if( deviceLost )
				Thread.Sleep( 50 );
			else if( !game.IsActive && !this.CurrentSettings.EnableVSync )  // #23568 2010.11.4 yyagi: Don't add sleep() while VSync is enabled.
				Thread.Sleep( this.game.InactiveSleepTime.Milliseconds );

            if (deviceLost)
            {
                Result result = Direct3D9.Device.TestCooperativeLevel();
                if (result == SharpDX.Direct3D9.ResultCode.DeviceLost)
                {
                    e.Cancel = true;
                    return;
                }

                // if we are windowed, check the adapter format to see if the user
                // changed the desktop format, causing a lost device
                if (IsWindowed)
                {
                    DisplayMode displayMode = GraphicsDeviceManager.Direct3D9Object.GetAdapterDisplayMode(CurrentSettings.Direct3D9.AdapterOrdinal);
                    if (CurrentSettings.Direct3D9.AdapterFormat != displayMode.Format)
                    {
                        DeviceSettings newSettings = CurrentSettings.Clone();
                        ChangeDevice(newSettings);
                        e.Cancel = true;
                        return;
                    }
                }

                result = ResetDevice();
                if (result.Failure)
                {
                    e.Cancel = true;
                    return;
                }
            }

            deviceLost = false;
        }

		bool CanDeviceBeReset( DeviceSettings oldSettings, DeviceSettings newSettings )
		{
			if( oldSettings == null )
				return false;

			return Direct3D9.Device != null &&
				oldSettings.Direct3D9.AdapterOrdinal == newSettings.Direct3D9.AdapterOrdinal &&
				oldSettings.Direct3D9.DeviceType == newSettings.Direct3D9.DeviceType &&
				oldSettings.Direct3D9.CreationFlags == newSettings.Direct3D9.CreationFlags;
		}

        void InitializeDevice()
        {
			try
			{
				EnsureD3D9();

#if TEST_Direct3D9Ex
				// 2011.4.26 yyagi
				// Direct3D9.DeviceExを呼ぶ際(IDirect3D9Ex::CreateDeviceExを呼ぶ際)、
				// フルスクリーンモードで初期化する場合はDisplayModeEx(D3DDISPLAYMODEEX *pFullscreenDisplayMode)に
				// 適切な値を設定する必要あり。
				// 一方、ウインドウモードで初期化する場合は、D3DDISPLAYMODEEXをNULLにする必要があるが、
				// DisplayModeExがNULL不可と定義されているため、DeviceExのoverloadの中でDisplayModeExを引数に取らないものを
				// 使う。(DeviceEx側でD3DDISPLAYMODEEXをNULLにしてくれる)
				// 結局、DeviceExの呼び出しの際に、フルスクリーンかどうかで場合分けが必要となる。
				if ( CurrentSettings.Direct3D9.PresentParameters.Windowed == false )
				{
					DisplayModeEx fullScreenDisplayMode = new DisplayModeEx();
					fullScreenDisplayMode.Width = CurrentSettings.Direct3D9.PresentParameters.BackBufferWidth;
					fullScreenDisplayMode.Height = CurrentSettings.Direct3D9.PresentParameters.BackBufferHeight;
					fullScreenDisplayMode.RefreshRate = CurrentSettings.Direct3D9.PresentParameters.FullScreenRefreshRateInHertz;
					fullScreenDisplayMode.Format = CurrentSettings.Direct3D9.PresentParameters.BackBufferFormat;

					Direct3D9.Device = new SlimDX.Direct3D9.DeviceEx( Direct3D9Object, CurrentSettings.Direct3D9.AdapterOrdinal,
						CurrentSettings.Direct3D9.DeviceType, game.Window.Handle,
						CurrentSettings.Direct3D9.CreationFlags, CurrentSettings.Direct3D9.PresentParameters, fullScreenDisplayMode );
				}
				else
				{
					Direct3D9.Device = new SlimDX.Direct3D9.DeviceEx( Direct3D9Object, CurrentSettings.Direct3D9.AdapterOrdinal,
						CurrentSettings.Direct3D9.DeviceType, game.Window.Handle,
						CurrentSettings.Direct3D9.CreationFlags, CurrentSettings.Direct3D9.PresentParameters );
				}
				Direct3D9.Device.MaximumFrameLatency = 1;
#else
				Direct3D9.Device = new SharpDX.Direct3D9.Device(
					Direct3D9Object,
					CurrentSettings.Direct3D9.AdapterOrdinal,
					CurrentSettings.Direct3D9.DeviceType,
					game.Window.Handle,
					CurrentSettings.Direct3D9.CreationFlags,
					CurrentSettings.Direct3D9.PresentParameters );
#endif
				if ( Result.GetResultFromWin32Error( Marshal.GetLastWin32Error() ) == SharpDX.Direct3D9.ResultCode.DeviceLost )
				{
					deviceLost = true;
					return;
				}
#if TEST_Direct3D9Ex
				Direct3D9.Device.MaximumFrameLatency = 1;			// yyagi
#endif
			}
			catch( Exception e )
			{
				throw new DeviceCreationException( "Could not create graphics device.", e );
			}

            PropogateSettings();

            UpdateDeviceStats();

            game.Initialize();
            game.LoadContent();
        }

		Result ResetDevice()
		{
			game.UnloadContent();

			Direct3D9.Device.Reset( CurrentSettings.Direct3D9.PresentParameters );

			var result = Result.GetResultFromWin32Error( Marshal.GetLastWin32Error() );

			if( result == SharpDX.Direct3D9.ResultCode.DeviceLost )
				return result;

			PropogateSettings();
			UpdateDeviceStats();
			game.LoadContent();

			return result;
		}

		void ReleaseDevice()
        {
            ReleaseDevice9();
        }

		void ReleaseDevice9()
        {
            if (Direct3D9.Device == null)
                return;

            if (game != null)
            {
                game.UnloadContent();
                game.Dispose(true);
            }

			try
			{
				Direct3D9.Device.Dispose();
			}
			catch( ObjectDisposedException )
			{
				// 時々発生するのでキャッチしておく。
			}
            Direct3D9Object.Dispose();

            Direct3D9Object = null;
            Direct3D9.Device = null;
        }
		void PropogateSettings()
		{
			CurrentSettings.BackBufferCount = CurrentSettings.Direct3D9.PresentParameters.BackBufferCount;
			CurrentSettings.BackBufferWidth = CurrentSettings.Direct3D9.PresentParameters.BackBufferWidth;
			CurrentSettings.BackBufferHeight = CurrentSettings.Direct3D9.PresentParameters.BackBufferHeight;
			CurrentSettings.BackBufferFormat = CurrentSettings.Direct3D9.PresentParameters.BackBufferFormat;
			CurrentSettings.DepthStencilFormat = CurrentSettings.Direct3D9.PresentParameters.AutoDepthStencilFormat;
			CurrentSettings.DeviceType = CurrentSettings.Direct3D9.DeviceType;
			CurrentSettings.MultisampleQuality = CurrentSettings.Direct3D9.PresentParameters.MultiSampleQuality;
			CurrentSettings.MultisampleType = CurrentSettings.Direct3D9.PresentParameters.MultiSampleType;
			CurrentSettings.RefreshRate = CurrentSettings.Direct3D9.PresentParameters.FullScreenRefreshRateInHz;
			CurrentSettings.Windowed = CurrentSettings.Direct3D9.PresentParameters.Windowed;
		}

		void UpdateDeviceInformation()
		{
			StringBuilder builder = new StringBuilder();

			if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Hardware )
				builder.Append( "HAL" );
			else if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Reference )
				builder.Append( "REF" );
			else if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Software )
				builder.Append( "SW" );

			if( ( CurrentSettings.Direct3D9.CreationFlags & CreateFlags.HardwareVertexProcessing ) != 0 )
				if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Hardware )
					builder.Append( " (hw vp)" );
				else
					builder.Append( " (simulated hw vp)" );
			else if( ( CurrentSettings.Direct3D9.CreationFlags & CreateFlags.MixedVertexProcessing ) != 0 )
				if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Hardware )
					builder.Append( " (mixed vp)" );
				else
					builder.Append( " (simulated mixed vp)" );
			else
				builder.Append( " (sw vp)" );

			if( CurrentSettings.Direct3D9.DeviceType == DeviceType.Hardware )
			{
				// loop through each adapter until we find the right one
				foreach( AdapterInfo9 adapterInfo in Enumeration9.Adapters )
				{
					if( adapterInfo.AdapterOrdinal == CurrentSettings.Direct3D9.AdapterOrdinal )
					{
						builder.AppendFormat( ": {0}", adapterInfo.Description );
						break;
					}
				}
			}

			DeviceInformation = builder.ToString();
		}

		void UpdateDeviceStats()
		{
			StringBuilder builder = new StringBuilder();

			builder.Append( "D3D9 Vsync " );

			if( CurrentSettings.Direct3D9.PresentParameters.PresentationInterval == PresentInterval.Immediate )
				builder.Append( "off" );
			else
				builder.Append( "on" );

			builder.AppendFormat( " ({0}x{1}), ", CurrentSettings.Direct3D9.PresentParameters.BackBufferWidth, CurrentSettings.Direct3D9.PresentParameters.BackBufferHeight );

			if( CurrentSettings.Direct3D9.AdapterFormat == CurrentSettings.Direct3D9.PresentParameters.BackBufferFormat )
				builder.Append( Enum.GetName( typeof( SharpDX.Direct3D9.Format ), CurrentSettings.Direct3D9.AdapterFormat ) );
			else
				builder.AppendFormat( "backbuf {0}, adapter {1}",
					Enum.GetName( typeof( SharpDX.Direct3D9.Format ), CurrentSettings.Direct3D9.AdapterFormat ),
					Enum.GetName( typeof( SharpDX.Direct3D9.Format ), CurrentSettings.Direct3D9.PresentParameters.BackBufferFormat ) );

			builder.AppendFormat( " ({0})", Enum.GetName( typeof( SharpDX.Direct3D9.Format ), CurrentSettings.Direct3D9.PresentParameters.AutoDepthStencilFormat ) );

			if( CurrentSettings.Direct3D9.PresentParameters.MultiSampleType == MultisampleType.NonMaskable )
				builder.AppendFormat( " (Nonmaskable Multisample {0}x)", (int)CurrentSettings.Direct3D9.PresentParameters.MultiSampleQuality);
			else if( CurrentSettings.Direct3D9.PresentParameters.MultiSampleType != MultisampleType.None )
				builder.AppendFormat( " (Multisample {0}x Type={1})", (int) CurrentSettings.Direct3D9.PresentParameters.MultiSampleQuality, CurrentSettings.Direct3D9.PresentParameters.MultiSampleType.ToString() );

			DeviceStatistics = builder.ToString();
		}

		int GetAdapterOrdinal( IntPtr screen )
		{
			AdapterInfo9 adapter = null;
			foreach( AdapterInfo9 a in Enumeration9.Adapters )
			{
				if( Direct3D9Object.GetAdapterMonitor( a.AdapterOrdinal ) == screen )
				{
					adapter = a;
					break;
				}
			}

			if( adapter != null )
				return adapter.AdapterOrdinal;

			return -1;
		}

        internal static void EnsureD3D9()
        {
			if ( Direct3D9Object == null )
#if TEST_Direct3D9Ex
				Direct3D9Object = new Direct3DEx();		// yyagi
#else
				Direct3D9Object = new Direct3D();
#endif
        }
    }
}
