using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MikuMikuDance.XNA.Model;
using MikuMikuDance.XNA.Model.ModelData;
using MikuMikuDance.XNA.Motion;
using MikuMikuDance.XNA.Motion.MotionData;
using MikuMikuDance.XNA.Stages;
using MikuMikuDance.XNA.Debug;
using BulletMMD.BulletDynamics.Dynamics;
using MikuMikuDance.XNA.Accessory;

namespace MikuMikuDance.XNA
{
    /// <summary>
    /// MikuMikuDance for XNAのスタートクラス
    /// </summary>
    public class MikuMikuDanceXNA
    {
        /// <summary>
        /// モーショントラックの最大トラック数
        /// </summary>
        public const int MotionTrackCap = 30;

#if XBOX360//スレッド用の定数。ベイク処理等に使用
        internal const int NumThread = 4;//使用するスレッド数
#else
        internal readonly int NumThread;
#endif
        //properties...
        /// <summary>
        /// MikuMikuDance for XNAで用いるコンテンツマネージャ
        /// </summary>
        public ContentManager Content { get; set; }
        /// <summary>
        /// MikuMikuDance for XNAカメラ
        /// </summary>
        public MMDCamera Camera { get; protected set; }
        /// <summary>
        /// ライトマネージャ
        /// </summary>
        public MMDLightManager LightManager { get; private set; }
        /// <summary>
        /// 物理エンジン
        /// </summary>
        public btDiscreteDynamicsWorld Physic { get; internal set; }

        /// <summary>
        /// 物理演算を使用フラグ
        /// </summary>
        /// <remarks>物理演算完成までデフォルトはfalse</remarks>
        public bool UsePhysic { get; set; }

#if TRACE
        /// <summary>
        /// デバッグ用のタイム計測オブジェクト格納場所
        /// </summary>
        public ITimeRular TimeRular = null;
#endif

        //物理演算用の地面(仮置き)
        private MMDGroundObject Ground { get; set; }
        /// <summary>
        /// MikuMikuDance for XNAに必要となる環境を満たしているかチェックする
        /// </summary>
        /// <param name="gdManager">GraphicsDeviceManager</param>
        /// <returns>今のところtrueが返る</returns>
        /// <remarks>シェーダ要件を満たしていない場合はXNAのダイアログが出てアプリケーションが終了する。</remarks>
        public static bool CheckDevice(GraphicsDeviceManager gdManager)
        {
            //MMDXにはシェーダモデル3.0が必要
            gdManager.MinimumVertexShaderProfile = ShaderProfile.VS_3_0;
            gdManager.MinimumPixelShaderProfile = ShaderProfile.PS_3_0;
            return true;//今のところ、他に必須環境は無いのでtrueを返す
        }

