using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using MikuMikuDance.Motion;
using MikuMikuDance.Motion.Motion2;
using MikuMikuDance.XNA.Model;
using MikuMikuDance.XNA.Model.ModelData;
using MikuMikuDance.XNA.Motion.MotionData;
using TInput = MikuMikuDance.XNA.Motion.BakeMotionSet;
using TOutput = MikuMikuDance.XNA.Motion.MMDBakedMotion;

namespace MikuMikuDance.XNA.Motion
{
    /// <summary>
    /// ベイク済みモーション作成プロセッサ
    /// </summary>
    [ContentProcessor(DisplayName = "MikuMikuDanceモーションベイク : MikuMikuDance for XNA")]
    public class MMDBakedMotionProcessor : ContentProcessor<TInput, TOutput>
    {
        /// <summary>
        /// モーションのベイク処理
        /// </summary>
        /// <param name="input">ベイクモーションセット</param>
        /// <param name="context">ベイク済みモーション</param>
        /// <returns></returns>
        public override TOutput Process(TInput input, ContentProcessorContext context)
        {
            //モーションの変換
            string modelName = input.model.Name;
            MMDMotion2 motion2;
            try
            {
                motion2 = MotionManager.Read(input.motion.Filename, MikuMikuDance.Motion.CoordinateType.RightHandedCoordinate) as MMDMotion2;
            }
            catch (Exception e)
            {
                throw new InvalidContentException("モーションファイルの読み込みに失敗しました。モーションファイルが壊れている可能性があります。MikuMikuDanceで出力しなおすと上手くいくかもしれません", e);
            }
            MMDImporterOutput modelInter = context.BuildAndLoadAsset<MMDImporterOutput, MMDImporterOutput>(input.model, null);
            MMDMotionProcessor motionProc = new MMDMotionProcessor();
            MMDProcessor modelProc = new MMDProcessor();
            MMDMotionData motionData = motionProc.Process(motion2, context);
            MMDModelContent model = modelProc.Process(modelInter, context);
            //ベイクルーチンでベイクする
            int NumThread;
            //論理コア数を取得して、最適スレッド数を取得
            Win32API.SYSTEM_INFO info = new Win32API.SYSTEM_INFO();
            Win32API.GetSystemInfo(ref info);
            NumThread = (int)info.dwNumberOfProcessors;
            //MikuMikuDance for XNAのベイク機能を呼び出して、このタイミングでベイクを行う
            //この場にノーマルモーショントラックを生成する
            NormalMotionTrack normal = new NormalMotionTrack();
            MMDMotion motion = new MMDMotion();
            motion.Initialize(motionData);
            normal.Motion = motion;
            normal.IsEmpty = false;
            //MMDModelDataが要るのだが、モデルポリゴンデータが邪魔で作れないので、それ抜きで無理やり作る
            MMDModelData ModelData = new MMDModelData();
            ModelData.Bones = model.Bones;
            ModelData.Joints = model.Joints;
            ModelData.ModelData = null;//これだけは一度書き出さないと作れない……、ベイクには要らないので無視
            ModelData.NumVertexForFace = model.NumVertexForFace;
            ModelData.Rigids = model.Rigids;
            ModelData.Skins = model.Skins;
            //モデルデータより、ボーンマネージャとフェイスマネージャを生成する
            MMDBoneManager BoneManager = new MMDBoneManager(ModelData);
            MMDFaceManager FaceManager = new MMDFaceManager(ModelData);
            //MikuMikuDance for XNAのベイク機能を呼び出してベイク
            TOutput result = AnimationBaker.Bake(NumThread, normal, BoneManager, FaceManager);
            //チェック
#if DEBUG
            for (long i = 0; i < result.Faces.LongLength; i++)
            {
                if(MMDMath.IsNaN(ref result.Faces[i]))
                    throw new InvalidContentException("BakeMotionのチェック中にNaNを発見-face");
            }
            for (long i = 0; i < result.Poses.LongLength; i++)
            {
                if (MMDMath.IsNaN(ref result.Poses[i].Poses))
                    throw new InvalidContentException("BakeMotionのチェック中にNaNを発見-Pose");
                if (!MMDMath.IsValid(ref result.Poses[i].Poses.Rotation))
                    throw new InvalidContentException("BakeMotionのチェック中に無効なrotationを発見-Pose");
                
            }
#endif
            //モデル名格納
            result.ModelName = modelName;
            return result;
        }

        private void CheckNaN(ref QuatTransform quatTransform)
        {
            CheckNaN(ref quatTransform.Translation);
            CheckNaN(ref quatTransform.Rotation);
        }

        private void CheckNaN(ref Quaternion quaternion)
        {
            if (float.IsNaN(quaternion.W) ||
                float.IsNaN(quaternion.X) ||
                float.IsNaN(quaternion.Y) ||
                float.IsNaN(quaternion.Z))
                throw new InvalidContentException("BakeMotionのチェック中にNaNを発見-q");
        }

        private void CheckNaN(ref Vector3 vector3)
        {
            if (float.IsNaN(vector3.X) ||
                float.IsNaN(vector3.Y) ||
                float.IsNaN(vector3.Z))
                throw new InvalidContentException("BakeMotionのチェック中にNaNを発見-v3");
        }
        void CheckNaN(ref Vector4 vec)
        {
            if (float.IsNaN(vec.W) ||
                float.IsNaN(vec.X) ||
                float.IsNaN(vec.Y) ||
                float.IsNaN(vec.Z))
                throw new InvalidContentException("BakeMotionのチェック中にNaNを発見-v4");
        
        }

    }
}