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

namespace Innovation
{
    public class Actor : Component, I3DComponent
    {
        // The model to draw
        Model model;
        string modelName;
        Dictionary<ModelMeshPart, string> materials = new Dictionary<ModelMeshPart, string>();

        // I3DComponent values
        public virtual Vector3 Position { get; set; }
        public Vector3 EulerRotation
        {
            get { return MathUtil.MatrixToVector3(Rotation); }
            set { Rotation = MathUtil.Vector3ToMatrix(value); }
        }
        public virtual Matrix Rotation { get; set; }
        public virtual Vector3 Scale { get; set; }
        public virtual BoundingBox BoundingBox
        {
            get
            {
                return new BoundingBox(
                    Position - (Scale / 2),
                    Position + (Scale / 2)
                );
            }
        }

        public Dictionary<ModelMeshPart, string> Materials
        {
            get { return materials; }
        }

        public string Model
        {
            get { return modelName; }
            set
            {
                modelName = value;
                model = Engine.Content.Load<Model>(value);

                materials.Clear();

                foreach(ModelMesh mesh in model.Meshes)
                    foreach (ModelMeshPart part in mesh.MeshParts)
                    {
                        TexturedMaterial mat = new TexturedMaterial();

                        if (part.Tag != null)
                        {
                            string texName = (string)part.Tag;
                            mat.TextureName = "Content/" + texName;
                        }

                        materials.Add(part, mat.Name);
                    }
            }
        }

        public Actor()
        {

        }

        // Constructors take a model to draw and a position
        public Actor(string Model, Vector3 Position)
            : base()
        {
            Setup(Model, Position);
        }

        // Provide a method to setup the actor so we don't need to 
        // write it in each constructor
        void Setup(string Model, Vector3 Position)
        {
            this.Model = Model;
            this.Position = Position;
            Scale = Vector3.One;
            EulerRotation = Vector3.Zero;
        }

        public override void Draw()
        {
            // Look for a camera in the service container
            Camera camera = Engine.Services.GetService<Camera>();

            if (model == null || camera == null)
                return;

            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            // Generate the world matrix (describes the object's movement in 3D)
            Matrix world = MathUtil.CreateWorldMatrix(Position, Rotation, Scale);

            // Set some renderstates so the model will draw properly
            Engine.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            Engine.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            Engine.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            Engine.GraphicsDevice.RenderState.DepthBufferEnable = true;

            // Loop through meshes and effects and set them up to draw
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    TexturedMaterial material = (TexturedMaterial)Engine.Services.GetService<MaterialManager>().GetMaterial(Materials[part]);
                    material.Prepare3DDraw(world);
                    part.Effect = material.Effect;
                }

                // Draw the mesh
                mesh.Draw();
            }
        }

        public override void SaveSerializationData(SerializationData Data)
        {
            base.SaveSerializationData(Data);
            Data.AddData("Actor.Model", modelName);
        }

        public override void LoadFromSerializationData(SerializationData Data)
        {
            base.LoadFromSerializationData(Data);
            Model = Data.GetData<string>("Actor.Model");
        }
    }
}