using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using SkinnedModelLibrary;

namespace FredriksdalAssault2
{
    /// <summary>
    /// This class is used to update and draw a soldier from a clipName and a Soldierworld variable.
    /// </summary>
    public class SimpleSoldier
    {
        #region Variables

        // Models
        Model soldierModel;
        Model weapon;

        // Spheres surrounding the enemy model to be able to detect collisions.
        SkinnedSphere[] skinnedSpheresXML;
        public BoundingSphere[] boundingSpheres;

        // Animation variables
        AnimationPlayer animationPlayer;
        SkinningData skinningData;
        Matrix[] boneTransforms;
        public String clipName = "Take 002";
        public AnimationClip clip;

        // The matrix to translate the soldier position to 2D space.
        public Matrix soldierWorld;

        // Movement variables
        public Vector3 position;
        public Vector3 direction;

        // Rotation variables
        public Matrix rotationMatrix;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a simple soldier.
        /// </summary>
        /// <param name="contentManager"></param>
        /// <param name="position"></param>
        /// <param name="direction"></param>
        public SimpleSoldier(ContentManager contentManager, Vector3 position, Vector3 direction, String redOrBlackSoldier)
        {

            // Set models
            this.soldierModel = contentManager.Load<Model>("Models/" + redOrBlackSoldier + "Soldier/" + redOrBlackSoldier + "-soldier");
            this.weapon = contentManager.Load<Model>("Models/SoldierResources/weapon");

            // Set the position
            this.position = position;
            this.direction = direction;
            this.direction.Normalize();
            this.rotationMatrix = Matrix.Identity;

            // Set the soldier world according to the position.
            this.soldierWorld = Matrix.CreateTranslation(this.position);

            // Look up our custom skinning information.
            skinningData = this.soldierModel.Tag as SkinningData;

            if (skinningData == null)
                throw new InvalidOperationException
                    ("This model does not contain a SkinningData tag.");

            // Bone Transform for the enemy
            this.boneTransforms = new Matrix[skinningData.BindPose.Count];

            // Create an animation player.
            this.animationPlayer = new AnimationPlayer(skinningData);
            this.clip = skinningData.AnimationClips[clipName];

            // Start the animation clip
            this.animationPlayer.StartClip(clip);

            // Load the bounding spheres XML file for the soldier.
            this.skinnedSpheresXML = contentManager.Load<SkinnedSphere[]>("Models/SoldierResources/collisionSpheres");
            this.boundingSpheres = new BoundingSphere[skinnedSpheresXML.Length]; 
        }

