using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

using Cysharp.Threading.Tasks;

using Proyecto26;

using UnityEngine;
using UnityEngine.SceneManagement;
namespace XenseAR
{
    public class DataLoader
    {
        public bool isEdit;
        public DataLoader()
        {
            isEdit = SceneManager.GetActiveScene().name.Contains("Edit");
        }

        [SerializeField] DataScriptableObject sessionData;

        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        GameObject navigationTargetRoot;
        ARWorldBlockInfo blockRoot;
        GameObject navigationMeshRoot;

        public void Cancel()
        {
            cancellationTokenSource.Cancel();
        }

        public void SetNavigationTargetRoot(GameObject root, ARWorldBlockInfo blockRoot, DataScriptableObject sessionData)
        {
            navigationTargetRoot = root;
            this.blockRoot = blockRoot;
            this.sessionData = sessionData;
        }

        public void SetNavigationMeshRoot(GameObject root, ARWorldBlockInfo blockRoot, DataScriptableObject sessionData)
        {
            navigationMeshRoot = root;
            this.blockRoot = blockRoot;
            this.sessionData = sessionData;
        }

        public void SetAnchoringRoot(DataScriptableObject sessionData)
        {
            this.sessionData = sessionData;
        }

        public void LoadNavigationTargetEdu(Action<bool> onFinishLoadNavigationTarget)
        {
            cancellationTokenSource = new CancellationTokenSource();
            RequestHelper requestHelper = new RequestHelper
            {
                SimpleForm = new Dictionary<string, string>()
            {
                {"type", "GetBlockNavigationTargetData" },
                {"blockID", blockRoot.GetInfo().ID },
            },
            };

            cancellationTokenSource.Token.Register(() =>
            {
                requestHelper.Abort();
            });

            BackendServices.Instance.Post(requestHelper).Then(res =>
            {
                var webresp = JsonUtility.FromJson<BlockNavigationTargetsResponse>(res.Text);

                if (webresp.status != Defines.successCode)
                {
                    Debug.LogError($"Unable to get NavigationTargetData!\n" +
                        $"blockID: {blockRoot.GetInfo().ID}\n" +
                        $"error code: {res.StatusCode}");
                    onFinishLoadNavigationTarget(false);
                    return;
                }

                if (sessionData.SessionData.navigation == null) sessionData.SessionData.navigation = new Navigation();
                sessionData.SessionData.navigation.targets = webresp.data.targets;
                onFinishLoadNavigationTarget(true);
            }).Catch((error) =>
            {
                if (!requestHelper.IsAborted)
                    Debug.LogError($"Unable to get NavigationTargetData!\n" +
                        $"blockID: {blockRoot.GetInfo().ID}\n" +
                        $"error: {error.Message}");
                onFinishLoadNavigationTarget(false);
            });
        }

        public void LoadLocationInfos(Action<bool> onFinishLoadLocationInfos)
        {
            cancellationTokenSource = new CancellationTokenSource();
            RequestHelper requestHelper = new RequestHelper
            {
                SimpleForm = new Dictionary<string, string>()
            {
                {"type", "GetLocationInfomation" },
                {"BlockID", blockRoot.GetInfo().ID },
            },
            };

            cancellationTokenSource.Token.Register(() =>
            {
                requestHelper.Abort();
            });

            BackendServices.Instance.Post(requestHelper).Then(res =>
            {
                var webresp = JsonUtility.FromJson<LocationInfosResponseData>(res.Text);

                if (webresp.status != Defines.successCode)
                {
                    Debug.LogError($"Unable to get LocationInfos!\n" +
                        $"blockID: {blockRoot.GetInfo().ID}\n" +
                        $"error code: {res.StatusCode}");
                    onFinishLoadLocationInfos(false);
                    return;
                }
                onFinishLoadLocationInfos(true);
                if (sessionData.SessionData.navigation == null) sessionData.SessionData.navigation = new Navigation();
                sessionData.SessionData.navigation.locations = webresp.data;
            }).Catch((error) =>
            {
                if (!requestHelper.IsAborted)
                    Debug.LogError($"Unable to get LocationInfos!\n" +
                        $"blockID: {blockRoot.GetInfo().ID}\n" +
                        $"error: {error.Message}");
                onFinishLoadLocationInfos(false);
            });
        }

        public async Task<bool> LoadNavigationMeshEdu(BlockInfo block, Action<NavigationMesh, GameObject> loadNavMeshBundle, Action onFinishLoadNavMesh)
        {
            bool result = false;
            string data = "";
            bool hasResponse = false;
            BackendServices.Instance.Post(new RequestHelper
            {
                SimpleForm = new Dictionary<string, string>()
            {
                {"type", "GetBlockNavigationMeshData" },
                {"blockID", block.ID },
            },
            }).Then(res =>
            {
                data = res.Text;

                OnLoadNavigationMesh(data, block.Name, loadNavMeshBundle, onFinishLoadNavMesh);
                result = true;
                hasResponse = true;
            }).Catch(ex => {
                result = false;
                hasResponse = true;
            });
            await UniTask.WaitUntil(() => hasResponse == true);

            return result;
        }

