#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 QuatTransformContext
    {
        #region フィールド

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

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

        #endregion

        #region 初期化

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

        /// <summary>
        /// 指定された行列から生成する
        /// </summary>
        /// <param name="matrix"></param>
        /// <returns></returns>
        public static QuatTransformContext 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 QuatTransformContext( rotation, translation );
        }

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

        #endregion

        /// <summary>
        /// QuatTransformの結合
        /// </summary>
        public static QuatTransformContext operator *(QuatTransformContext value1, QuatTransformContext 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;

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

            result.Translation = newTranslation;

            return result;
        }

    }
}
