using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using BulletX.BulletDynamics.Dynamics;
using BulletX.LinerMath;
using MikuMikuDance.XNA.Model;

namespace MikuMikuDance.XNA.MultiThread
{
    /// <summary>
    /// 物理演算データ受け渡し用
    /// </summary>
    public class PhysicsThreadBuffer
    {
        /// <summary>
        /// 物理演算使用フラグ
        /// </summary>
        public bool UsePhysic { get; set; }
        /// <summary>
        /// ユーザーデータ
        /// </summary>
        public object UserData { get; set; }

        internal List<MMDModel> ResetModels { get; set; }
        internal void ResetRigid(MMDModel model)
        {
            ResetModels.Add(model);
        }
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public PhysicsThreadBuffer()
        {
            ResetModels = new List<MMDModel>();
        }
    }
    /// <summary>
    /// 物理演算スレッドコールバック用デリゲート
    /// </summary>
    /// <param name="gameTime"></param>
    public delegate void AfterPhysicsDelegate(GameTime gameTime);
    /// <summary>
    /// 物理演算用のスレッド
    /// </summary>
    public class PhysicsThread : GameThread<PhysicsThreadBuffer>
    {
        MikuMikuDanceXNA mmdx;
        DiscreteDynamicsWorld Physics;
        int onthread = 0;
        internal int OnThread { get { return onthread; } }
        internal int OutThread { get { return onthread == 0 ? 1 : 0; } }
        /// <summary>
        /// 物理演算後に呼び出されるイベント
        /// </summary>
        public event AfterPhysicsDelegate AfterPhysics;
        /// <summary>
        /// 物理演算用スレッド
        /// </summary>
        /// <param name="game">Game</param>
        /// <param name="manager">Manager</param>
        /// <param name="physics">物理演算オブジェクト</param>
        /// <param name="mmdx">MMDXオブジェクト</param>
        public PhysicsThread(Game game, ThreadManager manager, DiscreteDynamicsWorld physics, MikuMikuDanceXNA mmdx)
#if XBOX
            : base(ThreadManager.PhysicsThreadNumber, game, manager)
#else
            : base(game, manager)
#endif
        {
            Physics = physics;
            this.mmdx = mmdx;
        }
        /// <summary>
        /// 更新処理
        /// </summary>
        /// <param name="gameTime">GameTimeオブジェクト</param>
        public override void Update(GameTime gameTime)
        {
            float timeStep = (float)gameTime.ElapsedGameTime.TotalSeconds /* (float)RefreshRate*/;
            if (timeStep != 0.0f && WritableBuffer.UsePhysic)
            {
                Physics.stepSimulation(timeStep, MikuMikuDanceXNA.MaxSubStep, MikuMikuDanceXNA.FixedTimeStep);
            }
            if (AfterPhysics != null)
                AfterPhysics(gameTime);
        }
        internal override void SwapBuffer()
        {
            while (FreeBuffer.ResetModels.Count > 0)
            {
                FreeBuffer.ResetModels[0].BoneManager.UpdateWorldTransforms();
                for (int i = 0; i < FreeBuffer.ResetModels[0].Physics.motionState.Length; i++)
                {
                    RigidBody rigid = FreeBuffer.ResetModels[0].Physics.Rigids[i];
                    MMDMotionState motionstate = FreeBuffer.ResetModels[0].Physics.motionState[i];
                    rigid.activate(true);
                    motionstate.Flush(true);
                    btTransform temp;
                    motionstate.getWorldTransform(out temp);
                    rigid.WorldTransform = temp;
                    rigid.LinearVelocity = btVector3.Zero;
                    rigid.AngularVelocity = btVector3.Zero;
                }
                FreeBuffer.ResetModels.RemoveAt(0);
            }
            base.SwapBuffer();
            WritableBuffer.UsePhysic = mmdx.UsePhysic;
            onthread = (++onthread) % 2;
        }
#if false//FPS調整した。
        //nフレームに1回更新する仕掛けに変更
        int frameNum = 0;
        const int RefreshRate = 1;
        public override void StartFrame()
        {
            if (frameNum == 0)
                base.StartFrame();
        }
        public override void Synchronize()
        {
            if (frameNum == 0)
                base.Synchronize();
            frameNum = (++frameNum) % RefreshRate;
        }
#endif
    }
}
