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

namespace Innovation
{
    // Base Material class keeps track of an effect and handles basic
    // properties for it
    public class Material : Component
    {
        // The effect to handle
        Effect effect;

        public virtual Effect Effect
        {
            get { return effect; }
            set { effect = value; }
        }

        // Sets up the material from the values embedded in a MeshPart
        public virtual Material CreateFromMeshPart(ModelMeshPart MeshPart)
        {
            this.effect = MeshPart.Effect;
            return this;
        }

        // Set World, View, and Projection values
        public virtual void Prepare3DDraw(Matrix World)
        {
            effect.Parameters["World"].SetValue(World);
            effect.Parameters["View"].SetValue(
                Engine.Services.GetService<Camera>().View);
            effect.Parameters["Projection"].SetValue(
                Engine.Services.GetService<Camera>().Projection);
        }
    }

    // Material that handles a texture
    public class TexturedMaterial : Material
    {
        // The texture to keep track of
        Texture2D texture;

        // Set accesor sets the texture to the underlying effect
        public virtual Texture2D Texture
        {
            get { return texture; }
            set { texture = value; SetTextureToEffect(Effect, texture); }
        }

        public override Effect Effect
        {
            get { return base.Effect; }
            set
            {
                SetTextureToEffect(value, GetTextureFromEffect(base.Effect));
                base.Effect = value;
            }
        }

        // Override gets the texture from the MeshPart
        public override Material CreateFromMeshPart(ModelMeshPart MeshPart)
        {
            base.CreateFromMeshPart(MeshPart);
            this.texture = GetTextureFromEffect(MeshPart.Effect);
            return this;
        }

        // Method to get texture from various types of effect
        internal static Texture2D GetTextureFromEffect(Effect Effect)
        {
            if (Effect == null)
                return null;

            if (Effect is BasicEffect)
                return ((BasicEffect)Effect).Texture;
            else if (Effect.Parameters["Texture"] != null)
                return Effect.Parameters["Texture"].GetValueTexture2D();

            return null;
        }

        // Method to set the texture to various types of effect
        internal static void SetTextureToEffect(Effect Effect, Texture2D Texture)
        {
            if (Effect == null)
                return;

            if (Effect is BasicEffect)
                ((BasicEffect)Effect).Texture = Texture;
            else if (Effect.Parameters["Texture"] != null)
                Effect.Parameters["Texture"].SetValue(Texture);
        }
    }

    // Handles setting effect and texture to a MeshPart
    public class MeshPartMaterial : TexturedMaterial
    {
        ModelMeshPart meshPart;

        public ModelMeshPart MeshPart
        {
            get { return meshPart; }
            set { meshPart = value; value.Effect = Effect; }
        }

        public override Effect Effect
        {
            get { return base.Effect; }
            set { SetTextureToEffect(value, GetTextureFromEffect(base.Effect)); base.Effect = value; MeshPart.Effect = value; }
        }

        public override Material CreateFromMeshPart(ModelMeshPart MeshPart)
        {
            base.CreateFromMeshPart(MeshPart);
            this.meshPart = MeshPart;
            return this;
        }
    }
}
