using Framework.DataStructures;
using Framework.Entities;
using Framework.GuardianSaveSystem;
using Framework.InteractionSystem;
using Framework.ItemSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Framework.ActorComponents
{
    /// <summary>
    /// The action of updating the axis based move input coming from the user.
    /// </summary>
    public class AxisMoveInput : CoreAction
    {
        private Vector3 inputVector; // The vector of input updated by the three button axis

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

        /// <summary>
        /// Gets the input vector.
        /// </summary>
        /// <returns></returns>
        public Vector3 GetInputVector()
        {
            return this.inputVector;
        }
    }

    /// <summary>
    /// The action of updating the jump input coming from the user.
    /// </summary>
    public class JumpInput : KeyAction
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="JumpInput"/> class.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="keyState">State of the key.</param>
        public JumpInput(CoreBehaviour sender, KeyState keyState) : base(sender, keyState) { }
    }

    /// <summary>
    /// The action of equip an item from an input component.
    /// </summary>
    public class EquipItemInput : ItemTransfer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EquipItemInput"/> class.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="item"></param>
        public EquipItemInput(CoreBehaviour sender, Itemlike item) : base(sender, item) { }
    }

    /// <summary>
    /// The action of unequip an item from an input component.
    /// </summary>
    public class UnequipItemInput : CoreAction
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EquipItemInput"/> class.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="item"></param>
        public UnequipItemInput(CoreBehaviour sender) : base(sender) { }
    }

    /// <summary>
    /// This component converts user input, using the UnityEngine <see cref="Input"/> class,
    /// into interaction system <see cref="CoreAction"/> objects. These actions can then
    /// be received by other parts of the player actor. 
    /// </summary>
    [AddComponentMenu("Scripts/Framework/Actor Components/Player Input")]
    public class PlayerInput : CoreBehaviour, Savable
    {
        private Inventory inventory; // TODO: Remove as testing
        private Vector3 inputVector; // The current input vector for axis movement

        /// <summary>
        /// Called when this component is awoken after the Unity
        /// Awake is called. Should be overriden.
        /// </summary>
        protected override void InnerAwake()
        {
            AddActionHandler<ExposeInventory>(SetInventory);
        }

        /// <summary>
        /// Called when this component is updated after the Unity
        /// Update is called. Checks which keys are being pressed.
        /// </summary>
        protected override void InnerUpdate()
        {
            CheckMovementKeys();   
        }

        /// <summary>
        /// Checks the movement keys to see whether or not any have been pressed
        /// or released this frame. If they have fire the relevant actions.
        /// </summary>
        private void CheckMovementKeys()
        {
            this.inputVector = new Vector3(Input.GetAxis("Move X"), Input.GetAxis("Move Y"), Input.GetAxis("Move Z"));
            SelfInteract(new AxisMoveInput(this, inputVector));

            if(Input.GetButtonDown("Jump"))
            {
                SelfInteract(new JumpInput(this, KeyState.Pressed));
            }

            if (Input.GetKeyDown(KeyCode.E))
            {
                SelfInteract(new EquipItemInput(this, this.inventory.GetItem("Gun")));
            }

            if (Input.GetKeyDown(KeyCode.Q))
            {
                SelfInteract(new UnequipItemInput(this));
            }
        }

        private void SetInventory(ExposeInventory action)
        {
            this.inventory = action.GetInventory();
        }

        public ObjectDataMap Save(ObjectDataMap data)
        {
            data.Add("InputVector", new Vector3Data(inputVector));
            return data;
        }

        public void Load(ObjectDataMap data)
        {
            inputVector = data.Get<Vector3Data>("InputVector").ToVector3();
        }
    }
}
