#region Using ステートメント

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using BulletX;
using BulletX.BulletCollision.CollisionDispatch;
using MikuMikuDance.XNA.Debug.Utils;
using Microsoft.Xna.Framework.Input;

#endregion

namespace MikuMikuDance.XNA.Debug
{
    /// <summary>
    /// デバッグ用のグラフィクスコンテントを格納する為のデバッグ用マネージャー(伊藤 雄一(HN:ひげねこ)作、ウィルフレム改)
    /// </summary>
    public class DebugManager : DefaultDebugDraw
    {
        // 入力処理用変数
        // 現フレームのゲームパッドとキー入力ステート
        GamePadState padState;
        KeyboardState keyState;

        // 前フレームのゲームパッドとキー入力ステート
        GamePadState prevPadState;
        KeyboardState prevKeyState;

        MikuMikuDanceXNA m_mmdx;
        #region プロパティ
        /// <summary>
        /// FPSの表示/非表示に使うキー
        /// </summary>
        public Keys FPSKey = Keys.A;
        /// <summary>
        /// タイムルーラーの表示/非表示に使うキー
        /// </summary>
        public Keys TimeRulerKey = Keys.B;
        /// <summary>
        /// タイムルーラーログの表示/非表示に使うキー
        /// </summary>
        public Keys TimeLogKey = Keys.X;
        /// <summary>
        /// FPSの表示/非表示に使うボタン
        /// </summary>
        public Buttons FPSButton = Buttons.A;
        /// <summary>
        /// タイムルーラーの表示/非表示に使うボタン
        /// </summary>
        public Buttons TimeRulerButton = Buttons.B;
        /// <summary>
        /// タイムルーラーログの表示/非表示に使うボタン
        /// </summary>
        public Buttons TimeLogButton = Buttons.X;

        /// <summary>
        /// デバッグ用SpriteBatchの取得
        /// </summary>
        public SpriteBatch SpriteBatch { get; private set; }

        /// <summary>
        /// 白テクスチャの取得
        /// </summary>
        public Texture2D WhiteTexture { get; private set; }

        /// <summary>
        /// デバッグ用フォント
        /// </summary>
        public SpriteFont DebugFont { get; private set; }

        /// <summary>
        /// FPSCounter
        /// </summary>
        public FpsCounter FPS { get; private set; }
        /// <summary>
        /// TimeRular
        /// </summary>
        public DefaultTimeRuler TimeRuler { get; private set; }
        /// <summary>
        /// DebugCommandUI
        /// </summary>
        public DebugCommandUI DebugCommandUI { get; private set; }
        /// <summary>
        /// 剛体を描画するかどうか
        /// </summary>
        public bool DrawRigids { get; set; }
        /// <summary>
        /// DebugDrawの説明を描画するかどうか
        /// </summary>
        public bool DrawMessage { get; set; }
        #endregion

        #region 初期化
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="Game">ゲームオブジェクト</param>
        /// <param name="mmdx">MikuMikuDanceXNA</param>
        /// <param name="debugFont">デバッグ用フォント</param>
        /// <param name="device">GraphicsDevice</param>
        /// <param name="world">BulletのCollisionWorld</param>
        public DebugManager(Game Game, MikuMikuDanceXNA mmdx, SpriteFont debugFont, GraphicsDevice device, CollisionWorld world)
            : this(Game, mmdx, debugFont, device, world, KeyboardType.Japanese) { }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="Game">ゲームオブジェクト</param>
        /// <param name="mmdx">MikuMikuDanceXNA</param>
        /// <param name="debugFont">デバッグ用フォント</param>
        /// <param name="device">GraphicsDevice</param>
        /// <param name="world">BulletのCollisionWorld</param>
        /// <param name="keytype">キーボードレイアウト</param>
        public DebugManager(Game Game, MikuMikuDanceXNA mmdx, SpriteFont debugFont, GraphicsDevice device, CollisionWorld world, KeyboardType keytype)
            : base(device, world)
        {
            // サービスとして登録する
            Game.Services.AddService(typeof(DebugManager), this);
            // デバッグ用コンテントの読み込み
            SpriteBatch = new SpriteBatch(device);

            // 白テクスチャの生成
            WhiteTexture = new Texture2D(device, 1, 1);
            Color[] whitePixels = new Color[] { Color.White };
            WhiteTexture.SetData<Color>(whitePixels);

            // デバッグマコマンドUIの初期化と追加
            DebugCommandUI = new DebugCommandUI(Game, keytype);

            // デバッグコマンドUIを最上面に表示させる為にDrawOrderを変更する
            DebugCommandUI.DrawOrder = 100;
            Game.Components.Add(DebugCommandUI);
            //FPSカウンタの作成
            FPS = new FpsCounter(Game);
            Game.Components.Add(FPS);
            FPS.DrawOrder = 99;
            FPS.Visible = true;
            //TimeRularの作成
            TimeRuler = new DefaultTimeRuler(Game);
            TimeRuler.Visible = true;
            TimeRuler.ShowLog = true;
            TimeRuler.DrawOrder = 99;
            Game.Components.Add(TimeRuler);
            //物理演算システムにフック
            CollisionWorld.Profiler = TimeRuler;
            
            DebugFont = debugFont;

            DrawMessage = true;
            m_mmdx = mmdx;
        }

