using System;
using System.Text;
using System.Threading.Tasks;
using ColdMint.scripts.damage;
using ColdMint.scripts.deathInfo;
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using ColdMint.scripts.pickable;
using Godot;

namespace ColdMint.scripts.character;

/// <summary>
/// <para>玩家角色</para>
/// </summary>
public partial class Player : CharacterTemplate
{
    private Control? _floatLabel;

    //Empty object projectile
    //空的物品抛射线
    private readonly Vector2[] _emptyVector2Array = [Vector2.Zero];

    //抛物线
    private Line2D? _parabola;

    //用于检测玩家是否站在平台上的射线
    private RayCast2D? _platformDetectionRayCast2D;


    private const float PromptTextDistance = 50;


    //抛出物品的飞行速度
    private float _throwingVelocity = Config.CellSize * 13;

    //射线是否与平台碰撞
    private bool _collidingWithPlatform;

    //How long does it take for the character to recover from a collision with the platform after jumping off the platform (in seconds)
    //角色从平台上跳下后，多少时间后恢复与平台的碰撞（单位：秒）
    private double _platformCollisionRecoveryTime = 0.2f;


    public override void _Ready()
    {
        base._Ready();
        CharacterName = TranslationServerUtils.Translate("default_player_name");
        LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat, ReadOnlyCharacterName,
            GlobalPosition);
        var floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn");
        //Initializes the float label.
        //初始化悬浮标签。
        _floatLabel = NodeUtils.InstantiatePackedScene<Control>(floatLabelPackedScene);
        if (_floatLabel == null)
        {
            throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed"));
        }

