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

namespace Innovation
{
    public class Actor : Component, I3DComponent
    {
        // The model to draw
        Model model;

        // 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, Material> Materials = new Dictionary<ModelMeshPart, Material>();

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

        public Actor(Model Model, Vector3 Position, GameScreen Parent)
            : base(Parent)
        {
            Setup(Model, Position);
        }

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

            foreach (ModelMesh mesh in Model.Meshes)
                foreach (ModelMeshPart part in mesh.MeshParts)
                    Materials.Add(part, new MeshPartMaterial().CreateFromMeshPart(part));
        }

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

            // Throw an exception if one isn't present
            if (camera == null)
                throw new Exception("Camera not found in engine's"
                + "service container, cannot draw");

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

            // Generate the world matrix (describes the objects 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)
                    Materials[part].Prepare3DDraw(world);

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