using System.Collections.Generic;
using _PROJECT.Components.Portals2;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.Rendering;

#if (UNITY_EDITOR)
using static UnityEditor.GameObjectUtility;
#endif

public class PortalScene : MonoBehaviour
{
    private StencilPortal _portal;
    private StencilPortal _targetPortal;
    private PortalScene _targetPortalScene;
    public LayerMask defaultLayer;
    public LayerMask portalLayer;
    public BoxCollider boxCollider;
    private GameObject _skybox;

#if (UNITY_EDITOR)
    public void BuildPortalScene()
    {
        _portal = transform.parent.GetComponentInChildren<StencilPortal>();
        _targetPortal = _portal.targetPortal;
        _targetPortalScene = _targetPortal.portalScene;
        boxCollider = GetComponent<BoxCollider>();
        _skybox = transform.Find("Skybox").gameObject;


        // Make skybox size slightly smaller than collider
        _skybox.transform.localScale = _targetPortalScene.boxCollider.bounds.size * 0.999f;
        _skybox.transform.position =
            _targetPortalScene.boxCollider.bounds.center + _targetPortal.normalVisible.forward * -0.001f;

        // Move skybox to this portal, but mirror the position
        _skybox.transform.position =
            StencilPortal.TransformPositionBetweenPortals(_targetPortal, _portal, _skybox.transform.position);
        _skybox.transform.localRotation =
            StencilPortal.TransformRotationBetweenPortals(_targetPortal, _portal, _skybox.transform.rotation);

        // Get all gameobjects in target portal collider
        var gameObjects = _targetPortalScene.getAllCollisions(defaultLayer);

        // Clone all gameobjects found
        var clonedGameObjects = CloneGameObjects(gameObjects, portalLayer);

        // Add normalVector rotation to portalScene
        transform.rotation = transform.rotation * Quaternion.Inverse(_portal.normalVisible.rotation);
    }

    private List<GameObject> getAllCollisions(LayerMask mask)
    {
        // Cast physics overlapbox
        var colliders = Physics.OverlapBox(boxCollider.bounds.center, boxCollider.bounds.extents,
            boxCollider.transform.rotation, mask);

        // Make list of all gameobjects
        var gameObjects = new List<GameObject>();
        foreach (var collider in colliders)
        {
            if (collider.gameObject.GetComponent<StencilPortal>()) continue;
            if (collider.gameObject.GetComponent<PortalScene>()) continue;
            gameObjects.Add(collider.gameObject);
        }

        // Return list of gameobjects
        return gameObjects;
    }

    private List<GameObject> CloneGameObjects(List<GameObject> gameObjects, LayerMask targetMask)
    {
        // Make list of cloned gameobjects
        var clonedGameObjects = new List<GameObject>();
        foreach (var gameObject in gameObjects)
        {
            // Clone gameobject without static flag
            var clonedGameObject = Instantiate(gameObject, transform);
            clonedGameObject.isStatic = false;

            // Remove all children from cloned gameobject
            foreach (Transform child in clonedGameObject.transform)
            {
                DestroyImmediate(child.gameObject);
            }
            
            var flags = GetStaticEditorFlags(gameObject);
            SetStaticEditorFlags(clonedGameObject, flags);

            // Make sure mesh is not shared
            var meshFilter = clonedGameObject.GetComponent<MeshFilter>();
            if (meshFilter)
            {
                meshFilter.mesh = Instantiate(meshFilter.sharedMesh);
            }

            // Make sure material is not shared
            var originalMeshRenderer = gameObject.GetComponent<MeshRenderer>();
            
            // Destroy cloned object if no mesh renderer is found
            if (!originalMeshRenderer)
            {
                DestroyImmediate(clonedGameObject);
                continue;
            }

            var clonedMeshRenderer = clonedGameObject.GetComponent<MeshRenderer>();
            if (clonedMeshRenderer)
            {
                var materials = new Material[clonedMeshRenderer.sharedMaterials.Length];
                for (var i = 0; i < originalMeshRenderer.sharedMaterials.Length; i++)
                {
                    materials[i] = Instantiate(originalMeshRenderer.sharedMaterials[i]);
                }

                clonedMeshRenderer.materials = materials;
            }

            // Remove collider if exists
            var collider = clonedGameObject.GetComponent<Collider>();
            if (collider)
            {
                DestroyImmediate(collider);
            }

            // Add LightmapDataSync component
            // Check if lightmap data is available
            if (originalMeshRenderer.lightmapIndex >= 0)
            {
                var lightmapDataSync = clonedGameObject.AddComponent<LightmapDataSync>();
                // Set lightmap data
                lightmapDataSync.toCloneRenderer = originalMeshRenderer;
            }

            // Check if blend probes are enabled on original gameobject
            if (originalMeshRenderer.lightProbeUsage == LightProbeUsage.BlendProbes)
            {
                // Use probes from original position
                clonedMeshRenderer.lightProbeUsage = LightProbeUsage.BlendProbes;
                clonedMeshRenderer.probeAnchor = originalMeshRenderer.transform;
            }

            // Set position and rotation of cloned gameobject
            clonedGameObject.transform.position =
                StencilPortal.TransformPositionBetweenPortals(_targetPortal, _portal, gameObject.transform.position);
            
            // Move slightly up
            clonedGameObject.transform.position += _targetPortal.normalVisible.up * 0.002f;
            clonedGameObject.transform.rotation =
                StencilPortal.TransformRotationBetweenPortals(_targetPortal, _portal, gameObject.transform.rotation);

            // Set layer of cloned gameobject to target layer
            clonedGameObject.layer = targetMask.GetFirstLayerIndex();

            // Set parent of cloned gameobject to our portal scene
            clonedGameObject.transform.parent = transform;

            // Add cloned gameobject to list
            clonedGameObjects.Add(clonedGameObject);
        }

        // Return list of cloned gameobjects
        return clonedGameObjects;
    }
#endif
}