        /// <summary>
        /// Update a soldier model from a world matrix and a clip number.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="soldierWorldMatrix"></param>
        /// <param name="clipName"></param>
        public void UpdateSimpleSoldier(GameTime gameTime, Vector3 position, Quaternion rotation, byte clipNumber)
        {
            // Set the soldierWorldMatrix
            this.soldierWorld = Matrix.Transform(Matrix.Identity, rotation) * Matrix.CreateTranslation(position);

            // Create a string for clip name.
            String parameterClipName = "Unknown";

            // Set the string value for clip name.
            if (clipNumber == 1)
                parameterClipName = "Take 001";
            else if (clipNumber == 2)
                parameterClipName = "Take 002";

            // Start the correct animation clip.
            if (this.clipName != parameterClipName)
            {
                this.clipName = parameterClipName;

                this.clip = skinningData.AnimationClips[this.clipName];

                // Start the animation clip
                this.animationPlayer.StartClip(this.clip);
            }

            if (gameTime == null)
            {
                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(TimeSpan.Zero, true);
            }
            else
            {
                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(gameTime.ElapsedGameTime, true);
            }

            // Copy the transforms into our own array, so we can safely modify the values.
            this.animationPlayer.GetBoneTransforms().CopyTo(boneTransforms, 0);

            // START MODIFY THE SOLDIER
            float armZrotation = 4.7f;

            // Create rotation matrices for the head and arm bones.
            //Matrix headTransform = Matrix.CreateRotationX(headRotation);
            Matrix armTransform = Matrix.CreateRotationZ(armZrotation);
            Matrix rightArmTransform = Matrix.CreateRotationY(-0.6f);
            Matrix leftArmTransform = Matrix.CreateRotationY(0.3f);
            Matrix leftForeArmTransform = Matrix.CreateRotationX(-1.9f);

            // Modify the transform matrices for the head and upper-left arm bones.
            //int headIndex = skinningData.BoneIndices["Head"];
            int rightUpperArmIndex = skinningData.BoneIndices["R_UpperArm"];
            int rightForeArmIndex = skinningData.BoneIndices["R_Forearm"];
            int leftArmIndex = skinningData.BoneIndices["L_UpperArm"];
            int leftForeArmIndex = skinningData.BoneIndices["L_Forearm"];

            //boneTransforms[headIndex] = headTransform * boneTransforms[headIndex];
            boneTransforms[rightUpperArmIndex] = rightArmTransform * boneTransforms[rightUpperArmIndex];
            boneTransforms[rightForeArmIndex] = armTransform * boneTransforms[rightForeArmIndex];
            boneTransforms[leftArmIndex] = leftArmTransform * armTransform * boneTransforms[leftArmIndex];
            boneTransforms[leftForeArmIndex] = leftForeArmTransform * leftArmTransform * boneTransforms[leftForeArmIndex];

            // Tell the animation player to recompute the world and skin matrices. (Matrix.Identity)
            this.animationPlayer.UpdateWorldTransforms(this.soldierWorld, boneTransforms);
            this.animationPlayer.UpdateSkinTransforms();

            // Update bounding spheres
            UpdateBoundingSpheres();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="soldierWorldMatrix"></param>
        public void UpdateDeadSoldier(GameTime gameTime, Vector3 position, Quaternion rotation)
        {
            // Set the soldierWorldMatrix
            this.soldierWorld = Matrix.Transform(Matrix.Identity, rotation) * Matrix.CreateTranslation(position);

            // Start the correct animation clip.
            if (this.clipName != "Take 002")
            {
                this.clipName = "Take 002";

                this.clip = skinningData.AnimationClips[this.clipName];

                // Start the animation clip
                this.animationPlayer.StartClip(this.clip);
            }

            // Update bone transforms
            if (gameTime == null)
            {
                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(TimeSpan.Zero, true);
            }
            else
            {
                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(gameTime.ElapsedGameTime, true);
            }

            // Copy the transforms into our own array, so we can safely modify the values.
            this.animationPlayer.GetBoneTransforms().CopyTo(boneTransforms, 0);
 
            // Rotate the soldier forward (90 degrees), set up the soldier with 5 points.
            this.soldierWorld = Matrix.CreateRotationX(-MathHelper.PiOver2) * Matrix.CreateTranslation(new Vector3(0,7,0)) * this.soldierWorld;

            // Tell the animation player to recompute the world and skin matrices. (Matrix.Identity)
            this.animationPlayer.UpdateWorldTransforms(this.soldierWorld, boneTransforms);
            this.animationPlayer.UpdateSkinTransforms();

            // Update bounding spheres
            UpdateBoundingSpheres();

        }

        /// <summary>
        /// Updates the boundingSpheres array to match the current animation state.
        /// </summary>
        private void UpdateBoundingSpheres()
        {
            // Look up the current world space bone positions.
            Matrix[] worldTransforms = animationPlayer.GetWorldTransforms();

            for (int i = 0; i < skinnedSpheresXML.Length; i++)
            {
                // Convert the SkinnedSphere description to a BoundingSphere.
                SkinnedSphere source = skinnedSpheresXML[i];
                Vector3 center = new Vector3(source.Offset, 0, 0);
                BoundingSphere sphere = new BoundingSphere(center, source.Radius);

                // Transform the BoundingSphere by its parent bone matrix,
                // and store the result into the boundingSpheres array.
                int boneIndex = skinningData.BoneIndices[source.BoneName];

                boundingSpheres[i] = sphere.Transform(worldTransforms[boneIndex]);
            }
        }

        /// <summary>
        /// Draw a living simple soldier
        /// </summary>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        public void DrawSimpleSoldier(Matrix view, Matrix projection)
        {
            // Get bones
            Matrix[] bones = animationPlayer.GetSkinTransforms();

            // Draw the enemy model.
            foreach (ModelMesh mesh in this.soldierModel.Meshes)
            {
                foreach (SkinnedEffect effect in mesh.Effects)
                {
                    effect.SetBoneTransforms(bones);

                    effect.View = view;
                    effect.Projection = projection;

                    effect.EnableDefaultLighting();

                    effect.SpecularColor = new Vector3(0.25f);
                    effect.SpecularPower = 16;
                }

                mesh.Draw();
            }

            // Draw a weapon
            DrawWeapon(view, projection);

        }

        /// <summary>
        /// Draws the weapon.
        /// </summary>
        private void DrawWeapon(Matrix view, Matrix projection)
        {
            int handIndex = skinningData.BoneIndices["R_Index1"];

            Matrix[] worldTransforms = animationPlayer.GetWorldTransforms();

            // Nudge the weapon over so it appears between the left thumb and index finger. ((-1.3f, 2.1f, 0.1f))
            Matrix batWorldTransform = Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(-MathHelper.Pi / 4) *
                Matrix.CreateScale(0.40f) * Matrix.CreateTranslation(new Vector3(1.5f, -1.1f, 0.9f)) * worldTransforms[handIndex];

            foreach (ModelMesh mesh in this.weapon.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = batWorldTransform;
                    effect.View = view;
                    effect.Projection = projection;

                    effect.EnableDefaultLighting();
                }

                mesh.Draw();
            }
        }

        #endregion
    } // End of class

} // End of namespace