        _floatLabel.Hide();
        NodeUtils.CallDeferredAddChild(this, _floatLabel);
        _parabola = GetNode<Line2D>("Parabola");
        _platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
        UpdateOperationTip();
        var healthBarUi = GameSceneNodeHolder.HealthBarUi;
        if (healthBarUi != null)
        {
            healthBarUi.MaxHp = MaxHp;
            healthBarUi.CurrentHp = CurrentHp;
        }
    }

    protected override void WhenBindItemContainer(IItemContainer? itemContainer)
    {
        if (itemContainer == null)
        {
            return;
        }

        //Subscribe to events when the item container is bound to the player.
        //在物品容器与玩家绑定时订阅事件。
        itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent;
    }

    public override void _ExitTree()
    {
        base._ExitTree();
        if (ProtectedItemContainer != null)
        {
            //Unsubscribe to events when this object is destroyed.
            //此节点被销毁时，取消订阅事件。
            ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent;
        }
    }

    private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
    {
        var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
        GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
        if (item is Node2D node2D)
        {
            CurrentItem = node2D;
        }
        else
        {
            CurrentItem = null;
        }
    }

    /// <summary>
    /// <para>Update operation prompt</para>
    /// <para>更新操作提示</para>
    /// </summary>
    private void UpdateOperationTip()
    {
        var operationTipLabel = GameSceneNodeHolder.OperationTipLabel;
        if (operationTipLabel == null)
        {
            return;
        }

        var operationTipBuilder = new StringBuilder();

        operationTipBuilder.Append("[color=");
        operationTipBuilder.Append(Config.OperationTipActionColor);
        operationTipBuilder.Append(']');
        operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_left")[0].AsText()));
        operationTipBuilder.Append("[/color]");
        operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_left"));
        operationTipBuilder.Append(' ');
        operationTipBuilder.Append("[color=");
        operationTipBuilder.Append(Config.OperationTipActionColor);
        operationTipBuilder.Append(']');
        operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_right")[0].AsText()));
        operationTipBuilder.Append("[/color]");
        operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_right"));
        operationTipBuilder.Append(' ');
        operationTipBuilder.Append("[color=");
        operationTipBuilder.Append(Config.OperationTipActionColor);
        operationTipBuilder.Append(']');
        operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_up")[0].AsText()));
        operationTipBuilder.Append("[/color]");
        operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump"));
        if (_collidingWithPlatform)
        {
            operationTipBuilder.Append(' ');
            operationTipBuilder.Append("[color=");
            operationTipBuilder.Append(Config.OperationTipActionColor);
            operationTipBuilder.Append(']');
            operationTipBuilder.Append(
                TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_down")[0].AsText()));
            operationTipBuilder.Append("[/color]");
            operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down"));
        }

        var nearestItem = FindTheNearestItem();
        if (nearestItem != null)
        {
            operationTipBuilder.Append(' ');
            operationTipBuilder.Append("[color=");
            operationTipBuilder.Append(Config.OperationTipActionColor);
            operationTipBuilder.Append(']');
            operationTipBuilder.Append(
                TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText()));
            operationTipBuilder.Append("[/color]");
            operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up"));
            if (nearestItem is IItem item)
            {
                operationTipBuilder.Append(item.Name);
            }

            operationTipLabel.Text = operationTipBuilder.ToString();
        }

        if (CurrentItem != null)
        {
            operationTipBuilder.Append(' ');
            operationTipBuilder.Append("[color=");
            operationTipBuilder.Append(Config.OperationTipActionColor);
            operationTipBuilder.Append(']');
            operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("throw")[0].AsText()));
            operationTipBuilder.Append("[/color]");
            operationTipBuilder.Append(TranslationServerUtils.Translate("action_throw"));
            if (CurrentItem is IItem item)
            {
                operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name));
                operationTipBuilder.Append(' ');
                operationTipBuilder.Append("[color=");
                operationTipBuilder.Append(Config.OperationTipActionColor);
                operationTipBuilder.Append(']');
                operationTipBuilder.Append(
                    TranslationServerUtils.Translate(InputMap.ActionGetEvents("use_item")[0].AsText()));
                operationTipBuilder.Append("[/color]");
                operationTipBuilder.Append(TranslationServerUtils.Translate("action_use_item"));
                operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name));
            }
        }

        operationTipLabel.Text = operationTipBuilder.ToString();
    }


    protected override void HookPhysicsProcess(ref Vector2 velocity, double delta)
    {
        //When the collision state between the platform detection ray and the platform changes
        //在平台检测射线与平台碰撞状态改变时
        if (_platformDetectionRayCast2D != null && _platformDetectionRayCast2D.IsColliding() != _collidingWithPlatform)
        {
            //When the state changes, update the action hint
            //当状态改变时，更新操作提示
            _collidingWithPlatform = _platformDetectionRayCast2D.IsColliding();
            UpdateOperationTip();
        }

        //If the character is on the ground, give an upward velocity when the jump button is pressed
        //如果角色正在地面上，按下跳跃键时，给予一个向上的速度
        if (Input.IsActionJustPressed("ui_up") && IsOnFloor())
            velocity.Y = JumpVelocity;

        //Moving left and right
        //左右移动
        var axis = Input.GetAxis("ui_left", "ui_right");
        velocity.X = axis * Speed * Config.CellSize * ProtectedSpeedScale;

        //Use items
        //使用物品
        if (Input.IsActionPressed("use_item"))
        {
            UseItem(GetGlobalMousePosition());
        }

        //Pick up an item
        //捡起物品
        if (Input.IsActionJustPressed("pick_up"))
        {
            var pickAbleItem = FindTheNearestItem();
            var success = PickItem(pickAbleItem);
            if (success)
            {
                if (pickAbleItem != null)
                {
                    PickingRangeBodiesList?.Remove(pickAbleItem);
                }

                RecycleFloatLabel();
            }
        }

        if (Input.IsActionJustPressed("ui_down"))
        {
            if (_collidingWithPlatform)
            {
                //When the character stands on the platform and presses the ui_down key, we cancel the collision between the character and the platform
                //当角色站在平台上按下 ui_down 键时，我们取消角色与平台的碰撞
                var timer = new Timer();
                AddChild(timer);
                timer.WaitTime = _platformCollisionRecoveryTime;
                timer.OneShot = true;
                timer.Start();
                timer.Timeout += () =>
                {
                    SetCollisionMaskValue(Config.LayerNumber.Platform, true);
                    timer.QueueFree();
                };
                SetCollisionMaskValue(Config.LayerNumber.Platform, false);
            }
        }


        //Display a parabola when an item is thrown
        //抛出物品时，显示抛物线
        if (Input.IsActionPressed("throw"))
        {
            if (_parabola == null)
            {
                return;
            }

            if (ItemMarker2D == null)
            {
                //Cannot get the marked location of the item, then do not draw a line
                //无法获取物品的标记位置，那么不绘制线
                return;
            }

            _parabola.Points = CurrentItem == null
                ? _emptyVector2Array
                : ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f);
        }


        //When you raise your hand, throw the object
        //抬起手时，抛出物品
        if (Input.IsActionJustReleased("throw"))
        {
            if (ItemContainer == null)
            {
                return;
            }

            if (_parabola != null)
            {
                _parabola.Points = [Vector2.Zero];
            }

            ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
            GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
            CurrentItem = null;
        }
    }

    protected override void WhenUpdateCurrentItem(Node2D? currentItem)
    {
        UpdateOperationTip();
    }

    /// <summary>
    /// <para>当玩家手动抛出物品时，施加到物品上的速度值</para>
    /// </summary>
    /// <returns></returns>
    private Vector2 GetThrowVelocity()
    {
        //We take the mouse position, normalize it, and then multiply it by the distance the player can throw
        //我们拿到鼠标的位置，将其归一化处理，然后乘以玩家可扔出的距离
        return GetLocalMousePosition().Normalized() * _throwingVelocity;
    }

    public override void _Process(double delta)
    {
        if (!Visible)
        {
            return;
        }

        AimTheCurrentItemAtAPoint(GetGlobalMousePosition());
        var itemMarker2DPosition = Vector2.Zero;
        if (ItemMarker2D != null)
        {
            itemMarker2DPosition = ItemMarker2D.Position;
        }

        var axis = Input.GetAxis("ui_left", "ui_right");
        switch (axis)
        {
            case -1:
                //Minus 1, we move to the left
                //-1，向左移动
                FacingLeft = true;
                if (ItemMarker2D != null)
                {
                    itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX;
                }

                Flip();
                break;
            case 1:
                //1, move to the right
                //1，向右移动
                FacingLeft = false;
                if (ItemMarker2D != null)
                {
                    itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX;
                }

                Flip();
                break;
        }

        if (ItemMarker2D != null)
        {
            ItemMarker2D.Position = itemMarker2DPosition;
        }
    }

    protected override void Flip()
    {
        base.Flip();
        //If there is a weapon, flip it too
        //如果有武器的话，也要翻转
        if (CurrentItem is PickAbleTemplate pickAbleTemplate)
        {
            pickAbleTemplate.Flip(FacingLeft);
        }
    }

    public override void Revive(int newHp)
    {
        base.Revive(newHp);
        var healthBarUi = GameSceneNodeHolder.HealthBarUi;
        if (healthBarUi != null)
        {
            //The purpose of setting Hp to the current Hp is to cause the life bar to refresh.
            //将Hp设置为当前Hp的目的是，使生命条刷新。
            healthBarUi.CurrentHp = CurrentHp;
        }
    }

    /// <summary>
    /// <para>When the player dies</para>
    /// <para>当玩家死亡的时候</para>
    /// </summary>
    /// <param name="damageTemplate"></param>
    protected override async Task OnDie(DamageTemplate damageTemplate)
    {
        Hide();
        ProcessMode = ProcessModeEnum.Disabled;
        if (EventManager.GameOverEvent == null)
        {
            return;
        }

        var gameOverEvent = new GameOverEvent();
        if (damageTemplate.Attacker != null)
        {
            gameOverEvent.DeathInfo = await DeathInfoGenerator.GenerateDeathInfo(this, damageTemplate.Attacker);
        }

        EventManager.GameOverEvent.Invoke(gameOverEvent);
    }

    protected override void EnterThePickingRangeBody(Node node)
    {
        base.EnterThePickingRangeBody(node);
        if (CurrentItem == node)
        {
            //If the node entering the pick range is the node held by the player, then return.
            //如果说进入拾捡范围的节点是玩家所持有的节点，那么返回。
            return;
        }

        if (node is not Node2D node2D)
        {
            return;
        }

        if (_floatLabel != null)
        {
            if (node is not PickAbleTemplate pickAbleTemplate)
            {
                return;
            }

            if (pickAbleTemplate.Picked)
            {
                //If the pickables are picked up, the label is not displayed.
                //如果可拾捡物被捡起了，那么不显示标签。
                LogCat.LogWarning("pickable_picked_up");
                return;
            }

            NodeUtils.CallDeferredReparent(node, _floatLabel);
            var rotationDegreesNode2D = node2D.RotationDegrees;
            var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
            _floatLabel.GlobalPosition = node2D.GlobalPosition;
            _floatLabel.Position = rotationDegreesNode2DAbs > 90
                ? new Vector2(0, PromptTextDistance)
                : new Vector2(0, -PromptTextDistance);
            _floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
            var label = _floatLabel.GetNode<Label>("Label");

            var stringBuilder = new StringBuilder();
            if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate)
            {
                stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
                stringBuilder.Append(TranslationServerUtils.Translate("de"));
            }

            stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name));
            label.Text = stringBuilder.ToString();
            _floatLabel.Show();
        }

        UpdateOperationTip();
    }

    protected override void ExitThePickingRangeBody(Node node)
    {
        base.ExitThePickingRangeBody(node);
        if (node is not Node2D)
        {
            return;
        }

        RecycleFloatLabel();
        UpdateOperationTip();
    }

    /// <summary>
    /// <para>Recycle Float Label</para>
    /// <para>回收悬浮标签</para>
    /// </summary>
    private void RecycleFloatLabel()
    {
        if (_floatLabel == null)
        {
            return;
        }

        _floatLabel.Hide();
        NodeUtils.CallDeferredReparent(this, _floatLabel);
    }

    protected override void OnHit(DamageTemplate damageTemplate)
    {
        base.OnHit(damageTemplate);
        var healthBarUi = GameSceneNodeHolder.HealthBarUi;
        if (healthBarUi != null)
        {
            healthBarUi.CurrentHp = CurrentHp;
        }
    }
}