using System.Collections.Generic;
using Games.LogicObj;
using Games.Scene;
using GCGame.Table;
using Module.Log;
using UnityEngine;
using UnityEngine.AI;

public class NexusMover
{
    private const float _followTime = 0.1f;
    private readonly Obj_Character _carrier;
    private readonly List<Vector3> _cornerList;
    private readonly List<MoverData> _moverList;
    private readonly NavMeshPath _path;

    public NexusMover(int roleId, Obj_Character carrier)
    {
        _carrier = carrier;
        var movers = TableManager.GetClientPathMoverByID(roleId);
        var corners = TableManager.GetClientPathByID(roleId);
        if (movers == null)
        {
            LogModule.ErrorLog("Cannot find movers for roleId = " + roleId);
        }
        else if (corners == null || corners.Count < 2)
        {
            LogModule.ErrorLog("Cannot find path for roleId = " + roleId);
        }
        else
        {
            _path = new NavMeshPath();
            _moverList = new List<MoverData>(movers.Count);
            for (var i = 0; i < movers.Count; i++)
                _moverList.Add(new MoverData(movers[i]));
            _cornerList = new List<Vector3>(corners.Count);
            for (var i = 0; i < corners.Count; i++)
            {
                var point = new Vector3(corners[i].PosX, 0f, corners[i].PosY) * 0.01f;
                point = ActiveScene.GetTerrainPosition(point);
                _cornerList.Add(point);
            }
        }
    }

    public bool CreateMover()
    {
        var result = _moverList != null;
        if (result)
        {
            var next = GetNextPoint();
            for (var i = _moverList.Count - 1; i >= 0; i--)
            {
                var roleBase = TableManager.GetRoleBaseAttrByID(_moverList[i].data.RoleId);
                if (roleBase == null)
                {
                    Debug.LogError("Cannot find role base by id " + _moverList[i].data.RoleId);
                    _moverList.RemoveAt(i);
                }
                else
                {
                    var position = GetWorldPos(_carrier.transform, next, _moverList[i].data);
                    _moverList[i].CreateMover(roleBase, position);
                }
            }
        }

        return result;
    }

    public void DestroyMover()
    {
        for (var i = 0; i < _moverList.Count; i++)
            if (_moverList[i].mover != null)
                _moverList[i].mover.DestroyObj();
    }

    public void SyncMovers()
    {
        var next = GetNextPoint();
        // 获得每个物体的位置
        for (var i = 0; i < _moverList.Count; i++)
        {
            var target = GetWorldPos(_carrier.transform, next, _moverList[i].data);
            float speed;
            if (NavMesh.CalculatePath(_moverList[i].mover.Position, target, NavMesh.AllAreas, _path))
            {
                var distance = 0f;
                for (var j = 0; j < _path.corners.Length - 1; j++)
                    distance += Vector3.Distance(_path.corners[j], _path.corners[j + 1]);
                speed = distance / _followTime;
            }
            else
            {
                speed = _carrier.MoveSpeed;
            }

            speed = Mathf.Clamp(speed, _moverList[i].data.MinSpeed * 0.01f, _moverList[i].data.MaxSpeed * 0.01f);
            _moverList[i].mover.BaseAttr.MoveSpeed = speed;
            _moverList[i].mover.MoveAsMainPlayer(target);
        }
    }

    private int FixIndex(int sourceIndex)
    {
        if (sourceIndex > _cornerList.Count - 1)
            sourceIndex -= _cornerList.Count;
        if (sourceIndex < 0)
            sourceIndex += _cornerList.Count;
        return sourceIndex;
    }

    public static Vector2 GetCornerPoint(Vector2 p0, Vector2 p1, Vector2 p2, float offset)
    {
        var forward0 = (p1 - p0).normalized;
        var right0 = new Vector2(forward0.y, -forward0.x);
        var forward1 = (p2 - p1).normalized;
        var right1 = new Vector2(forward1.y, -forward1.x);
        var off0 = p0 + right0 * offset;
        var off1 = p1 + right0 * offset;
        var off2 = p1 + right1 * offset;
        var off3 = p2 + right1 * offset;
        var y = ((off0.y - off1.y) * (off3.y - off2.y) * off0.x +
                 (off3.y - off2.y) * (off1.x - off0.x) * off0.y +
                 (off1.y - off0.y) * (off3.y - off2.y) * off2.x +
                 (off2.x - off3.x) * (off1.y - off0.y) * off2.y) /
                ((off1.x - off0.x) * (off3.y - off2.y) + (off0.y - off1.y) * (off3.x - off2.x));
        var x = off2.x + (off3.x - off2.x) * (y - off2.y) / (off3.y - off2.y);
        return new Vector2(x, y);
    }