        #endregion
        /// <summary>
        /// 入力処理
        /// </summary>
        public void HandleInput()
        {
            padState = GamePad.GetState(PlayerIndex.One);
            keyState = Keyboard.GetState();

            // Aボタン、AキーでFPSカウンターの表示、非表示
            if (IsButtonOrKeyPressed(FPSButton, FPSKey))
            {
                FPS.Visible = !FPS.Visible;
            }

            // Bボタン、BキーでTimeRulerの表示、非表示
            if (IsButtonOrKeyPressed(TimeRulerButton, TimeRulerKey))
            {
                TimeRuler.Visible = !TimeRuler.Visible;
            }

            // Xボタン、XキーでTimeRulerログの表示、非表示
            if (IsButtonOrKeyPressed(TimeLogButton, TimeLogKey))
            {
                TimeRuler.Visible = true;
                TimeRuler.ShowLog = !TimeRuler.ShowLog;
            }
            prevKeyState = keyState;
            prevPadState = padState;
        }
        /// <summary>
        /// 指定されたボタン、またはキー入力があったか?
        /// </summary>
        /// <param name="button">ボタン</param>
        /// <param name="key">キー</param>
        /// <returns>入力があった場合はtrueを返す</returns>
        bool IsButtonOrKeyPressed(Buttons button, Keys key)
        {
            return (padState.IsButtonDown(button) && prevPadState.IsButtonUp(button)) ||
                (keyState.IsKeyDown(key) && prevKeyState.IsKeyUp(key));
        }

        /// <summary>
        /// 描画
        /// </summary>
        /// <param name="GraphicsDevice">GraphicsDevice</param>
        public override void Draw(GraphicsDevice GraphicsDevice)
        {
            if (DrawMessage)
            {
                // デバッグマネージャのフォントとSpriteBatchを使わせてもらう
                SpriteFont font = this.DebugFont;
                SpriteBatch sb = this.SpriteBatch;

                sb.Begin();

                // 使い方の表示
                string message =
                    "A Button, A key: Show/Hide FPS Counter\n" +
                    "B Button, B key: Show/Hide Time Ruler\n" +
                    "X Button, X key: Show/Hide Time Ruler Log\n" +
                    "Tab key: Open debug command UI";
                Vector2 size = font.MeasureString(message);
                Layout layout = new Layout(GraphicsDevice.Viewport);

                float margin = font.LineSpacing;
                Rectangle rc = new Rectangle(0, 0,
                                        (int)(size.X + margin),
                                        (int)(size.Y + margin));

                // 黒い背景の配置位置を計算して描画
                rc = layout.Place(rc, 0.01f, 0.01f, Alignment.TopRight);
                sb.Draw(this.WhiteTexture, rc, new Color(0, 0, 0, 128));

                // 使い方文字列を黒い背景の真ん中に配置して描画
                layout.ClientArea = rc;
                Vector2 pos = layout.Place(size, 0, 0, Alignment.Center);
                sb.DrawString(font, message, pos, Color.White);
                sb.End();
                GraphicsDevice.RenderState.DepthBufferEnable = true;
                GraphicsDevice.RenderState.AlphaTestEnable = true;
                GraphicsDevice.RenderState.ReferenceAlpha = 1;
                GraphicsDevice.RenderState.AlphaFunction = CompareFunction.GreaterEqual;
                GraphicsDevice.RenderState.DepthBufferEnable = true;
                GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
                GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            }
            if (DrawRigids)
            {
                View = m_mmdx.Camera.GetViewMatrix();
                Proj = m_mmdx.Camera.GetProjectionMatrix(GraphicsDevice);
                base.Draw(GraphicsDevice);
            }
        }
    }
}