        public async UniTask<bool> LoadAnchoringFromEdu(string blockID, string worldId, bool spawnTarget = true)
        {
            bool ret = false;
            if (sessionData.SessionData.anchoring.blockId == blockID
                && sessionData.SessionData.anchoring.worldId == worldId)
            {
                if (spawnTarget)
                    OnFinishLoadBlockAnchoringData(blockID);
            }
            else
            {
                bool hasResponse = false;
                BackendServices.Instance.Post(new RequestHelper
                {
                    SimpleForm = new Dictionary<string, string>()
            {
                {"type","GetAnchoringTargetsOfBlockInWorld" },
                {"BlockID", blockID},
                {"WorldID", worldId},
            },
                }).Then(res =>
                {
                    Debug.Log("GetAnchoringTargetsOfBlockInWorld: " + res.Text);
                    var webresp = JsonUtility.FromJson<Defines.BlockAnchoringResponse>(res.Text);
                    sessionData.SessionData.anchoring = webresp.data;
                    if (spawnTarget)
                        OnFinishLoadBlockAnchoringData(blockID);
                    sessionData.SessionData.anchoring.blockId = blockID;
                    sessionData.SessionData.anchoring.worldId = worldId;
                    hasResponse = true;

                });
                await UniTask.WaitUntil(() => hasResponse == true);

            }
            return ret;
        }

        public async UniTask<bool> LoadAnchoringFromEdu(string blockID, string worldId, string groupId, bool spawnTarget = true)
        {
            bool ret = false;
            if (sessionData.SessionData.anchoring.blockId == blockID
                && sessionData.SessionData.anchoring.worldId == worldId)
            {
                if (spawnTarget)
                    OnFinishLoadBlockAnchoringData(blockID);
            }
            else
            {
                bool hasResponse = false;
                BackendServices.Instance.Post(new RequestHelper
                {
                    SimpleForm = new Dictionary<string, string>()
            {
                {"type","GetAnchoringTargetsOfBlockInWorld" },
                {"BlockID", blockID},
                {"WorldID", worldId},
            },
                }).Then(res =>
                {
                    Debug.Log("GetAnchoringTargetsOfBlockInWorld: " + res.Text);
                    var webresp = JsonUtility.FromJson<Defines.BlockAnchoringResponse>(res.Text);
                    List<AnchoringTarget> groupTargets = new List<AnchoringTarget>();
                    foreach (AnchoringTarget act in webresp.data.targets)
                    {
                        if (act.group.Equals(groupId))
                        {
                            groupTargets.Add(act);
                        }
                    }
                    sessionData.SessionData.anchoring.targets = groupTargets;
                    if (spawnTarget)
                        OnFinishLoadBlockAnchoringData(blockID);
                    sessionData.SessionData.anchoring.blockId = blockID;
                    sessionData.SessionData.anchoring.worldId = worldId;
                    hasResponse = true;

                });
                await UniTask.WaitUntil(() => hasResponse == true);

            }
            return ret;
        }

        public async UniTask<bool> LoadAnchoringFromEdu(string blockID)
        {
            bool ret = false;

            // hot fix, need refactor for clean
            if (sessionData.SessionData.anchoring.blockId == blockID)
            {
                OnFinishLoadBlockAnchoringData(blockID);
            }
            else
            {
                bool hasResponse = false;
                BackendServices.Instance.Post(new RequestHelper
                {
                    SimpleForm = new Dictionary<string, string>()
            {
                {"type","GetBlockAnchoringTargetsData" },
                {"BlockID",blockID },

            },
                    //            Uri = "",
                }).Then(res =>
                {
                    var webresp = JsonUtility.FromJson<Defines.BlockAnchoringResponse>(res.Text);
                    sessionData.SessionData.anchoring = webresp.data;
                    OnFinishLoadBlockAnchoringData(blockID);
                    hasResponse = true;
                });
                await UniTask.WaitUntil(() => hasResponse == true);
            }
            return ret;
        }
        private void OnFinishLoadBlockAnchoringData(string blockID)
        {
            SpawnAnchoringTarget(blockID);
        }

        public List<NavigationTargetEditor> InitializeNavigationTarget()
        {
            GameObject navigationTargetBlock = new GameObject(blockRoot.GetInfo().Name);
            navigationTargetBlock.transform.SetParent(navigationTargetRoot.transform);
            navigationTargetBlock.transform.position = blockRoot.transform.position;
            navigationTargetBlock.transform.rotation = blockRoot.transform.rotation;
            navigationTargetBlock.transform.localScale = blockRoot.transform.localScale;

            Transform parentTransform = navigationTargetBlock.transform;

            List<NavigationTargetEditor> targetsList = new List<NavigationTargetEditor>();

            foreach (var navigationTarget in sessionData.SessionData.navigation.targets)
            {
                GameObject targetObject = new GameObject();
                targetObject.transform.parent = parentTransform;
                targetObject.transform.localPosition = navigationTarget.transform.position.ToUnity();
                targetObject.transform.localRotation = navigationTarget.transform.orientation.ToUnity();
                targetObject.transform.localScale = navigationTarget.transform.scale.ToUnity();
                targetObject.name = navigationTarget.name;

                NavigationTargetEditor navEdior = targetObject.AddComponent<NavigationTargetEditor>();

                if (navEdior != null)
                {
                    targetsList.Add(navEdior);
                    navEdior.isNew = false;
                    navEdior.target = navigationTarget;
                    navEdior.blockRoot = blockRoot;
                }
            }

            return targetsList;
        }

