using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MikuMikuDance.XNA
{
    class DrawManagerInstanceInfo
    {
        public long beforeDrawTime = -1;
        public long beforeShadowTime = -1;
        public Matrix LightViewProj = Matrix.Identity;
    }
    /// <summary>
    /// モデル及びアクセサリで共通するHLSLエフェクトのsharedパラメータの設定を1フレームに1回行うためのクラス
    /// </summary>
    class DrawManager
    {
        /// <summary>
        /// 現在のフレームでのView
        /// </summary>
        public Matrix View;

        int instanceNum;
        //インスタンス情報管理
        static List<DrawManagerInstanceInfo> DrawManagerInfo = new List<DrawManagerInstanceInfo>();

        //通常の作成は不可
        private DrawManager(MikuMikuDanceXNA mmdx)
        {
            instanceNum = DrawManagerInfo.Count;
            DrawManagerInfo.Add(new DrawManagerInstanceInfo());
        }

        internal static void InitDrawManager(MikuMikuDanceXNA mmdXNA)
        {
            if (mmdXNA.DrawManager != null)
                throw new Exception("MikuMikuDanceXNAオブジェクトに対して2回InitDrawManagerを行った");
            mmdXNA.DrawManager = new DrawManager(mmdXNA);
        }

        //共用パラメータセットアップ
        internal void SharedEffectParameterSetup(long totalTick, bool IsShadow, bool UseShadowMap, Effect effect, GraphicsDevice graphics, MikuMikuDanceXNA mmdXNA)
        {
            if (IsShadow)
            {
                if (DrawManagerInfo[instanceNum].beforeShadowTime < totalTick)
                {//1フレームに1回呼ぶ仕掛け
                    Matrix proj;
                    if (mmdXNA.ShadowMapManager == null)
                        throw new ApplicationException("MikuMikuDanceXNA.ShadowMapManager==nullです。シャドウマップ描画にはShadowMapManagerに適切なオブジェクトを入れる必要があります");
                    //シャドウマップマネージャからシャドウマップDraw時のViewとProjectionマトリクスを取得
                    mmdXNA.ShadowMapManager.CreateLightViewProjMatrix(mmdXNA.Camera, mmdXNA.LightManager, graphics, out View, out proj);
                    //View&Projectionをセット
                    effect.Parameters["View"].SetValue(View);
                    effect.Parameters["Projection"].SetValue(proj);
                    //光源から見たView&Proj計算して、次のパス用に保存しておく
                    DrawManagerInfo[instanceNum].LightViewProj = View * proj;
                    //時刻更新
                    DrawManagerInfo[instanceNum].beforeShadowTime = totalTick;
                    //別のMMDXNAインスタンスがある
                    if (DrawManagerInfo.Count > 2)
                    {//更新をなかった事にする。(理由はshared設定を上書きしているため)
                        for (int i = 0; i < DrawManagerInfo.Count; i++)
                        {
                            if (i != instanceNum)
                                DrawManagerInfo[i].beforeShadowTime = -1;
                        }
                    }
                }
            }
            else
            {
                if (DrawManagerInfo[instanceNum].beforeDrawTime < totalTick)
                {//1フレームに1回呼ぶ仕掛け
                    //カメラからViewとProjectionマトリクスを取得&設定
                    View = mmdXNA.Camera.GetViewMatrix();
                    effect.Parameters["View"].SetValue(View);
                    effect.Parameters["Projection"].SetValue(mmdXNA.Camera.GetProjectionMatrix(graphics));
                    //ライティング設定を取得
                    //ライティングセット
                    effect.Parameters["AmbientLightColor"].SetValue(mmdXNA.LightManager.AmbientLight.ToVector3());
                    effect.Parameters["DirLight0Direction"].SetValue(mmdXNA.LightManager.KeyLight.Direction);
                    effect.Parameters["EyePosition"].SetValue(mmdXNA.Camera.CameraPos);
                    if (UseShadowMap)
                    {//シャドウマップ処理
                        if (mmdXNA.ShadowMapManager == null)
                            throw new System.ApplicationException("シャドウマップマネージャがMMDXにセットされていません");

                        Texture2D shadowTex;
                        Vector2 offset;
                        //シャドウマップマネージャからテクスチャをもらい、セットする
                        shadowTex = mmdXNA.ShadowMapManager.GetShadowMap(out offset);
                        effect.Parameters["ShadowMap"].SetValue(shadowTex);
                        effect.Parameters["ShadowMapOffset"].SetValue(offset);
                        //保存済みの光源から見たView&Projをセット
                        effect.Parameters["ShadowViewProj"].SetValue(DrawManagerInfo[instanceNum].LightViewProj);
                    }
                    //時刻を更新
                    DrawManagerInfo[instanceNum].beforeDrawTime = totalTick;
                    //別のMMDXNAインスタンスがある
                    if (DrawManagerInfo.Count > 2)
                    {//更新をなかった事にする。(理由はshared設定を上書きしているため)
                        for (int i = 0; i < DrawManagerInfo.Count; i++)
                        {
                            if (i != instanceNum)
                                DrawManagerInfo[i].beforeDrawTime = -1;
                        }
                    }
                }
            }
        }
    }
}
