using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MikuMikuDance.XNA.Model;

namespace MikuMikuDance.XNA.Accessory
{
    
    /// <summary>
    /// MikuMikuDanceのアクセサリデータ
    /// </summary>
    public class MMDAccessory : DrawableGameComponent
    {
        internal MMDModel parent = null;
        MikuMikuDanceXNA mmd = null;
        /// <summary>
        /// アクセサリに用いるモデルデータ
        /// </summary>
        public Microsoft.Xna.Framework.Graphics.Model ModelData { get; set; }
        /// <summary>
        /// このモデルに適応するワールド座標系
        /// </summary>
        /// <remarks>MMDモデルにもたせている場合はMMDモデル座標系</remarks>
        public Matrix World { get; set; }
        /// <summary>
        /// MikuMikuDanceXNA.TimeRularをこのアクセサリが呼び出すかどうか
        /// </summary>
        public bool UseTimeRular { get; set; }
        /// <summary>
        /// このオブジェクトがモデルの描画設定を自動で呼ぶかどうか
        /// </summary>
        /// <remarks>falseにして、設定を手動にすることでGPU命令発行を減らすことは可能</remarks>
        public bool CallDrawSetup { get; set; }


        

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="model">モデルデータ</param>
        /// <param name="mmd">MikuMikuDanceXNAオブジェクト</param>
        /// <param name="game">Gameオブジェクト</param>
        /// <param name="world">初期トランスフォームマトリックス</param>
        public MMDAccessory(Microsoft.Xna.Framework.Graphics.Model model,MikuMikuDanceXNA mmd, Game game,Matrix world)
            :base(game)
        {
            this.mmd = mmd;
            ModelData = model;
            World = world;
            UseTimeRular = true;
            CallDrawSetup = true;
            game.Components.Add(this);
            //アクセサリの初期設定
            foreach (ModelMesh mesh in ModelData.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    BasicEffect bEffect = effect as BasicEffect;
                    if (bEffect != null)
                    {
                        bEffect.LightingEnabled = true;
                        bEffect.DirectionalLight0.Enabled = true;
                        bEffect.DirectionalLight1.Enabled = false;
                        bEffect.DirectionalLight2.Enabled = false;
                    }
                    else
                    {
                        //スクリーン付きのアクセサリかどうか調べる
                        foreach (var i in effect.Parameters)
                        {
                            if (i.Name == "Screen")
                            {//スクリーン付きなのでフックする
                                effect.Parameters["ScreenMode"].SetValue(0);//最初はScreenはoffにしておく
                                mmd.ScreenHook.Add(effect);
                                break;
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// デストラクタ処理
        /// </summary>
        ~MMDAccessory()
        {//C#のデストラクタはいつ呼ばれるか分からんのであまり意味なけど、処理しておく必要はあるので書く
            foreach (ModelMesh mesh in ModelData.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    foreach (var i in effect.Parameters)
                    {
                        if (i.Name == "Screen")
                        {//フックを外す
                            mmd.ScreenHook.Remove(effect);
                            break;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// アクセサリの描画(DrawableGameComponent用
        /// </summary>
        /// <param name="gameTime">GameTimeオブジェクト</param>
        public override void Draw(GameTime gameTime)
        {
            //親がいない場合は自分で描画する。親がいる場合は親のタイミングで描画される
            if (parent == null)
                Draw(Matrix.Identity);
            //base.Draw(gameTime);
        }
        /// <summary>
        /// オブジェクトの描画設定
        /// </summary>
        /// <param name="graphics">GraphicDevice</param>
        public static void DrawSetup(GraphicsDevice graphics)
        {
            //基本的な設定
            graphics.RenderState.AlphaBlendEnable = true;
            graphics.RenderState.BlendFunction = BlendFunction.Add;
            graphics.RenderState.SourceBlend = Blend.SourceAlpha;
            graphics.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            graphics.RenderState.AlphaTestEnable = true;
            graphics.RenderState.ReferenceAlpha = 1;
            graphics.RenderState.AlphaFunction = CompareFunction.GreaterEqual;
            graphics.RenderState.DepthBufferEnable = true;
            graphics.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            graphics.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            //モデルのCullModeを変更
            graphics.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
        }
        /// <summary>
        /// 内部描画ルーチン
        /// </summary>
        /// <param name="baseTransform">基準トランスフォーム</param>
        internal void Draw(Matrix baseTransform)
        {
            if (mmd.TimeRular != null && UseTimeRular)
            {
                mmd.TimeRular.BeginMark(0, "DrawAccessory", Color.Yellow);
            }
            //アクセサリ描画用に設定
            if (CallDrawSetup)
                DrawSetup(Game.GraphicsDevice);
            
            //データ準備
            Vector3 AmbientLight = mmd.LightManager.AmbientLight.ToVector3();
            Vector3 LightDir = mmd.LightManager.KeyLight.Direction;
            LightDir.Normalize();
            Vector3 DiffuseColor = mmd.LightManager.KeyLight.Color.ToVector3();
            Vector3 SpecularColor = mmd.LightManager.KeyLight.Color.ToVector3();
            Matrix trueWorld = World * baseTransform;
            Matrix View = mmd.Camera.GetViewMatrix();
            Matrix Projection = mmd.Camera.GetProjectionMatrix(Game.GraphicsDevice);
            //アクセサリの描画
            foreach (ModelMesh mesh in ModelData.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    effect.Parameters["AmbientLightColor"].SetValue(AmbientLight);
                    effect.Parameters["DirLight0Direction"].SetValue(LightDir);
                    effect.Parameters["DirLight0DiffuseColor"].SetValue(DiffuseColor);
                    effect.Parameters["DirLight0SpecularColor"].SetValue(SpecularColor);
                    effect.Parameters["World"].SetValue(trueWorld);
                    effect.Parameters["View"].SetValue(View);
                    effect.Parameters["Projection"].SetValue(Projection);
                }
                /*
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.AmbientLightColor = mmd.LightManager.AmbientLight.ToVector3();
                    effect.DirectionalLight0.Direction = mmd.LightManager.KeyLight.Direction;
                    effect.DirectionalLight0.Direction.Normalize();
                    effect.DirectionalLight0.DiffuseColor = mmd.LightManager.KeyLight.Color.ToVector3();//new Vector3(0.2f, 0.2f, 0.2f);
                    effect.DirectionalLight0.SpecularColor = mmd.LightManager.KeyLight.Color.ToVector3();
                    effect.World = World * baseTransform;
                    effect.View = mmd.Camera.GetViewMatrix();
                    effect.Projection = mmd.Camera.GetProjectionMatrix(Game.GraphicsDevice);
                }*/
                mesh.Draw();
            }
            if (mmd.TimeRular != null && UseTimeRular)
            {
                mmd.TimeRular.EndMark(0, "DrawAccessory");
            }
        }

    }
}
