using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MikuMikuDance.XNA.Stages
{
    /// <summary>
    /// MikuMikuDanceで用いるカメラ
    /// </summary>
    public class MMDCamera : GameComponent, IMMDCamera
    {
        // 視錐台(シャドウマップ用)
        BoundingFrustum CameraFrustum;
        Vector3 m_CameraPos;
        Vector3 m_CameraTarget;
        Vector3 m_CameraUpVector;
        Matrix m_CameraMatrix;
        /// <summary>
        /// カメラのポジション
        /// </summary>
        public Vector3 CameraPos { get { return m_CameraPos; } set { m_CameraPos = value; } }
        /// <summary>
        /// カメラの視点
        /// </summary>
        public Vector3 CameraTarget { get { return m_CameraTarget; } set { m_CameraTarget = value; } }
        /// <summary>
        /// カメラの上方向を表すベクトル
        /// </summary>
        public Vector3 CameraUpVector { get { return m_CameraUpVector; } set { m_CameraUpVector = value; } }
        /// <summary>
        /// カメラに適応するワールド座標系
        /// </summary>
        public Matrix CameraMatrix { get { return m_CameraMatrix; } set { m_CameraMatrix = value; } }
        /// <summary>
        /// 視野角
        /// </summary>
        public float FieldOfView { get; set; }
        /// <summary>
        /// Nearプレーン距離
        /// </summary>
        public float Near { get; set; }
        /// <summary>
        /// Farプレーン距離
        /// </summary>
        public float Far { get; set; }
        /// <summary>
        /// アニメーションプレイヤー
        /// </summary>
        public CameraAnimationPlayer Player { get; private set; }
        /// <summary>
        /// 既定のコンストラクタ
        /// </summary>
        internal MMDCamera(Game game)
            :base(game)
        {
            CameraPos = new Vector3(0, 10, 35);
            CameraTarget = new Vector3(0, 10, 0);
            CameraUpVector = Vector3.Up;
            CameraMatrix = Matrix.Identity;
            FieldOfView = MathHelper.PiOver4;
            Near = 1.0f;
            Far = 300.0f;
            Player = new CameraAnimationPlayer();
            CameraFrustum = new BoundingFrustum(Matrix.Identity);
        }
        /// <summary>
        /// ビューマトリックス取得
        /// </summary>
        /// <returns>ビューマトリックス</returns>
        public Matrix GetViewMatrix()
        {
            Vector3 transformedCameraPos, transformedCameraTarget, transformedUpVector, temp1, temp2;
            Quaternion rot;
            m_CameraMatrix.Decompose(out temp1, out rot, out temp2);
            Vector3.Transform(ref m_CameraPos, ref m_CameraMatrix, out transformedCameraPos);
            Vector3.Transform(ref m_CameraTarget, ref m_CameraMatrix, out transformedCameraTarget);
            Vector3.Transform(ref m_CameraUpVector, ref rot, out transformedUpVector);
            return Matrix.CreateLookAt(transformedCameraPos,
                transformedCameraTarget, transformedUpVector);
        }
        /// <summary>
        /// シャドウマップ用ビューマトリックス取得
        /// </summary>
        /// <param name="LightPos">ライトの位置</param>
        /// <returns>シャドウマップ用ビューマトリックス</returns>
        public Matrix GetShadowViewMatrix(Vector3 LightPos)
        {
            Vector3 transformedLightPos, transformedCameraTarget, transformedUpVector, temp1, temp2;
            Quaternion rot;
            m_CameraMatrix.Decompose(out temp1, out rot, out temp2);
            Vector3.Transform(ref LightPos, ref m_CameraMatrix, out transformedLightPos);
            Vector3.Transform(ref m_CameraTarget, ref m_CameraMatrix, out transformedCameraTarget);
            Vector3.Transform(ref m_CameraUpVector, ref rot, out transformedUpVector);
            return Matrix.CreateLookAt(transformedLightPos,
                transformedCameraTarget, transformedUpVector);
        }
        /// <summary>
        /// プロジェクションマトリックス取得
        /// </summary>
        /// <param name="graphics">グラフィックデバイスオブジェクト</param>
        /// <returns>プロジェクションマトリックス</returns>
        public Matrix GetProjectionMatrix(GraphicsDevice graphics)
        {
            Viewport viewport = graphics.Viewport;
            float aspectRatio = (float)viewport.Width / (float)viewport.Height;
            return Matrix.CreatePerspectiveFieldOfView(FieldOfView, aspectRatio, Near, Far);
        }
        /// <summary>
        /// カメラUpdate
        /// </summary>
        /// <param name="gameTime">ゲームタイムオブジェクト</param>
        public override void Update(GameTime gameTime)
        {
            Player.Update(-1, false, this);
            
        }
        /// <summary>
        /// 視錐台のコーナー情報取得
        /// </summary>
        /// <param name="graphics">グラフィックスデバイス</param>
        /// <param name="shadowDist">シャドウ距離</param>
        /// <param name="frustumCorners">視錐台のコーナー</param>
        public void GetFrustumCorners(GraphicsDevice graphics, float shadowDist, Vector3[] frustumCorners)
        {
            //視錐台の更新
            Viewport viewport = graphics.Viewport;
            float aspectRatio = (float)viewport.Width / (float)viewport.Height;
            Matrix Proj = Matrix.CreatePerspectiveFieldOfView(FieldOfView, aspectRatio, Near, shadowDist);
            CameraFrustum.Matrix = GetViewMatrix() * Proj;
            //視錐台からコーナーの取得
            CameraFrustum.GetCorners(frustumCorners);
        }
    }
}