    private int GetNextPoint()
    {
        var target = _carrier.ServerPos;
        var nextPoint = 0;
        var distance = (_cornerList[nextPoint] - target).sqrMagnitude;
        for (var i = 1; i < _cornerList.Count; i++)
        {
            var newDist = (_cornerList[i] - target).sqrMagnitude;
            if (newDist < distance)
            {
                nextPoint = i;
                distance = newDist;
            }
        }

        if ((_cornerList[nextPoint] - target).sqrMagnitude > 0.1f)
            LogModule.ErrorLog("Next Point has precision problem from " + target + " to " + _cornerList[nextPoint]);
        return nextPoint;
    }

    private Vector3 GetWorldPos(Transform root, int next, Tab_ClientPathMover data)
    {
        var distance = data.PosY * 0.01f;
        var current = root.position;
        var target = new Vector3();
        while (distance > 0f)
        {
            var delta = (_cornerList[next] - current).magnitude;
            if (delta > distance)
            {
                // 试图解决转角平移导致误差的问题
                // 计算平移后直线实际的开始和结束位置
                var start = FixIndex(next - 1);
                var previous = FixIndex(start - 1);
                var nextEnd = FixIndex(next + 1);
                var offset = data.PosX * 0.01f;

                var curForward = (_cornerList[next].RemoveY() - _cornerList[start].RemoveY()).normalized;
                var curRight = new Vector2(curForward.y, -curForward.x);

                var curPoint = (current + distance * (_cornerList[next] - _cornerList[start]).normalized).RemoveY();
                curPoint += curRight * offset;

                // 防止平行线导致NaN
                var preForward = _cornerList[start].RemoveY() - _cornerList[previous].RemoveY();
                var nextForward = _cornerList[nextEnd].RemoveY() - _cornerList[next].RemoveY();
                if (Vector2.Dot(curForward, preForward) < 0.9f)
                {
                    var p0 = GetCornerPoint(_cornerList[previous].RemoveY(),
                        _cornerList[start].RemoveY(),
                        _cornerList[next].RemoveY(),
                        offset);
                    if (Vector2.Dot(curPoint - p0, curForward) < 0f)
                        curPoint = p0;
                }

                if (Vector2.Dot(curForward, nextForward) < 0.9f)
                {
                    var p1 = GetCornerPoint(_cornerList[start].RemoveY(),
                        _cornerList[next].RemoveY(),
                        _cornerList[nextEnd].RemoveY(),
                        offset);
                    if (Vector2.Dot(p1 - curPoint, curForward) < 0f)
                        curPoint = p1;
                }

                target = ActiveScene.GetTerrainPosition(curPoint.InsertY());
                break;
            }

            distance -= delta;
            current = _cornerList[next];
            next = FixIndex(next + 1);
        }

        return target;
    }

    private class MoverData
    {
        public readonly Tab_ClientPathMover data;
        public Obj_Character_Mover mover;

        public MoverData(Tab_ClientPathMover data)
        {
            this.data = data;
        }

        public Obj_Character_Mover CreateMover(Tab_RoleBaseAttr roleBase, Vector3 position)
        {
            if (mover == null)
            {
                var npcData = new Obj_NPC_Init_Data
                {
                    _InitCurHP = 1,
                    _InitMaxHP = 1,
                    m_RoleBaseID = data.RoleId,
                    m_Force = 1,
                    m_fX = position.x,
                    m_fZ = position.z,
                    m_MoveSpeed = roleBase.MoveSpeed * 0.01f
                };
                var npcObj = ResourceManager.InstantiateResource("Prefab/Model/NPCRoot");
                npcObj.name = "Mover_" + roleBase.Id;
                mover = npcObj.EnsureComponent<Obj_Character_Mover>();
                mover.Init(npcData);
            }

            return mover;
        }
    }
}