using System;
using System.Collections.Generic;
using System.Globalization;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
namespace XenseAR
{
    public enum NavigationIconType
    {
        None,
        PathIcon,
        WarningIcon
    }

    public enum DirectionType
    {
        None,
        Up,
        Down
    }

    public class NavigationUIController : MonoBehaviour
    {
        [Header("Turn Notify")]
        [SerializeField] GameObject turnLeft;
        [SerializeField] GameObject turnRight;

        [Header("Navigation UI")]
        [SerializeField] GameObject UIPanel;
        [SerializeField] GameObject navigateIconRoot;
        [SerializeField] TextMeshProUGUI textPanel;

        [Header("Warning Prefab")]
        [SerializeField] GameObject downSlopePrefab;
        [SerializeField] GameObject upSlopePrefab;
        [SerializeField] GameObject downStarePrefab;
        [SerializeField] GameObject upStarePrefab;

        [Header("Navigation Prefab")]
        [SerializeField] GameObject startPositionPrefab;
        [SerializeField] GameObject movePrefab;
        [SerializeField] GameObject moveUpPrefab;
        [SerializeField] GameObject moveDownPrefab;
        [SerializeField] GameObject elevatorPrefab;
        [SerializeField] GameObject destinationPrefab;

        [Header("Route Controller")]
        [SerializeField] ScrollRect routeScrollRect;

        private float scrollLength = 0.1f;
        private float updateIconExtendDistance = 30;
        private float updateIconShortenDistanceRate = 0.5f;
        private float updateIconDistance = 10;
        private float remainDistance = 0f;

        bool updateIconRealtime = false;
        bool disable = false;

        private NavMeshPath navigationPath;
        private NavMeshAgent m_activeAgent;

        public NavigationIconType m_activeIconType = NavigationIconType.None;

        private List<NavigationIconController> navigationIcons = new List<NavigationIconController>();
        private List<NavigationIconController> warningIcons = new List<NavigationIconController>();

        private List<NavigatePathSegment> navigationSegmentDatas = new List<NavigatePathSegment>();


        public NavigationIconType activeIconType
        {
            get => m_activeIconType;
            set
            {
                if (m_activeIconType != value)
                {
                    forceUpdateIcon();
                    ClearNavigateIcon();
                }
                m_activeIconType = value;
            }
        }

        void Start()
        {
            NavigationSystemManager.NavigationStatusChange += NavigationSystemManager_NavigationStatusChange;
        }

        private void OnDestroy()
        {
            NavigationSystemManager.NavigationStatusChange -= NavigationSystemManager_NavigationStatusChange;
        }

        public void ClearNavigateIcon()
        {
            foreach (Transform oldIcon in navigateIconRoot.transform)
            {
                Destroy(oldIcon.gameObject);
            }
        }

        public void UpdateNavigation(NavMeshAgent agent, NavMeshPath path)
        {
            m_activeAgent = agent;
            navigationPath = path;
            remainDistance = 0f;
            if (path != null && path.corners.Length > 1)
            {
                for (int i = 1; i < path.corners.Length; i++)
                {
                    remainDistance += Vector3.Distance(path.corners[i], path.corners[i - 1]);
                }
            }
            TurnSignal();
            SetNavigateIcon(path);
            UpdateDevicePositionStatus();
        }

        private void UpdateDevicePositionStatus()
        {
            float checkLength = 0;
            float totalLength = navigationSegmentDatas[0].distance;
            if (navigationSegmentDatas.Count > 1)
            {
                totalLength = 0;
                for (int i = navigationSegmentDatas.Count - 1; i >= 0; i--)
                {
                    totalLength += navigationSegmentDatas[i].distance;
                    if (checkLength < remainDistance)
                    {
                        if (navigationSegmentDatas[i].type == NavigatePathSegmentType.walk)
                            checkLength += navigationSegmentDatas[i].distance;
                        if (checkLength >= remainDistance)
                        {
                            if (navigationSegmentDatas[i].type == NavigatePathSegmentType.escalatorDown ||
                                navigationSegmentDatas[i].type == NavigatePathSegmentType.escalatorUp)
                            {
                                if (navigationIcons.Count > i - 1 && i > 1) navigationIcons[i - 1].SetStatusIconVisibility(true);
                            }
                            else if (navigationIcons.Count > i && i > 0)
                            {
                                if (!navigationIcons[i].isVisible)
                                {
                                    updateIconRealtime = true;
                                }
                                navigationIcons[i].SetStatusIconVisibility(true);
                                continue;
                            }
                        }
                    }
                    if (navigationIcons.Count > i)
                        if (navigationIcons[i].NavigatePathSegmentData?.type == NavigatePathSegmentType.walk)
                            navigationIcons[i].SetStatusIconVisibility(false);
                }
            }

            if (checkLength < remainDistance)
            {
                if (navigationIcons.Count > 0) navigationIcons[0].SetStatusIconVisibility(true);
                if (remainDistance - totalLength > updateIconExtendDistance ||
                    (remainDistance >= updateIconDistance && remainDistance / totalLength < updateIconShortenDistanceRate))
                {
                    updateIconRealtime = true;
                }
            }

            UpdateWebUI();
        }

