using Framework.Entities;
using Framework.InteractionSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Framework.Extensions;
using Framework.Debugging;
using Framework.GuardianSaveSystem;

namespace Framework.ActorComponents
{
    /// <summary>
    /// The action that commands another component to move this gameObject.
    /// </summary>
    public class MoveCommand : CoreAction
    {
        private Vector3 targetPosition; // The target position to move this gameObject to.

        /// <summary>
        /// Initializes a new instance of the <see cref="MoveCommand"/> class.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="targetPosition">The target position.</param>
        public MoveCommand(CoreBehaviour sender, Vector3 targetPosition) : base(sender)
        {
            this.targetPosition = targetPosition;
        }

        /// <summary>
        /// Gets the target position.
        /// </summary>
        /// <returns></returns>
        public Vector3 GetTargetPosition()
        {
            return this.targetPosition;
        }
    }

    /// <summary>
    /// The action that commands another component to make this gameObject jump.
    /// </summary>
    public class JumpCommand : CoreAction
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="JumpCommand"/> class.
        /// </summary>
        /// <param name="sender">The sender.</param>
        public JumpCommand(CoreBehaviour sender) : base(sender) { }
    }

    [AddComponentMenu("Scripts/Framework/Experiments/Actor Components/Player Test Move Logic")]
    public class PlayerTestMoveLogic : CoreBehaviour
    {
        public float timeWarp = 1f;
        public Vector3 inputVector; // The input vector received from a sensor component, mapped on to the forward reference forward, right and up vectors

        private Transform forwardReference; // The reference object used to workout which direction is demeed to be forward (usually by the camera)

        /// <summary>
        /// Called when this component is awoken after the Unity
        /// Awake is called. 
        /// </summary>
        protected override void InnerAwake()
        {
            AddActionHandler<AxisMoveInput>(Move);
            AddActionHandler<JumpInput>(Jump);
            AddActionHandler<ExposeForwardReference>(SetFowardReference);
        }

        /// <summary>
        /// Called when this component is updated after the Unity
        /// Update is called.
        /// </summary>
        protected override void InnerUpdate()
        {
            if (Time.timeScale != timeWarp)
            {
                Time.timeScale = timeWarp;
            }
            SelfInteract(new MoveCommand(this, this.transform.position +
                (this.forwardReference.forward * this.inputVector.x) +
                (this.forwardReference.up * this.inputVector.y) -
                (this.forwardReference.right * this.inputVector.z)));
        }

        /// <summary>
        /// Handles the <see cref="AxisMoveInput"/> action by updating the input vector.
        /// </summary>
        /// <param name="action">The action.</param>
        private void Move(AxisMoveInput action)
        {
            this.inputVector = action.GetInputVector();
        }

        /// <summary>
        /// Handles the <see cref="JumpInput"/> action by calling the <see cref="JumpCommand"/>
        /// action.
        /// </summary>
        /// <param name="action">The action.</param>
        private void Jump(JumpInput action)
        {
            SelfInteract(new JumpCommand(this));
        }

        /// <summary>
        /// Handles the <see cref="ExposeForwardReference"/> by setting the foward reference transform.
        /// </summary>
        /// <param name="action">The action.</param>
        private void SetFowardReference(ExposeForwardReference action)
        {
            this.forwardReference = action.GetReference();
        }

        public ObjectDataMap Save(ObjectDataMap data)
        {
            data.Add("TimeWarp", timeWarp);
            return data;
        }

        public void Load(ObjectDataMap data)
        {
            timeWarp = data.Get<float>("TimeWarp");
        }
    }
}
