using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using MikuMikuDance.XNA.Model;
using MikuMikuDance.XNA.Model.ModelData;

namespace MikuMikuDance.XNA.Motion
{
    /// <summary>
    /// ベイク済みの各ボーンのデータ
    /// </summary>
    public struct BakedBoneData
    {
        /// <summary>
        /// ボーン番号
        /// </summary>
        public int BoneIndex;
        /// <summary>
        /// トランスフォーム
        /// </summary>
        public QuatTransform Poses;
    }
    /// <summary>
    /// ベイク済みの各表情のデータ
    /// </summary>
    public struct BakedFaceData
    {
        /// <summary>
        /// 表情頂点の移動量
        /// </summary>
        public Vector3 FaceVert;
        /// <summary>
        /// キーフレーム番号
        /// </summary>
        /// <remarks>longでないのは.NET CFの配列がintまでしか扱えないため</remarks>
        public int Frame;
    }
    class BakedMotionTrack : MotionTrack
    {
        //ベイク済みモーションデータ
        MMDBakedMotion motion;

        Stopwatch timer = new Stopwatch();
        decimal frame = 0;
        int numRepeat = 0;
        decimal beforeMS = 0;
        decimal LossTime = 0;
        bool isEmpty = true;
        bool isLoopPlay;

        public bool IsEmpty { get { return isEmpty; } set { isEmpty = value; } }
        public bool IsLoopPlay { get { return isLoopPlay; } set { isLoopPlay = value; } }
        public decimal NowFrame { get; set; }
        public int NumRepeat { get { return numRepeat; } }
        public long MaxFrame { get; set; }
        public TrackType Type { get { return TrackType.BakedTrack; } }
        public void Clear() { motion = null; timer.Reset(); isLoopPlay = false; isEmpty = true; }
        /// <summary>
        /// 逆再生フラグ
        /// </summary>
        public bool Reverse { get; set; }

        public MMDBakedMotion Motion
        {
            get { return motion; }
            set
            {
                motion = value;
                MaxFrame = motion.MaxFrame;//Math.Max(motion.Poses.GetLength(0) / motion.NumBone, motion.Faces.GetLength(0) / motion.NumFace) - 1;
            }
        }

        public void Stop()
        {
            timer.Stop();
            LossTime = (decimal)timer.Elapsed.TotalMilliseconds - beforeMS;//ロスタイムを計測
        }