        private void UpdateWebUI()
        {
            string navigateData = "{" +
                    $"\\\"remainDistance\\\" : \\\"{remainDistance.ToString()}\\\"," +
                    $"\\\"pause\\\" : \\\"{disable}\\\"," +
                    $"\\\"navigateSegments\\\" : [";

            for (int i = 0; i < navigationSegmentDatas.Count; i++)
            {
                navigateData += $"{{\\\"distance\\\" : \\\"{navigationSegmentDatas[i].distance}\\\"," +
                    $"\\\"type\\\" : \\\"{Enum.GetName(typeof(NavigatePathSegmentType), navigationSegmentDatas[i].type)}\\\"," +
                    $"\\\"start\\\" : \\\"{navigationSegmentDatas[i].startPointObject?.name}\\\"," +
                    $"\\\"end\\\" : \\\"{navigationSegmentDatas[i].endPointObject?.name}\\\"}}";
                if (i != navigationSegmentDatas.Count - 1) navigateData += ",";
            }

            navigateData += "]}";

            API.UpdateNavigate(navigateData);
        }

        public void SetPause(bool isPause)
        {
            if (!isPause)
            {
                SetDisable(false);
            }
        }

        public void SetDisable(bool disable)
        {
            this.disable = disable;
            turnLeft.SetActive(false);
            turnRight.SetActive(false);

            UpdateWebUI();
        }

        public void forceUpdateIcon()
        {
            updateIconRealtime = true;
        }

        private void SetNavigateIcon(NavMeshPath path = null)
        {
            if (path == null && navigationPath == null) return;

            if (navigationSegmentDatas.Count > 0 && !updateIconRealtime)
            {
                return;
            }

            updateIconRealtime = false;

            foreach (Transform oldIcon in navigateIconRoot.transform)
            {
                Destroy(oldIcon.gameObject);
            }

            if (path != null)
            {
                navigationPath = path;
            }

            NavigatePathData navigatePathData = Utility.GetNavigatePathData(navigationPath);
            //Repeate two time
            navigatePathData = Utility.OptimizeNavigatePathData(navigatePathData);
            navigatePathData = Utility.OptimizeNavigatePathData(navigatePathData);

            navigationSegmentDatas.Clear();
            for (int i = 0; i < navigatePathData.segments.Count; i++)
            {
                navigationSegmentDatas.Add(navigatePathData.segments[i]);
            }

            if (navigationPath == null) return;

            navigationIcons.Clear();
            warningIcons.Clear();
            ClearNavigateIcon();

            if (activeIconType == NavigationIconType.None)
            {
                UIPanel.gameObject.SetActive(false);
                return;
            }
            else if (activeIconType == NavigationIconType.PathIcon)
            {
                UIPanel.gameObject.SetActive(true);

                Instantiate(startPositionPrefab, navigateIconRoot.transform);
                for (int i = 0; i < navigationSegmentDatas.Count; i++)
                {
                    AddNavigateIcon(navigationSegmentDatas[i]);
                }
                Instantiate(destinationPrefab, navigateIconRoot.transform);

                foreach (LayoutGroup layoutGroup in GetComponentsInChildren<LayoutGroup>())
                {
                    LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.GetComponent<RectTransform>());
                }

                SetInitStateValue(navigationSegmentDatas.Count);
            }
            else
            {
                NavMeshHit hit;
                if (NavMesh.SamplePosition(m_activeAgent.transform.position, out hit, 4f, NavMesh.AllAreas))
                {
                    if (hit.mask == 0) // not on the NavMesh
                        return;

                    int index = 0;
                    while ((hit.mask & 1) == 0)
                    {
                        hit.mask >>= 1; index++;
                    }
                    //textPanel.text =  "Current area: " + NavMesh.GetAreaNames()[index];
                    AddNavigateIcon(index, navigationPath.corners[1] - navigationPath.corners[0]);
                }
            }
        }


        private void NavigationSystemManager_NavigationStatusChange((NavigationEvent status, Vector3? destination) data)
        {
            switch (data.status)
            {
                //case NavigationEvent.StartNavigate:
                //    break;
                //case NavigationEvent.DestinationChange:
                //    break;
                case NavigationEvent.StopNavigate:
                    UIPanel.gameObject.SetActive(false);
                    break;
                //case NavigationEvent.PauseNavigate:
                //    break;
                //case NavigationEvent.ResumeNavigate:
                //    break;
                //case NavigationEvent.DestinationArrived:
                //    break;
                default:
                    break;
            }
        }


