using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using MikuMikuDance.XNA.Motion.MotionData;
using TInput = MikuMikuDance.Motion.Motion2.MMDMotion2;
using TOutput = MikuMikuDance.XNA.Motion.MotionData.MMDMotionData;
using System.ComponentModel;
using System;
using MikuMikuDance.Motion.BaseBoneProcess;

namespace MikuMikuDance.XNA.Motion
{
    /// <summary>
    /// MikuMikuDanceのモーションデータデータをXNAにインポートするためのプロセッサ
    /// </summary>
    [ContentProcessor(DisplayName = "MikuMikuDanceモーション : MikuMikuDance for XNA")]
    public class MMDMotionProcessor : ContentProcessor<TInput, TOutput>
    {
        bool m_UseBoneToTransform = false;
        string m_TransformBoneName = AnimationPlayer.transBoneName;
        bool m_UseBaseBone = false;
        string m_BaseBoneName = "";
        string m_EffectedBoneName = "";
        

        /// <summary>
        /// 指定ボーン名をTransformへ変換の使用
        /// </summary>
        [DefaultValue(false)]
        [DisplayName("指定ボーン名をTransformへ変換の使用")]
        [Description("指定ボーンのモーションはボーンではなくTransformに適応されます")]
        public bool UseBoneToTransform { get { return m_UseBoneToTransform; } set { m_UseBoneToTransform = value; } }

        /// <summary>
        /// 指定ボーン名をTransformへ変換
        /// </summary>
        [DefaultValue(AnimationPlayer.transBoneName)]
        [DisplayName("指定ボーンのモーションはボーンではなくTransformに適応されます")]
        [Description("モーションがベースボーン基準に置き換わり、ベースボーンのモーションはTransformに適応されます")]
        public string TransformBoneName { get { return m_TransformBoneName; } set { m_TransformBoneName = value; } }

        /// <summary>
        /// 基準ボーンによるモーション補正の使用
        /// </summary>
        [DefaultValue(false)]
        [DisplayName("基準ボーンによるモーション補正の使用")]
        [Description("モーションが基準ボーン基準に置き換わり、基準ボーンのモーションはTransformに適応されます")]
        public bool UseBaseBone { get { return m_UseBaseBone; } set { m_UseBaseBone = value; } }

        /// <summary>
        /// 基準ボーンの名
        /// </summary>
        [DefaultValue("")]
        [DisplayName("基準ボーンの名")]
        [Description("モーションが基準ボーン基準に置き換わり、基準ボーンのモーションはTransformに適応されます")]
        public string BaseBoneName { get { return m_BaseBoneName; } set { m_BaseBoneName = value; } }

        /// <summary>
        /// 影響ボーンの名(;区切り)
        /// </summary>
        [DefaultValue("")]
        [DisplayName("影響ボーンの名(;区切り)")]
        [Description("モーションが基準ボーン基準に置き換わり、基準ボーンのモーションはTransformに適応されます")]
        public string EffectedBoneName { get { return m_EffectedBoneName; } set { m_EffectedBoneName = value; } }

