#region File Description
//-----------------------------------------------------------------------------
// QuatTransform.cs
//-----------------------------------------------------------------------------
#endregion
#region Using ステートメント

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
#endregion

namespace MikuMikuDance.XNA.Model
{
    /// <summary>
    /// クォータニオンを使った回転と平行移動を表すことのできる頂点変換用構造体
    /// 通常の行列の半分以下のメモリ使用量になる
    /// </summary>
    [ContentSerializerRuntimeType("MikuMikuDance.XNA.Model.ModelData.QuatTransform, MikuMikuDanceXNA")]
    public struct QuatTransformContent
    {
        #region フィールド

        /// <summary>
        /// 回転
        /// </summary>
        public Quaternion   Rotation;

        /// <summary>
        /// 平行移動
        /// </summary>
        public Vector3      Translation;

        #endregion

        #region 初期化

        /// <summary>
        /// クォータニオンと平行移動を指定して生成する
        /// </summary>
        /// <param name="rotation"></param>
        /// <param name="translation"></param>
        public QuatTransformContent(Quaternion rotation, Vector3 translation)
        {
            Rotation = rotation;
            Translation = translation;
        }

        /// <summary>
        /// 指定された行列から生成する
        /// </summary>
        /// <param name="matrix"></param>
        /// <returns></returns>
        public static QuatTransformContent FromMatrix( Matrix matrix )
        {
            // 行列の分解
            Quaternion rotation;
            Vector3 translation;
            Vector3 scale;
            matrix.Decompose( out scale, out rotation, out translation );

            // 一意のスケールか？
            if ( !CloseEnough( scale.X, scale.Y ) || !CloseEnough( scale.X, scale.Z ) )
            {
                throw new InvalidOperationException(
                    "一意のスケール(X,Y,Zが同じスケール値)ではありません" );
            }

            if ( !CloseEnough( scale.X, 1.0f ) )
                throw new InvalidOperationException( "スケール値が1以外です" );

            return new QuatTransformContent( rotation, translation );
        }

        static bool CloseEnough( float a, float b )
        {
            return ( Math.Abs( a - b ) < 1e-4f );
        }

        #endregion

        /// <summary>
        /// QuatTransformの結合
        /// </summary>
        public static QuatTransformContent operator *(QuatTransformContent value1, QuatTransformContent value2)
        {
            // 平行移動の算出
            Vector3 newTranslation;
            Vector3.Transform(ref value1.Translation, ref value2.Rotation,
                                out newTranslation);

            newTranslation.X += value2.Translation.X;
            newTranslation.Y += value2.Translation.Y;
            newTranslation.Z += value2.Translation.Z;

            // 回転部分の結合
            QuatTransformContent result;
            Quaternion.Concatenate(ref value1.Rotation, ref value2.Rotation,
                                        out result.Rotation);

            result.Translation = newTranslation;

            return result;
        }

    }
}