        private void TurnSignal()
        {
            if (disable) return;
            if (navigationPath.corners.Length > 1)
            {
                Vector3[] drawCorners = navigationPath.corners;
                //First Navigation Vector
                Vector3 temp = (drawCorners[1] - drawCorners[0]);
                Vector3 NavVect = temp.normalized;
                //Camera Vector
                temp = Camera.main.transform.forward;
                Vector3 CamVect = temp.normalized;
                //Angle between ARCamera Aim and Navigation Vector
                float angle = Utility.CaculateAngle(NavVect, CamVect).y;

                float disFromCamToConer1 = Vector3.Distance(Camera.main.transform.position, drawCorners[1]);
                if (disFromCamToConer1 < 2)
                {
                    turnLeft.SetActive(false);
                    turnRight.SetActive(false);
                    return;
                }

                if (angle > 35f)
                {
                    if (!turnLeft.activeSelf)
                        turnLeft.SetActive(true);
                    turnRight.SetActive(false);
                }
                else if (angle < -35f)
                {
                    if (!turnRight.activeSelf)
                        turnRight.SetActive(true);
                    turnLeft.SetActive(false);
                }
                else
                {
                    turnLeft.SetActive(false);
                    turnRight.SetActive(false);
                }
            }
        }

        private void AddNavigateIcon(NavigatePathSegment segment)
        {
            GameObject Icon;
            switch (segment.type)
            {
                case NavigatePathSegmentType.walk:
                    Icon = Instantiate(movePrefab, navigateIconRoot.transform);
                    Icon.GetComponentInChildren<NavigationIconController>().SetData(segment);
                    navigationIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    break;
                case NavigatePathSegmentType.escalatorUp:
                    Icon = Instantiate(moveUpPrefab, navigateIconRoot.transform);
                    Icon.GetComponentInChildren<NavigationIconController>().SetData(segment);
                    navigationIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    break;
                case NavigatePathSegmentType.escalatorDown:
                    Icon = Instantiate(moveDownPrefab, navigateIconRoot.transform);
                    Icon.GetComponentInChildren<NavigationIconController>().SetData(segment);
                    navigationIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    break;
                case NavigatePathSegmentType.elevatorUp:
                    Icon = Instantiate(elevatorPrefab, navigateIconRoot.transform);
                    Icon.GetComponentInChildren<NavigationIconController>().SetData(segment);
                    break;
                case NavigatePathSegmentType.elevatorDown:
                    Icon = Instantiate(elevatorPrefab, navigateIconRoot.transform);
                    Icon.GetComponentInChildren<NavigationIconController>().SetData(segment);
                    break;
            }
        }

        private void AddNavigateIcon(int areaIndex, Vector3 direction)
        {
            GameObject Icon;
            if (areaIndex == NavMesh.GetAreaFromName("Stair"))
            {
                UIPanel.gameObject.SetActive(true);
                if (direction.y >= 0)
                {
                    Icon = Instantiate(upStarePrefab, navigateIconRoot.transform);
                    warningIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    textPanel.text = Defines.NavigationUIText.UpStairWarning;
                }
                else
                {
                    Icon = Instantiate(downStarePrefab, navigateIconRoot.transform);
                    warningIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    textPanel.text = Defines.NavigationUIText.DownStairWarning;
                }
            }
            else if (areaIndex == NavMesh.GetAreaFromName("Slope"))
            {
                UIPanel.gameObject.SetActive(true);
                if (direction.y >= 0)
                {
                    Icon = Instantiate(upSlopePrefab, navigateIconRoot.transform);
                    warningIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    textPanel.text = Defines.NavigationUIText.UpSlopeWarning;
                }
                else
                {
                    Icon = Instantiate(downSlopePrefab, navigateIconRoot.transform);
                    warningIcons.Add(Icon.GetComponentInChildren<NavigationIconController>());
                    textPanel.text = Defines.NavigationUIText.DownSlopeWarning;
                }
            }
            else
            {
                UIPanel.gameObject.SetActive(false);
            }
        }

        private void SetInitStateValue(int childCount)
        {
            scrollLength = 1.0f / childCount;
            routeScrollRect.horizontalNormalizedPosition = 0;
        }

        public void ScrollBack()
        {
            if (routeScrollRect.horizontalNormalizedPosition - scrollLength > 0)
            {
                routeScrollRect.horizontalNormalizedPosition -= scrollLength;
            }
            else
            {
                routeScrollRect.horizontalNormalizedPosition = 0;
            }
        }

        public void ScrollForward()
        {
            if (routeScrollRect.horizontalNormalizedPosition + scrollLength < 1)
            {
                routeScrollRect.horizontalNormalizedPosition += scrollLength;
            }
            else
            {
                routeScrollRect.horizontalNormalizedPosition = 1;
            }
        }
    }
}
