using System;
using System.Collections.Generic;
using UnityEngine;
using UnityObject = UnityEngine.Object;

namespace UnityEditor.U2D.PSD
{
    internal class PSDGameObjectPreviewData : IDisposable
    {
        static int s_SliderHash = "PSDGameObjectPreviewData_Slider".GetHashCode();
        Texture m_Texture;
        bool m_Disposed;
        PreviewRenderUtility m_RenderUtility;
        Rect m_PreviewRect = new Rect();
        Vector2 m_PreviewDir = Vector2.zero;
        GameObject m_PreviewObject;
        string m_PrefabAssetPath;
        Bounds m_RenderableBounds;
        Vector2 m_GameObjectOffset;
        GameObject m_Root;
        Rect m_DocumentPivot;
        
        public PSDGameObjectPreviewData(GameObject assetPrefab, Rect documentPivot)
        {
            m_RenderUtility = new PreviewRenderUtility();
            m_RenderUtility.camera.fieldOfView = 30f;
            m_Root = new GameObject();
            m_PreviewObject = GameObject.Instantiate(assetPrefab, Vector3.zero, Quaternion.identity);
            m_PreviewObject.transform.parent = m_Root.transform;
            m_DocumentPivot = documentPivot;
            m_RenderUtility.AddSingleGO(m_Root);
        }
        
        static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
        {
            int controlId = GUIUtility.GetControlID(s_SliderHash, FocusType.Passive);
            var current = Event.current;
            switch (current.GetTypeForControl(controlId))
            {
                case UnityEngine.EventType.MouseDown:
                    if (position.Contains(current.mousePosition) && (double)position.width > 50.0)
                    {
                        GUIUtility.hotControl = controlId;
                        current.Use();
                        EditorGUIUtility.SetWantsMouseJumping(1);
                        break;
                    }

                    break;
                case UnityEngine.EventType.MouseUp:
                    if (GUIUtility.hotControl == controlId)
                        GUIUtility.hotControl = 0;
                    EditorGUIUtility.SetWantsMouseJumping(0);
                    break;
                case UnityEngine.EventType.MouseDrag:
                    if (GUIUtility.hotControl == controlId)
                    {
                        scrollPosition -= current.delta * (current.shift ? 3f : 1f) / Mathf.Min(position.width, position.height) * 140f;
                        current.Use();
                        GUI.changed = true;
                        break;
                    }
                    break;
            }
            return scrollPosition;
        }
        
        public void DrawPreview(Rect r, GUIStyle background, Vector2 offset, bool showPivot)
        {
            if (!ShaderUtil.hardwareSupportsRectRenderTexture)
            {
                if (Event.current.type != UnityEngine.EventType.Repaint)
                    return;
                EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f), "Preview requires\nrender texture support");
            }
            else
            {
                
                Vector2 vector2 = Drag2D(m_PreviewDir, r);
                if (vector2 != m_PreviewDir)
                {
                    UnityEngine.Object.DestroyImmediate(m_Texture);
                    m_Texture = null;
                    m_PreviewDir = vector2;
                }


                if (m_GameObjectOffset != offset)
                {
                    UnityEngine.Object.DestroyImmediate(m_Texture);
                    m_Texture = null;
                    m_GameObjectOffset = offset;
                }

                if (Event.current.type != EventType.Repaint)
                    return;

                if (m_PreviewRect != r)
                {
                    UnityEngine.Object.DestroyImmediate(m_Texture);
                    m_Texture = null;
                    m_PreviewRect = r;
                }
                
                if (m_Texture == null)
                {
                    m_PreviewObject.transform.position = Vector2.zero;
                    m_RenderUtility.BeginPreview(r, background);
                    DoRenderPreview();
                    m_Texture = m_RenderUtility.EndPreview();
                }
                
                GUI.DrawTexture(r, m_Texture, ScaleMode.StretchToFill, false);
            }
        }

        void DoRenderPreview()
        {
            m_RenderableBounds = GetRenderableBounds(m_Root);
            float num1 = Mathf.Max(m_RenderableBounds.extents.magnitude, 0.0001f);
            float num2 = num1 * 3.8f;
            Quaternion quaternion = Quaternion.Euler(-m_PreviewDir.y, -m_PreviewDir.x, 0.0f);
            Vector3 vector3 = m_RenderableBounds.center - quaternion * (Vector3.forward * num2);
            m_RenderUtility.camera.transform.position = vector3;
            m_RenderUtility.camera.transform.rotation = quaternion;
            m_RenderUtility.camera.nearClipPlane = num2 - num1 * 1.1f;
            m_RenderUtility.camera.farClipPlane = num2 + num1 * 5.1f;
            m_RenderUtility.lights[0].intensity = 0.7f;
            m_RenderUtility.lights[0].transform.rotation = quaternion * Quaternion.Euler(40f, 40f, 0.0f);
            m_RenderUtility.lights[1].intensity = 0.7f;
            m_RenderUtility.lights[1].transform.rotation = quaternion * Quaternion.Euler(340f, 218f, 177f);
            m_RenderUtility.ambientColor = new Color(0.1f, 0.1f, 0.1f, 0.0f);
            
            m_RenderUtility.Render(true);
        }
        
        public static Bounds GetRenderableBounds(GameObject go)
        {
            Bounds bounds = new Bounds();
            if (go == null)
                return bounds;
            var renderers = new List<Renderer>();
            go.GetComponentsInChildren(renderers);
            foreach (Renderer rendererComponents in renderers)
            {
                if (bounds.extents == Vector3.zero)
                    bounds = rendererComponents.bounds;
                else if(rendererComponents.enabled)
                    bounds.Encapsulate(rendererComponents.bounds);
            }
            return bounds;
        }
        
        public void Dispose()
        {
            if (m_Disposed)
                return;
            m_RenderUtility.Cleanup();
            UnityEngine.Object.DestroyImmediate(m_PreviewObject);
            m_PreviewObject = null;
            m_Disposed = true;
        }
    }

}