        /// <summary>
        /// コンテンツ変換
        /// </summary>
        /// <param name="input">MMDモーションデータ</param>
        /// <param name="context">コンテントプロセッサ</param>
        /// <returns>MMDモーションデータ for XNA</returns>
        public override TOutput Process(TInput input, ContentProcessorContext context)
        {

            if(UseBaseBone)
            {
                UseBoneToTransform = true;
                TransformBoneName = AnimationPlayer.transBoneName;
                string[] effectedBones = EffectedBoneName.Split(new string[1] { ";" }, System.StringSplitOptions.RemoveEmptyEntries);
                bool flag = false;
                bool[] flag3 = new bool[effectedBones.Length];
                Array.Clear(flag3, 0, flag3.Length);
                foreach (var it in input.Motions)
                {
                    if (it.BoneName == m_BaseBoneName)
                    {
                        flag = true;
                    }
                    for (int i = 0; i < effectedBones.Length; i++ )
                    {
                        if (it.BoneName == effectedBones[i])
                            flag3[i] = true;
                    }
                }
                if (!flag)
                    throw new InvalidContentException("ベースボーンを使用する場合、モーション内にそのボーン名が含まれている必要があります");
                for (int i = 0; i < effectedBones.Length; i++)
                {
                    if (!flag3[i])
                        throw new InvalidContentException("影響ボーンの名前で指定されたボーン名が見つかりません");
                }
                BaseProcessor.Process(input, m_BaseBoneName, effectedBones, AnimationPlayer.transBoneName);
            }
            if (UseBoneToTransform)
            {
                foreach (var it in input.Motions)
                {
                    if (it.BoneName == TransformBoneName)
                    {
                        it.BoneName = AnimationPlayer.transBoneName;//nullはTransform用のボーン名
                    }
                }
            }
            TOutput result = new TOutput();
            //ボーンモーションデータの変換
            result.BoneMotions = new MMDBoneMotion[input.Motions.LongLength];
            if (result.BoneMotions.LongLength > int.MaxValue)
                throw new InvalidContentException("ボーンモーション数が多すぎます。MikuMikuDanceXNAは.NET Compact Frameworkの制限のため、32bit符号付き整数最大値までしかモーション数をサポートしていません");
            for (long i = 0; i < result.BoneMotions.LongLength; i++)
            {
                result.BoneMotions[i] = new MMDBoneMotion();
                result.BoneMotions[i].BoneName = input.Motions[i].BoneName;
                result.BoneMotions[i].FrameNo = input.Motions[i].FrameNo;

                result.BoneMotions[i].Curve = new BezierCurve[4];
                for (int j = 0; j < result.BoneMotions[i].Curve.Length; j++)
                {
                    BezierCurve curve = new BezierCurve();
                    curve.v1 = new Vector2((float)input.Motions[i].Interpolation[0][0][j] / 128f, (float)input.Motions[i].Interpolation[0][1][j] / 128f);
                    curve.v2 = new Vector2((float)input.Motions[i].Interpolation[0][2][j] / 128f, (float)input.Motions[i].Interpolation[0][3][j] / 128f);
                    result.BoneMotions[i].Curve[j] = curve;
                }

                result.BoneMotions[i].Location = new Vector3(input.Motions[i].Location[0], input.Motions[i].Location[1], input.Motions[i].Location[2]);
                result.BoneMotions[i].Quatanion = new Quaternion(input.Motions[i].Quatanion[0], input.Motions[i].Quatanion[1], input.Motions[i].Quatanion[2], input.Motions[i].Quatanion[3]);
            }
            //表情モーションの変換
            result.FaceMotions = new MMDFaceMotion[input.FaceMotions.LongLength];
            if (result.FaceMotions.LongLength > int.MaxValue)
                throw new InvalidContentException("表情モーション数が多すぎます。MikuMikuDanceXNAは.NET Compact Frameworkの制限のため、32bit符号付き整数最大値までしかモーション数をサポートしていません");
            for (long i = 0; i < input.FaceMotions.Length; i++)
            {
                result.FaceMotions[i] = new MMDFaceMotion();
                result.FaceMotions[i].Rate = input.FaceMotions[i].Rate;
                result.FaceMotions[i].FaceName = input.FaceMotions[i].FaceName;
                result.FaceMotions[i].FrameNo = input.FaceMotions[i].FrameNo;
                float temp = input.FaceMotions[i].FrameNo;
            }
            //カメラモーションの変換
            result.CameraMotions = new MMDCameraMotion[input.CameraMotions.LongLength];
            if (result.CameraMotions.LongLength > int.MaxValue)
                throw new InvalidContentException("カメラモーション数が多すぎます。MikuMikuDanceXNAは.NET Compact Frameworkの制限のため、32bit符号付き整数最大値までしかモーション数をサポートしていません");
            for (long i = 0; i < input.CameraMotions.Length; i++)
            {
                result.CameraMotions[i] = new MMDCameraMotion();
                result.CameraMotions[i].FrameNo = input.CameraMotions[i].FrameNo;
                result.CameraMotions[i].Length = input.CameraMotions[i].Length;
                result.CameraMotions[i].Location = MMDMath.VectorFromArray(input.CameraMotions[i].Location);
                result.CameraMotions[i].Quatanion = Quaternion.CreateFromYawPitchRoll(input.CameraMotions[i].Rotate[1], input.CameraMotions[i].Rotate[0], input.CameraMotions[i].Rotate[2]);
                result.CameraMotions[i].ViewAngle = MathHelper.ToRadians(input.CameraMotions[i].ViewingAngle);
                result.CameraMotions[i].Curve = new BezierCurve[6];
                for (int j = 0; j < result.CameraMotions[i].Curve.Length; j++)
                {
                    BezierCurve curve = new BezierCurve();
                    curve.v1 = new Vector2((float)input.CameraMotions[i].Interpolation[j][0] / 128f, (float)input.CameraMotions[i].Interpolation[j][2] / 128f);
                    curve.v2 = new Vector2((float)input.CameraMotions[i].Interpolation[j][1] / 128f, (float)input.CameraMotions[i].Interpolation[j][3] / 128f);
                    result.CameraMotions[i].Curve[j] = curve;
                }
            }
            //ライトモーションの変換
            result.LightMotions = new MMDLightMotion[input.LightMotions.LongLength];
            if (result.LightMotions.LongLength > int.MaxValue)
                throw new InvalidContentException("ライトモーション数が多すぎます。MikuMikuDanceXNAは.NET Compact Frameworkの制限のため、32bit符号付き整数最大値までしかモーション数をサポートしていません");
            for (long i = 0; i < input.LightMotions.Length; i++)
            {
                result.LightMotions[i] = new MMDLightMotion();
                result.LightMotions[i].FrameNo = input.LightMotions[i].FrameNo;
                result.LightMotions[i].Color = MMDMath.VectorFromArray(input.LightMotions[i].Color);
                result.LightMotions[i].Location = MMDMath.VectorFromArray(input.LightMotions[i].Location);
            }
            //XNA用に変換したデータを返却
            return result;
        }
    }
}