        //constructors...
        /// <summary>
        /// MikuMikuDance for XNAのコンストラクタ
        /// </summary>
        /// <param name="game">使用するGameオブジェクト</param>
        public MikuMikuDanceXNA(Game game)
        {
            //初期設定
            Content = game.Content;
            Camera = new MMDCamera(game);
            game.Components.Add(Camera);
            LightManager = new MMDLightManager(game);
            game.Components.Add(LightManager);
#if WINDOWS
            //論理コア数を取得して、最適スレッド数を取得
            Win32API.SYSTEM_INFO info = new Win32API.SYSTEM_INFO();
            Win32API.GetSystemInfo(ref info);
            NumThread = (int)info.dwNumberOfProcessors;
#endif
            //物理エンジン
            Physic = CreatePhysicSystem();
            Ground = new MMDGroundObject(0.0f, Physic);
        }
        /// <summary>
        /// MikuMikuDance for XNAのコンストラクタ(作成済み物理エンジン指定)
        /// </summary>
        /// <param name="game">使用するGameオブジェクト</param>
        /// <param name="physic">使用するbtDiscreteDynamicsWorldオブジェクト</param>
        public MikuMikuDanceXNA(Game game, btDiscreteDynamicsWorld physic)
        {
            //初期設定
            Content = game.Content;
            Camera = new MMDCamera(game);
            game.Components.Add(Camera);
            LightManager = new MMDLightManager(game);
            game.Components.Add(LightManager);
#if WINDOWS
            //論理コア数を取得して、最適スレッド数を取得
            Win32API.SYSTEM_INFO info = new Win32API.SYSTEM_INFO();
            Win32API.GetSystemInfo(ref info);
            NumThread = (int)info.dwNumberOfProcessors;
#endif
            //物理エンジン
            Physic = physic;
        }
        /// <summary>
        /// モデルをアセットから読み込む
        /// </summary>
        /// <param name="assetName">モデルのアセット名</param>
        /// <param name="game">XNA Gameクラス</param>
        /// <param name="defaultTransform">デフォルトトランスフォームマトリックス</param>
        /// <returns>MikuMikuDance for XNAのモデル</returns>
        public MMDModel LoadModel(string assetName, Game game, Matrix defaultTransform)
        {
            MMDModel result = new MMDModel(game);
            result.Name = assetName;
            result.World = defaultTransform;
            result.ModelSetup(Content.Load<MMDModelData>(assetName), this, game.GraphicsDevice);
            return result;
        }
        /// <summary>
        /// モデルモーションをアセットから読み込む
        /// </summary>
        /// <param name="assetName">モーションのアセット名</param>
        /// <returns>MikuMikuDance for XNAのモデルモーション</returns>
        /// <remarks>カメラ、ライトモーションはLoadStageMotionを使用</remarks>
        public MMDMotion LoadMotion(string assetName)
        {
            MMDMotion result = new MMDMotion();
            result.Initialize(Content.Load<MMDMotionData>(assetName));
            return result;
        }
        /// <summary>
        /// ベイク済みモデルモーションをアセットから読み込む
        /// </summary>
        /// <param name="assetName">モーションのアセット名</param>
        /// <returns>MikuMikuDance for XNAのベイク済みモデルモーション</returns>
        public MMDBakedMotion LoadBakedMotion(string assetName)
        {
            return Content.Load<MMDBakedMotion>(assetName);
        }
        /// <summary>
        /// アクセサリをアセットから読み込む
        /// </summary>
        /// <param name="assetName">アクセサリのアセット名</param>
        /// <param name="game">XNA Gameクラス</param>
        /// <param name="defaultTransform">デフォルトトランスフォームマトリックス</param>
        /// <returns>MikuMikuDance for XNAのアクセサリ</returns>
        public MMDAccessory LoadAccessory(string assetName, Game game, Matrix defaultTransform)
        {
            Microsoft.Xna.Framework.Graphics.Model model = Content.Load<Microsoft.Xna.Framework.Graphics.Model>(assetName);
            return new MMDAccessory(model, this, game, defaultTransform);
        }
        /// <summary>
        /// VACをアセットから読み込む
        /// </summary>
        /// <param name="assetName">VACのアセット名</param>
        /// <returns>VACデータ</returns>
        public MMD_VAC LoadVAC(string assetName)
        {
            return Content.Load<MMD_VAC>(assetName);
        }
        /// <summary>
        /// カメラ、ライトモーション(ステージモーション)をアセットから読み込む
        /// </summary>
        /// <param name="assetName">モーションのアセット名</param>
        /// <returns>MikuMikuDance for XNAのステージモーション</returns>
        public MMDStageMotion LoadStageMotion(string assetName)
        {
            MMDStageMotion result = new MMDStageMotion();
            result.Initialize(Content.Load<MMDMotionData>(assetName), this);
            return result;
        }
        /// <summary>
        /// 登録済み物理エンジンのアップデート
        /// </summary>
        /// <param name="timeStep">GameTimeオブジェクト</param>
        public void UpdatePhisics(float timeStep)
        {
            if (timeStep != 0.0f && UsePhysic)
            {
                Physic.StepSimulation(timeStep);
            }
        }

        private btDiscreteDynamicsWorld CreatePhysicSystem()
        {
            return null;//現在は物理エンジン未完成のため、nullを返すようにしている
            //物理エンジンの作成
            /*CollisionDispatcher dispatcher = new CollisionDispatcher();
            OverlappingPairCache pairCache = new SimpleBroadphase();
            SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();
            
            DiscreteDynamicsWorld result = new DiscreteDynamicsWorld(dispatcher, pairCache, solver);
            result.Gravity = 9.81f * 5.0f * Vector3.Down;
            result.SolverInfo.IterationsCount = 10;
            result.SolverInfo.TimeStep = 15;
            result.Gravity = 9.81f * 5.0f * Vector3.Down;
            return result;*/
        }
        
    }
}
