using Schema;
using UnityEngine;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Events;

namespace VOID.Generic.AI.Schema.Actions
{
    [Description("This unit will move to given point")]
    [Category(SchemaCategory.ThisUnit)]
    public class UnitMoveTo : Action
    {
        private class UnitMoveToMemory
        {
            public SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory> onMoveEnd;
            public bool isMoving;
            public bool isFinished;
        }

        private enum MoveToType
        {
            Position,
            Unit
        }

        [SerializeField] private MoveToType _moveTo;
        [SerializeField] private BlackboardEntrySelector<Transform> _moveToPosition;
        [SerializeField] private BlackboardEntrySelector<UnitObject> _moveToUnit;
        [SerializeField, Space(15)] private BlackboardEntrySelector<float> _range;

        public override void OnNodeEnter(object nodeMemory, SchemaAgent agent)
        {
            var memory = (UnitMoveToMemory)nodeMemory;
            var unitAgent = (UnitObjectSchemaAgent)agent;

            memory.onMoveEnd = new SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory>(
                unitAgent.unit.unitEvents.onMoveEnd, OnMoveEnd, unitAgent, memory);
            memory.onMoveEnd.On();
            memory.isMoving = false;
            memory.isFinished = false;
        }

        public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
        {
            var memory = (UnitMoveToMemory)nodeMemory;
            var unitAgent = (UnitObjectSchemaAgent)agent;

            // moveTo target not defined
            if (_moveTo is MoveToType.Unit && !_moveToUnit.value) return NodeStatus.Failure;
            if (_moveTo is MoveToType.Position && !_moveToPosition.value) return NodeStatus.Failure;

            // unit reached target - end MoveTo with success
            if (memory.isFinished) return NodeStatus.Success;

            // not started moving yet - start moving for this unit
            if (!memory.isMoving)
            {
                memory.isMoving = true;
                memory.isFinished = false;
                unitAgent.unit.OrderStop();
                if (_moveTo is MoveToType.Position)
                    unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToPosition.value.position, _range.value));
                if (_moveTo is MoveToType.Unit)
                    unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToUnit.value, _range.value));
            }

            // If somehow this unit don't have anything in current action (something aborted that??)
            // Then end with failure whole MoveTo
            if (unitAgent.unit.unitActionQueue.actionCurrent == null)
                return NodeStatus.Failure;

            // moving
            return NodeStatus.Running;
        }

        public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
        {
            var memory = (UnitMoveToMemory)nodeMemory;
            memory.onMoveEnd.Off();
        }

        public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
        {
            var memory = (UnitMoveToMemory)nodeMemory;
            memory.onMoveEnd.Off();
        }

        private void OnMoveEnd(MoveEvent moveEvent, UnitMoveToMemory memory, UnitObjectSchemaAgent agent)
        {
            memory.isFinished = true;
        }
    }
}