        protected void OnLoadNavigationMesh(string data, string blockName, Action<NavigationMesh, GameObject> loadNavMeshBundle, Action onFinishLoadNavMesh)
        {
            var webresp = JsonUtility.FromJson<BlockNavigationMeshResponse>(data);
            if (webresp.status != Defines.successCode) return;

            foreach (Transform NavigationMeshBlock in navigationMeshRoot.transform)
            {
                if (NavigationMeshBlock.gameObject.name == blockName)
                    return;
            }

            GameObject navigationMeshBlock = new GameObject(blockName);
            navigationMeshBlock.transform.SetParent(navigationMeshRoot.transform);
            if (blockRoot)
            {
                navigationMeshBlock.transform.position = blockRoot.transform.position;
                navigationMeshBlock.transform.rotation = blockRoot.transform.rotation;
                navigationMeshBlock.transform.localScale = blockRoot.transform.localScale;
            }

            if (sessionData.SessionData.navigation == null) sessionData.SessionData.navigation = new Navigation();
            sessionData.SessionData.navigation.meshs = webresp.data;

            Transform parentTransform = navigationMeshBlock.transform;

            foreach (var navigationMesh in sessionData.SessionData.navigation.meshs)
            {
                GameObject meshObject = new GameObject();
                meshObject.transform.SetParent(parentTransform);
                meshObject.transform.localPosition = Vector3.zero;
                meshObject.transform.localRotation = Quaternion.identity;
                meshObject.transform.localScale = Vector3.one;
                meshObject.name = navigationMesh.name;

                loadNavMeshBundle(navigationMesh, meshObject);
            }
            onFinishLoadNavMesh?.Invoke();
        }

        public bool SpawnAnchoringTarget(string blockID)
        {
            return SpawnAnchoringTarget(blockID, sessionData.SessionData.anchoring.targets);
        }

        public bool SpawnAnchoringTarget(string blockID, List<AnchoringTarget> targets)
        {
            bool result = false;
            //find parent gameobject

            Transform parentTransform = null;
            var BlockInfos = GameObject.FindObjectsOfType<ARWorldBlockInfo>();
            string activeBlockID = "";
            foreach (ARWorldBlockInfo blContrller in BlockInfos)
            {
                activeBlockID = blContrller.GetInfo().ID;
                if (activeBlockID.Contains(blockID))
                {
                    parentTransform = blContrller.transform;
                }
                else
                {
                    //do nothing
                }
            }
            if (String.IsNullOrEmpty(activeBlockID)) //return result;
            {
                var temprootgo =  new GameObject("TempRootAnchor");
                temprootgo.transform.position =5*Camera.main.transform.forward;
                temprootgo.transform.localScale =  0.0001f*Vector3.one;
                parentTransform =  temprootgo.transform;
                
            }

            foreach (var anchoringTarget in targets)
            {
                IAnchoringLabel foundlabel= LabelManager.Instance.FindById(anchoringTarget.id);
                if(foundlabel != null)
                {
                    foundlabel.LabelObject.transform.parent = parentTransform;
                    foundlabel.UpdateData(anchoringTarget);
                    foundlabel.SetBlockID(activeBlockID);
                    continue;
                }
                GameObject targetObject = new GameObject(anchoringTarget.media.name);
                targetObject.transform.parent = parentTransform;

                IAnchoringLabel label = anchoringTarget.media.Type switch
                {
                    AnchoringMediaType.Gltf => isEdit
                        ? targetObject.AddComponent<GltfAnchorEditor>()
                        : targetObject.AddComponent<GltfAnchor>(),
                    AnchoringMediaType.Image => isEdit
                        ? targetObject.AddComponent<ImageAnchorEditor>()
                        : targetObject.AddComponent<ImageAnchor>(),
                    AnchoringMediaType.Assetbundle => isEdit
                        ? targetObject.AddComponent<AssetbundleAnchorEditor>()
                        : targetObject.AddComponent<AssetbundleAnchor>(),
                    _ => null
                };

                if(isEdit)
                {
                    targetObject.AddComponent<AnchoringDetails>();
                }


                if (label == null) continue;
                LabelManager.Instance.Add(label);
                label.Load(anchoringTarget);
                label.SetBlockID(activeBlockID);
            }
            return result;
        }
    }
}