        public void Start()
        {
            timer.Start();
            beforeMS = (decimal)timer.Elapsed.TotalMilliseconds - LossTime;
        }
        public bool IsPlay
        {
            get
            {
                return timer.IsRunning;
            }
        }
        public void Reset()
        {
            timer.Reset();
            beforeMS = 0;
            LossTime = 0;
            frame = 0;
            NowFrame = 0;
            numRepeat = 0;
        }
        /// <summary>
        /// トラックのシーク
        /// </summary>
        /// <param name="frame">新しいシーク位置</param>
        public void Seek(decimal frame)
        {
            this.frame = frame;
        }
        public bool UpdateFrame(decimal FramePerSecond)
        {
            //現在時刻の取得
            decimal nowMS = (decimal)timer.Elapsed.TotalMilliseconds;
            //前回コール時からの経過フレーム数を取得
            decimal dframe = (nowMS - beforeMS) * FramePerSecond / 1000.0m;
            //フレーム数の更新
            frame += dframe * (Reverse ? -1 : 1);
            beforeMS = nowMS;

            if (frame > MaxFrame && !isLoopPlay)
            {
                Stop();
                if (frame > MaxFrame)
                {
                    NowFrame = (decimal)MaxFrame;
                    frame = MaxFrame;
                }
                else
                {
                    NowFrame = 0;
                    frame = 0;
                }
                return false;
            }
            if (frame < 0)
            {//逆再生ループのおまじない
                frame += MaxFrame;
            }
            NowFrame = frame % (decimal)(MaxFrame + 1);
            int nextRepeat = (int)Math.Floor((double)(frame / (decimal)(MaxFrame + 1)));
            bool result = nextRepeat != numRepeat;
            numRepeat = nextRepeat;
            return result;
        }
        /*public void ApplyMotion(MMDBoneManager mmdBone, MMDFaceManager mmdFace, ref bool[] BoneUpdated)
        {
            int Frame = (int)Math.Round(NowFrame);
            int boneFrame = Frame;
            if (boneFrame > Math.Min(MaxFrame, motion.MaxBoneFrame))
                boneFrame = (int)Math.Min(MaxFrame, motion.MaxBoneFrame);
            for (int i = boneFrame * motion.NumBone; i < boneFrame * motion.NumBone + motion.NumBone; i++)
            {
                //計算済みトランスフォームを設定
                mmdBone[motion.Poses[i].BoneIndex].BoneTransform = motion.Poses[i].Poses;
                //IK計算済みなので、BoneUpdatedはOnにしない
            }
            for (int i = 0; i < motion.Faces.Length; i++)
            {
                while (motion.Faces[i].Length - 1 > motion.NowFrame[i] && motion.Faces[i][motion.NowFrame[i] + 1].Frame <= Frame)
                    ++motion.NowFrame[i];
                int j = motion.NowFrame[i];
                if (motion.Faces[i].Length - 1 == j || Frame < motion.Faces[i][j].Frame)
                    mmdFace.FaceTranslations[i] += new Vector4(motion.Faces[i][j].FaceVert, 0);
                else
                    mmdFace.FaceTranslations[i] += new Vector4(Vector3.Lerp(motion.Faces[i][j].FaceVert, motion.Faces[i][j + 1].FaceVert, 
                            ((float)(Frame - motion.Faces[i][j].Frame)) / ((float)(motion.Faces[i][j + 1].Frame - motion.Faces[i][j].Frame))
                            ), 0);
            }
        }*/
        
        
        public unsafe void ApplyMotion(MMDBoneManager mmdBone, MMDFaceManager mmdFace, ref bool[] BoneUpdated)
        {
            int Frame = (int)(NowFrame + 0.5m);
            int boneFrame = Frame;
            int FaceFrame = Frame;
            if (boneFrame > Math.Min(MaxFrame, motion.MaxBoneFrame))
                boneFrame = (int)Math.Min(MaxFrame, motion.MaxBoneFrame);
            if (FaceFrame > Math.Min(MaxFrame, motion.MaxFaceFrame))
                FaceFrame = (int)Math.Min(MaxFrame, motion.MaxFaceFrame);
            int start = boneFrame * motion.NumBone;
            for (int i = start; i < start + motion.NumBone; i++)
            {
                //計算済みトランスフォームを設定
                mmdBone.Bones[motion.Poses[i].BoneIndex].BoneTransform = motion.Poses[i].Poses;
                //IK計算済みなので、BoneUpdatedはOnにしない
            }
            start = FaceFrame * motion.NumFace;
            for (int i = 0; i < motion.NumFace; i++)
            {
                if (motion.Faces[i + start].X >= 0)
                {
                    mmdFace.FaceRates[i] = motion.Faces[i + start];
                    
                }
            }
            
            /*
            for (int i = 0; i < motion.Faces.Length; i++)
            {
                if (motion.Faces[i].Length == 0)
                    continue;
                while (motion.Faces[i].Length - 1 > motion.NowFrame[i] && motion.Faces[i][motion.NowFrame[i] + 1].Frame <= Frame)
                    ++motion.NowFrame[i];
                if (motion.Faces[i].Length - 1 == motion.NowFrame[i] || Frame < motion.Faces[i][motion.NowFrame[i]].Frame)
                    mmdFace.FaceTranslations[i] += new Vector4(motion.Faces[i][motion.NowFrame[i]].FaceVert, 0);
                else
                    mmdFace.FaceTranslations[i] += new Vector4(Vector3.Lerp(motion.Faces[i][motion.NowFrame[i]].FaceVert, motion.Faces[i][motion.NowFrame[i] + 1].FaceVert,
                            ((float)(Frame - motion.Faces[i][motion.NowFrame[i]].Frame)) / ((float)(motion.Faces[i][motion.NowFrame[i] + 1].Frame - motion.Faces[i][motion.NowFrame[i]].Frame))
                            ), 0);
                
            }*/
        }
        
    }
}
