using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using System.Threading;

namespace MikuMikuDance.XNA.Forms.Graphics
{
    /// <summary>
    /// グラフィックスデバイスサービスクラス
    /// </summary>
    class GraphicsDeviceService : IGraphicsDeviceService
    {
        #region シングルトン
        //シングルトン
        private static GraphicsDeviceService m_Instance = null;
        //参照カウント
        private static int referenceCount;
        /// <summary>
        /// シングルトンインスタンス
        /// </summary>
        public static GraphicsDeviceService Instance
        {
            get
            {
                return m_Instance;
            }
        }
        /// <summary>
        /// シングルトンインスタンスに対する参照を追加する
        /// </summary>
        /// <param name="handle">ウィンドウハンドル</param>
        /// <param name="width">ウィンドウ幅</param>
        /// <param name="height">ウィンドウ高さ</param>
        public static GraphicsDeviceService AddRef(IntPtr handle, int width, int height)
        {
            if (Interlocked.Increment(ref referenceCount) == 1)
            {
                m_Instance = new GraphicsDeviceService(handle, width, height);
            }
            return m_Instance;
        }
        
        #endregion
        //デバイスサイズ設定用パラメータ
        PresentationParameters m_pp;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="handle">ウィンドウハンドル</param>
        /// <param name="width">ウィンドウ幅</param>
        /// <param name="height">ウィンドウ高さ</param>
        private GraphicsDeviceService(IntPtr handle, int width, int height)
        {
            //グラフィックスデバイスパラメータ設定
            m_pp = new PresentationParameters();
            m_pp.BackBufferWidth = width;
            m_pp.BackBufferHeight = height;
            m_pp.BackBufferFormat = SurfaceFormat.Color;
            m_pp.EnableAutoDepthStencil = true;
            m_pp.AutoDepthStencilFormat = DepthFormat.Depth24;
            //グラフィックスデバイス作成
            GraphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, handle, m_pp);
            if (DeviceCreated != null)
                DeviceCreated(this, EventArgs.Empty);
        }
        /// <summary>
        /// シングルトンインスタンスへの参照を開放する
        /// </summary>
        /// <param name="disposing">リソースの破棄を行うかどうか</param>
        public void Release(bool disposing)
        {
            if (Interlocked.Decrement(ref referenceCount) == 0)
            {
                if (disposing)
                {
                    if (DeviceDisposing != null)
                        DeviceDisposing(this, EventArgs.Empty);
                    GraphicsDevice.Dispose();
                }
                GraphicsDevice = null;
            }
        }
        /// <summary>
        /// グラフィックスデバイスを指定された解像度か、現在保持している解像度のうち大きい方を選択する
        /// </summary>
        /// <param name="width">幅</param>
        /// <param name="height">高さ</param>
        public void ResetDevice(int width, int height)
        {
            //イベント呼び出し
            if (DeviceResetting != null)
                DeviceResetting(this, EventArgs.Empty);
            //幅と高さを再設定してリセット
            m_pp.BackBufferWidth = Math.Max(width, m_pp.BackBufferWidth);
            m_pp.BackBufferHeight = Math.Max(height, m_pp.BackBufferHeight);
            GraphicsDevice.Reset(m_pp);
            //イベント呼び出し
            if (DeviceReset != null)
                DeviceReset(this, EventArgs.Empty);
        }
        #region IGraphicsDeviceService メンバ
        /// <summary>
        /// グラフィック デバイスの作成時に発生するイベント。
        /// </summary>
        public event EventHandler DeviceCreated;
        /// <summary>
        /// グラフィック デバイスの破棄時に発生するイベント。
        /// </summary>
        public event EventHandler DeviceDisposing;
        /// <summary>
        /// グラフィック デバイスのリセット時に発生するイベント。
        /// </summary>
        public event EventHandler DeviceReset;
        /// <summary>
        /// グラフィック デバイスのリセットの進行中に発生するイベント。
        /// </summary>
        public event EventHandler DeviceResetting;
        /// <summary>
        /// グラフィックスデバイス
        /// </summary>
        public GraphicsDevice GraphicsDevice { get; private set; }

        #endregion

        
    }
}
