using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using MikuMikuDance.XNA;
using MikuMikuDance.XNA.Model;
using MikuMikuDance.XNA.Motion;
using MikuMikuDance.XNA.Debug;
using MikuMikuDance.XNA.Model.ModelData;
using MikuMikuDance.XNA.MultiThread;

namespace MikuMikuDanceXNADemo8
{
    
    /// <summary>
    /// このサンプルでは
    /// ・伊藤 雄一氏より頂き、MMDX用に調整したのDebugシステムの使い方について
    /// ・マルチスレッドクラス
    /// について説明します
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        //MikuMikuDance for XNA
        MikuMikuDanceXNA mmd;
        //MMDモデルデータ
        MMDModel model;
        //MMDモーションデータ
        MMDMotion motion;
        //デバッグマネージャ
        DebugManager debugManager;
        //デバッグ用フォント
        SpriteFont debugFont;
        //スレッドマネージャ
        ThreadManager threadManager;
        //テストスレッド
        TestThread test;

        public Game1()
        {
            Content.RootDirectory = "Content";
            graphics = new GraphicsDeviceManager(this);
            //デバイスがMMDXの必要条件を満たしているかチェック
            MikuMikuDanceXNA.InitialSetup(this, graphics, true);
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent関数
        /// 各種ロード処理はここでロードを行っておくと良い
        /// </summary>
        protected override void LoadContent()
        {
            //スレッドマネージャの作成
            threadManager = new ThreadManager();
            //MikuMikuDanceXNAを作成して、引数にスレッドマネージャを突っ込む(物理演算がマルチスレッド化されます)
            mmd = new MikuMikuDanceXNA(this, threadManager);
            //プロジェクトのContent内に追加してあるMiku.pmdモデルをロードする
            //ロードはMikuMikuDanceXNA.LoadModel関数で行う
            //(モデルデータはあにまさ氏よりお借りしました。このモデルファイルはMITライセンス適応外です)
            model = mmd.LoadModel("Miku", this, QuatTransform.Identity);
            //同じくContent内に追加してあるTrueMyHeart.vmdモーションをロードする
            //ロードはMikuMikuDanceXNA.LoadMotion関数で行う
            motion = mmd.LoadMotion("TrueMyHeart");
            //モデルのプレイヤーを呼び出し
            //モーションを0番トラックにセットする
            model.Player.SetMotion(0, motion, true);
            //デバッグ用のフォントのロード
            debugFont = Content.Load<SpriteFont>("DebugFont");
            //デバッグマネージャの作成
            debugManager = new DebugManager(this, mmd, debugFont, this.GraphicsDevice, mmd.Physic);
            //剛体を描画したい場合はDrawRigidsをtrueとする
            //debugManager.DrawRigids = true;
            //テストスレッドの作成
            //XBoxはハードウェアスレッド番号を指定する必要がある
            //XBoxのハードウェアスレッドは6つ、うち、0番と2番はXNAが、5番はBulletXが使用中なので1,3,4番のどれかを指定する。
#if XBOX
            test = new TestThread(4, this, threadManager, debugManager.TimeRuler);
#else
            test = new TestThread(this, threadManager, debugManager.TimeRuler);
#endif
        }

        /// <summary>
        /// UnloadContent関数
        /// アンロードが必要な物はここでアンロードを行う
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// アップデート関数
        /// 毎フレーム処理する描画以外のコードをここに記述する
        /// </summary>
        /// <param name="gameTime">現在の時間のスナップショットを表すオブジェクト</param>
        protected override void Update(GameTime gameTime)
        {
            //TimeRularに開始フレームを伝える
            debugManager.TimeRuler.StartFrame();
            //スレッドによる非同期処理開始
            threadManager.StartFrame(gameTime);
            //Update枠の開始
            debugManager.TimeRuler.BeginMark("Update", Color.Blue);
            //XBoxコントローラーのBackボタンを押したら終了
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            //キーボードのEscキーを押したら終了
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();
            //debugCommandが表示されていないとき
            if (!debugManager.DebugCommandUI.Focused)
            {
                //エンターキーorAボタンを押したとき
                if (Keyboard.GetState().IsKeyDown(Keys.Enter) || GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed)
                {
                    //現在再生中でなければ
                    if (!model.Player.IsPlay(0))
                    {
                        //0番トラックにセットされているモーションをリセット
                        model.Player.Reset(0);
                        //0番トラックにセットされているモーションを再生する
                        model.Player.Start(0, false);
                    }
                }
                //デバッグマネージャーのキー入力判定
                debugManager.HandleInput();
            }
            //モデルの再生処理はDrawableGameComponentを継承しているため
            //XNAにより自動で処理が行われる

            //XNA内部のUpdate処理を行う
            base.Update(gameTime);
            //Update枠の終了
            debugManager.TimeRuler.EndMark("Update");
        }
        
        /// <summary>
        /// Draw関数
        /// 毎フレーム処理する描画コードをここに記述する
        /// </summary>
        /// <param name="gameTime">現在の時間のスナップショットを表すオブジェクト</param>
        protected override void Draw(GameTime gameTime)
        {
            debugManager.TimeRuler.BeginMark("Draw", Color.Yellow);
            //画面をCornflowerBlue色で消去する
            GraphicsDevice.Clear(Color.CornflowerBlue);

            //モデルはDrawableGameComponentを継承しているため
            //XNAにより自動で描画される

            //XNA内部のDraw処理を行う
            base.Draw(gameTime);
            //デバッグの描画
            debugManager.Draw(this.GraphicsDevice);
            
            debugManager.TimeRuler.EndMark("Draw");
        }
        protected override void EndDraw()
        {
            base.EndDraw();
            //描画処理終了後にスレッドを同期させる
            threadManager.Synchronize();
        }
        protected override void Dispose(bool disposing)
        {
            if (threadManager != null)
            {
                //スレッドの終了及び破棄処理を行う
                threadManager.Dispose();
                threadManager = null;
            }
            base.Dispose(disposing);
        }
    }
}
