using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace DXGfxLib
{
    // States
    public enum AvatarState { Moving, Resting };

    /// <summary>
    /// The avatar is a special scene node. It is meant to be used in conjonction with a class implementing
    /// IInputHandler. The input handler will be notified of events and will pass relevant information both to
    /// the camera and the avatar. Each of these could also possibly implement its own logic to react to event.
    /// At the moment this is a bit overengineered as we have only a ThirdPersonCamera and a Base avatar class
    /// but this could change in the future.
    /// </summary>
    public class Avatar
    {
        /// <summary>
        /// The base avatar class will implement a basic move to functionnality.
        /// If you call the MoveTo method on an avatar he will remember its destination and move
        /// to reach that point of 3D space at the below speed.
        /// </summary>
        public float speed = 1.0f;

        /// <summary>
        /// We need to keep track of where we want to go.
        /// </summary>
        public Vector3 destination;

        /// <summary>
        /// Our current state. It is important to know in some function if we are still moving or not
        /// for example. Derived Avatar class might have a lot of additional different state.
        /// </summary>
        public AvatarState currentState = AvatarState.Resting;

        /// <summary>
        /// An Avatar needs to be attached to a sceneObject if you want it to be visible.
        /// </summary>
        public SceneObject associatedSceneObject = null;

        /// <summary>
        /// The default constructor.
        /// </summary>
        private Avatar()
        {
        }

        /// <summary>
        /// We want to be able to build an avatar from a row scene node.
        /// </summary>
        /// <param name="node"></param>
        private Avatar(SceneNode node)
        {
        }

        /// <summary>
        /// We also want to be able to build one from a SceneObject.
        /// </summary>
        /// <param name="sceneObj"></param>
        public Avatar(SceneObject sceneObj)
        {
            associatedSceneObject = sceneObj;
        }

        /// <summary>
        /// Call this method to indicate to the Avatar who want it to move to the specified location.
        /// </summary>
        /// <param name="desiredDestination"></param>
        public void MoveTo(Vector3 desiredDestination)
        {
            destination = desiredDestination;
            currentState = AvatarState.Moving;
        }

        /// <summary>
        /// Well name of the method is quite explicit. No argument needed :).
        /// </summary>
        public void StopMoving()
        {
            currentState = AvatarState.Resting;
        }

        /// <summary>
        /// We use that function to update the Avatar on a regular basis.
        /// In the base class this is only used to move the Avatar at the speed specified by the speed
        /// member variable to the destination indicated by destination member, all this happen if the
        /// currentState of the Avatar is moving. If instead the current state is resting then this
        /// method doesn't perform anything in fact !
        /// The base Avatar class is not so interesting for movements. The Avatar will only go to its
        /// destination whatever it may be. It will attempt to do so by flying !! In fact for any specific
        /// situation this class will most probably be overriden. For example we could have biped, quadriped,
        /// plane, car classes deriving from Avatar ! So on top of moving the Avatar this FrameMove
        /// method could also trigger the appropriate animation.
        /// </summary>
        /// <param name="elapsedTime"></param>
        public void FrameMove(float elapsedTime)
        {
            if (currentState == AvatarState.Moving)
            {

                Vector3 diff = Vector3.Subtract(destination, associatedSceneObject.Position);

                if (diff.Length() < 0.5f)
                {
                    currentState = AvatarState.Resting;
                }
                else
                {
                    diff.Normalize();

                    Matrix translation = Matrix.Translation(speed * diff);

                    associatedSceneObject.ApplyWorldTransform(translation);

                    // Now if a class implementing IPositionConstraint has been set for this scene node
                    // let's use it.
                    if (associatedSceneObject.positionConstraint != null)
                    {
                        Matrix transf = associatedSceneObject.positionConstraint.ValidateOrMove(associatedSceneObject.Position, associatedSceneObject.localBoundingBox);
                        associatedSceneObject.ApplyWorldTransform(transf);
                    }
                }

            }
        }
    }
}
