using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace AwesomeTechnologies.TouchReact
{
    [Serializable]
    public class CapsuleColliderInfo
    {
        public float Radius;
        public float Height;
        public Mesh CapsuleColliderMesh;
    }

    [Serializable]
    public class TouchColliderInfo
    {
        public Collider Collider;
        public float Scale = 1;
    }

    public enum TouchReactQuality
    {
        Low = 0,
        Normal = 1,
        High = 2
    }

    [HelpURL("http://www.awesometech.no/index.php/home/vegetation-studio/components/touch-bend-system")]
    [ExecuteInEditMode]
    public class TouchReactSystem : MonoBehaviour
    {
        public Vector3 Rotation;
        public static TouchReactSystem Instance;

      //  public int InvisibleLayer = 29;
      
        public Camera TouchReactCamera;
        public Camera SelectedCamera;
        public bool AutoselectCamera = true;
        public float CameraYPosition = -1000f;
        private Material _touchReactMaterial;
        private  Material _touchreactMaterialInstanced;
        private MaterialPropertyBlock _touchreactMaterialPropertyBlock;
        public TouchReactQuality TouchReactQuality = TouchReactQuality.Normal;
        public int OrthographicSize = 50;
        public float Speed = 2f;
        
        public bool ShowDebugColliders;
        public bool HideTouchReactCamera = true;

        private Mesh _sphereColliderMesh;
        private Mesh _boxColliderMesh;

        private RenderTexture _currentTouchRenderTexture;
        private RenderTexture _lastFrameTouchRenderTexture;
        private Material _blendBufferMaterial;
        
        public List<TouchColliderInfo> ColliderList = new List<TouchColliderInfo>();
        public List<MeshFilter> MeshFilterList = new List<MeshFilter>();

        private readonly List<CapsuleColliderInfo> _capsuleColliderMeshList = new List<CapsuleColliderInfo>();

        private Vector2 _uvOffset;
        private static readonly int SpeedID = Shader.PropertyToID("_Speed");
        private static readonly int OffsetVid = Shader.PropertyToID("_offsetV");
        private static readonly int OffsetUid = Shader.PropertyToID("_offsetU");

        private Camera _currentCamera;
        
        // ReSharper disable once UnusedMember.Local
        void Awake()
        {
            if (Instance == null) Instance = this;

            ColliderList.Clear();
            MeshFilterList.Clear();
        }

        // ReSharper disable once UnusedMember.Local
        private void OnEnable()
        {
            CreateColliderPrimitives();
            Init();
        }

        // ReSharper disable once UnusedMember.Local
        private void OnDisable()
        {
            if (TouchReactCamera) TouchReactCamera.enabled = false;

            if (TouchReactCamera)
            {
                TouchReactCamera.targetTexture = null;
                
                if (_currentTouchRenderTexture) DestroyImmediate(_currentTouchRenderTexture);
                if (_lastFrameTouchRenderTexture) DestroyImmediate(_lastFrameTouchRenderTexture);
            }
        }

        private void CreateColliderPrimitives()
        {
            _capsuleColliderMeshList.Clear();

            _sphereColliderMesh = CreateSphereMesh();
            _boxColliderMesh = CreateBoxMesh();
        }

        public int GetTouchReactQualityPixelResolution(TouchReactQuality touchReactQuality)
        {
            switch (touchReactQuality)
            {
                case TouchReactQuality.Low:
                    return 512;
                case TouchReactQuality.Normal:
                    return 1024;
                case TouchReactQuality.High:
                    return 2048;
                default:
                    return 1024;
            }
        }

        public void UpdateCamera()
        {
            if (TouchReactCamera)
            {
               // TouchReactCamera.cullingMask = 1 << InvisibleLayer;
                TouchReactCamera.orthographicSize = OrthographicSize;

                int textureResolution = GetTouchReactQualityPixelResolution(TouchReactQuality);
                if (_currentTouchRenderTexture) DestroyImmediate(_currentTouchRenderTexture);
                if (_lastFrameTouchRenderTexture) DestroyImmediate(_lastFrameTouchRenderTexture);
                
                _currentTouchRenderTexture =
                     new RenderTexture(textureResolution, textureResolution, 24,RenderTextureFormat.RGFloat, RenderTextureReadWrite.Linear)
                     {
                         wrapMode = TextureWrapMode.Clamp,
                         filterMode = FilterMode.Point,
                         autoGenerateMips = false,
                         hideFlags = HideFlags.DontSave
                     };
                
                _lastFrameTouchRenderTexture =
                    new RenderTexture(textureResolution, textureResolution, 24,RenderTextureFormat.RGFloat, RenderTextureReadWrite.Linear)
                    {
                        wrapMode = TextureWrapMode.Clamp,
                        filterMode = FilterMode.Point,
                        autoGenerateMips = false,
                        hideFlags = HideFlags.DontSave
                    };
                
                _blendBufferMaterial = (Material) Resources.Load("BlendTouchReactBuffer", typeof(Material));
                
                //RenderTexture oldRenderTexture = TouchReactCamera.targetTexture;
                
                // RenderTexture rt =
                //     new RenderTexture(textureResolution, textureResolution, 24, RenderTextureFormat.RGFloat, RenderTextureReadWrite.Linear)
                //     {
                //         wrapMode = TextureWrapMode.Clamp,
                //         filterMode = FilterMode.Point,
                //         autoGenerateMips = false,
                //         hideFlags = HideFlags.DontSave
                //     };
                //
                // RenderTexture oldRenderTexture = TouchReactCamera.targetTexture;
                
                
                TouchReactCamera.targetTexture = _currentTouchRenderTexture;

               
            }
        }

        private Mesh GetCapsuleColliderMesh(float radius, float height)
        {
            for (int i = 0; i <= _capsuleColliderMeshList.Count - 1; i++)
                if (Math.Abs(_capsuleColliderMeshList[i].Radius - radius) < 0.01f &&
                    Math.Abs(_capsuleColliderMeshList[i].Height - height) < 0.01f)
                    return _capsuleColliderMeshList[i].CapsuleColliderMesh;

            CapsuleColliderInfo newCapsuleColliderInfo = new CapsuleColliderInfo
            {
                Radius = radius,
                Height = height,
                CapsuleColliderMesh = CreateCapsuleMesh(radius, height)
            };

            _capsuleColliderMeshList.Add(newCapsuleColliderInfo);
            return newCapsuleColliderInfo.CapsuleColliderMesh;
        }

        public static Mesh CreateBoxMesh(float length = 1f,float width = 1f, float height = 1f)
        {
            Mesh mesh = new Mesh();
            mesh.Clear();

            //const float length = 1f;
            //const float width = 1f;
            //const float height = 1f;

            #region Vertices

            Vector3 p0 = new Vector3(-length * .5f, -width * .5f, height * .5f);
            Vector3 p1 = new Vector3(length * .5f, -width * .5f, height * .5f);
            Vector3 p2 = new Vector3(length * .5f, -width * .5f, -height * .5f);
            Vector3 p3 = new Vector3(-length * .5f, -width * .5f, -height * .5f);

            Vector3 p4 = new Vector3(-length * .5f, width * .5f, height * .5f);
            Vector3 p5 = new Vector3(length * .5f, width * .5f, height * .5f);
            Vector3 p6 = new Vector3(length * .5f, width * .5f, -height * .5f);
            Vector3 p7 = new Vector3(-length * .5f, width * .5f, -height * .5f);

            Vector3[] vertices =
            {
                // Bottom
                p0, p1, p2, p3,

                // Left
                p7, p4, p0, p3,

                // Front
                p4, p5, p1, p0,

                // Back
                p6, p7, p3, p2,

                // Right
                p5, p6, p2, p1,

                // Top
                p7, p6, p5, p4
            };

            #endregion

            #region Normales

            Vector3 up = Vector3.up;
            Vector3 down = Vector3.down;
            Vector3 front = Vector3.forward;
            Vector3 back = Vector3.back;
            Vector3 left = Vector3.left;
            Vector3 right = Vector3.right;

            Vector3[] normales =
            {
                // Bottom
                down, down, down, down,

                // Left
                left, left, left, left,

                // Front
                front, front, front, front,

                // Back
                back, back, back, back,

                // Right
                right, right, right, right,

                // Top
                up, up, up, up
            };

            #endregion

            #region UVs

            Vector2 _00 = new Vector2(0f, 0f);
            Vector2 _10 = new Vector2(1f, 0f);
            Vector2 _01 = new Vector2(0f, 1f);
            Vector2 _11 = new Vector2(1f, 1f);

            UnityEngine.Vector2[] uvs =
            {
                // Bottom
                _11, _01, _00, _10,

                // Left
                _11, _01, _00, _10,

                // Front
                _11, _01, _00, _10,

                // Back
                _11, _01, _00, _10,

                // Right
                _11, _01, _00, _10,

                // Top
                _11, _01, _00, _10
            };

            #endregion

            #region Triangles

            int[] triangles =
            {
                // Bottom
                3, 1, 0,
                3, 2, 1,

                // Left
                3 + 4 * 1, 1 + 4 * 1, 0 + 4 * 1,
                3 + 4 * 1, 2 + 4 * 1, 1 + 4 * 1,

                // Front
                3 + 4 * 2, 1 + 4 * 2, 0 + 4 * 2,
                3 + 4 * 2, 2 + 4 * 2, 1 + 4 * 2,

                // Back
                3 + 4 * 3, 1 + 4 * 3, 0 + 4 * 3,
                3 + 4 * 3, 2 + 4 * 3, 1 + 4 * 3,

                // Right
                3 + 4 * 4, 1 + 4 * 4, 0 + 4 * 4,
                3 + 4 * 4, 2 + 4 * 4, 1 + 4 * 4,

                // Top
                3 + 4 * 5, 1 + 4 * 5, 0 + 4 * 5,
                3 + 4 * 5, 2 + 4 * 5, 1 + 4 * 5
            };

            #endregion

            mesh.vertices = vertices;
            mesh.normals = normales;
            mesh.uv = uvs;
            mesh.triangles = triangles;

            mesh.RecalculateBounds();
            return mesh;
        }

        public static Mesh CreateCapsuleMesh(float radius, float height)
        {
            int segments = 24;


            if (segments % 2 != 0)
                segments++;

            // extra vertex on the seam
            int points = segments + 1;

            // calculate points around a circle
            float[] pX = new float[points];
            float[] pZ = new float[points];
            float[] pY = new float[points];
            float[] pR = new float[points];

            float calcH = 0f;
            float calcV = 0f;

            for (int i = 0; i < points; i++)
            {
                pX[i] = Mathf.Sin(calcH * Mathf.Deg2Rad);
                pZ[i] = Mathf.Cos(calcH * Mathf.Deg2Rad);
                pY[i] = Mathf.Cos(calcV * Mathf.Deg2Rad);
                pR[i] = Mathf.Sin(calcV * Mathf.Deg2Rad);

                calcH += 360f / segments;
                calcV += 180f / segments;
            }


            // - Vertices and UVs -

            Vector3[] vertices = new Vector3[points * (points + 1)];
            Vector2[] uvs = new Vector2[vertices.Length];
            int ind = 0;

            // Y-offset is half the height minus the diameter
            float yOff = (height - radius * 2f) * 0.5f;
            if (yOff < 0)
                yOff = 0;

            // uv calculations
            float stepX = 1f / (points - 1);
            float uvX, uvY;

            // Top Hemisphere
            int top = Mathf.CeilToInt(points * 0.5f);

            for (int y = 0; y < top; y++)
            for (int x = 0; x < points; x++)
            {
                vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius;
                vertices[ind].y = yOff + vertices[ind].y;

                uvX = 1f - stepX * x;
                uvY = (vertices[ind].y + height * 0.5f) / height;
                uvs[ind] = new Vector2(uvX, uvY);

                ind++;
            }

            // Bottom Hemisphere
            int btm = Mathf.FloorToInt(points * 0.5f);

            for (int y = btm; y < points; y++)
            for (int x = 0; x < points; x++)
            {
                vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius;
                vertices[ind].y = -yOff + vertices[ind].y;

                uvX = 1f - stepX * x;
                uvY = (vertices[ind].y + height * 0.5f) / height;
                uvs[ind] = new UnityEngine.Vector2(uvX, uvY);

                ind++;
            }


            // - Triangles -

            int[] triangles = new int[segments * (segments + 1) * 2 * 3];

            for (int y = 0, t = 0; y < segments + 1; y++)
            for (int x = 0; x < segments; x++, t += 6)
            {
                triangles[t + 0] = (y + 0) * (segments + 1) + x + 0;
                triangles[t + 1] = (y + 1) * (segments + 1) + x + 0;
                triangles[t + 2] = (y + 1) * (segments + 1) + x + 1;

                triangles[t + 3] = (y + 0) * (segments + 1) + x + 1;
                triangles[t + 4] = (y + 0) * (segments + 1) + x + 0;
                triangles[t + 5] = (y + 1) * (segments + 1) + x + 1;
            }

            Mesh mesh = new Mesh();
            mesh.Clear();

            mesh.name = "ProceduralCapsule";

            mesh.vertices = vertices;
            mesh.uv = uvs;
            mesh.triangles = triangles;

            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
            return mesh;
            //_capsuleColliderMesh = mesh;
        }

        public static Mesh CreateSphereMesh(float radius = 1f)
        {
            Mesh mesh = new Mesh();
            mesh.Clear();

            //float radius = 1f;
            // Longitude |||
            int nbLong = 24;
            // Latitude ---
            int nbLat = 16;

            #region Vertices

            Vector3[] vertices = new Vector3[(nbLong + 1) * nbLat + 2];
            float _pi = Mathf.PI;
            var _2Pi = _pi * 2f;

            vertices[0] = Vector3.up * radius;
            for (int lat = 0; lat < nbLat; lat++)
            {
                float a1 = _pi * (lat + 1) / (nbLat + 1);
                float sin1 = Mathf.Sin(a1);
                float cos1 = Mathf.Cos(a1);

                for (int lon = 0; lon <= nbLong; lon++)
                {
                    float a2 = _2Pi * (lon == nbLong ? 0 : lon) / nbLong;
                    float sin2 = Mathf.Sin(a2);
                    float cos2 = Mathf.Cos(a2);

                    vertices[lon + lat * (nbLong + 1) + 1] = new Vector3(sin1 * cos2, cos1, sin1 * sin2) * radius;
                }
            }
            vertices[vertices.Length - 1] = Vector3.up * -radius;

            #endregion

            #region Normales		

            Vector3[] normales = new Vector3[vertices.Length];
            for (int n = 0; n < vertices.Length; n++)
                normales[n] = vertices[n].normalized;

            #endregion

            #region UVs

            UnityEngine.Vector2[] uvs = new UnityEngine.Vector2[vertices.Length];
            uvs[0] = UnityEngine.Vector2.up;
            uvs[uvs.Length - 1] = UnityEngine.Vector2.zero;
            for (int lat = 0; lat < nbLat; lat++)
            for (int lon = 0; lon <= nbLong; lon++)
                uvs[lon + lat * (nbLong + 1) + 1] =
                    new UnityEngine.Vector2((float) lon / nbLong, 1f - (float) (lat + 1) / (nbLat + 1));

            #endregion

            #region Triangles

            int nbFaces = vertices.Length;
            int nbTriangles = nbFaces * 2;
            int nbIndexes = nbTriangles * 3;
            int[] triangles = new int[nbIndexes];

            //Top Cap
            int i = 0;
            for (int lon = 0; lon < nbLong; lon++)
            {
                triangles[i++] = lon + 2;
                triangles[i++] = lon + 1;
                triangles[i++] = 0;
            }

            //Middle
            for (int lat = 0; lat < nbLat - 1; lat++)
            for (int lon = 0; lon < nbLong; lon++)
            {
                int current = lon + lat * (nbLong + 1) + 1;
                int next = current + nbLong + 1;

                triangles[i++] = current;
                triangles[i++] = current + 1;
                triangles[i++] = next + 1;

                triangles[i++] = current;
                triangles[i++] = next + 1;
                triangles[i++] = next;
            }

            //Bottom Cap
            for (int lon = 0; lon < nbLong; lon++)
            {
                triangles[i++] = vertices.Length - 1;
                triangles[i++] = vertices.Length - (lon + 2) - 1;
                triangles[i++] = vertices.Length - (lon + 1) - 1;
            }

            #endregion

            mesh.vertices = vertices;
            mesh.normals = normales;
            mesh.uv = uvs;
            mesh.triangles = triangles;

            mesh.RecalculateBounds();
            return mesh;
        }
        void Update()
        {
            if (TouchReactCamera && SelectedCamera)
            {
                Vector3 oldCameraPosition = TouchReactCamera.transform.position;
                
                Vector3 tempCameraPosition = GetCameraPosition();
                var pos = tempCameraPosition;

                pos.x = SnapToPixel(pos.x, TouchReactCamera.targetTexture.width, TouchReactCamera.orthographicSize);
                pos.y = 0;
                pos.z = SnapToPixel(pos.z, TouchReactCamera.targetTexture.height, TouchReactCamera.orthographicSize);

                TouchReactCamera.transform.position = pos;

                Vector3 offset = pos - oldCameraPosition;
                _uvOffset = new Vector2(offset.x / (OrthographicSize * 2),offset.z/(OrthographicSize*2));
            }

            PrepareRenderTexture();
            DrawColliders();
            DrawMeshfilters();
            UpdateShaders();
            CompleteRenderTexture();
        }
        
        

        void PrepareRenderTexture()
        {
            RenderTexture tempRenderTexture = _lastFrameTouchRenderTexture;
            _lastFrameTouchRenderTexture = _currentTouchRenderTexture;
            _currentTouchRenderTexture = tempRenderTexture;

            TouchReactCamera.targetTexture = _currentTouchRenderTexture;

            _currentCamera = Camera.current;
            Camera.SetupCurrent(TouchReactCamera);
            
            Graphics.SetRenderTarget(_currentTouchRenderTexture);
            GL.Viewport(new Rect(0, 0, TouchReactCamera.targetTexture.width, TouchReactCamera.targetTexture.height));
            GL.Clear(true, true, TouchReactCamera.backgroundColor, 1f);

            if (_blendBufferMaterial)
            {
                _blendBufferMaterial.SetFloat(OffsetUid,_uvOffset.x);
                _blendBufferMaterial.SetFloat(OffsetVid,-_uvOffset.y);
                _blendBufferMaterial.SetFloat(SpeedID,Speed);
            
                Graphics.Blit(_lastFrameTouchRenderTexture,_currentTouchRenderTexture,_blendBufferMaterial);
            }
           
            GL.PushMatrix(); 
            
            GL.LoadProjectionMatrix(TouchReactCamera.projectionMatrix);
            //Matrix4x4 orthoMatrix = Matrix4x4.Ortho(-OrthographicSize, OrthographicSize, -OrthographicSize, OrthographicSize, -10000, 10000);
            //var projectionMatrix = GL.GetGPUProjectionMatrix(orthoMatrix, true);
            //GL.LoadProjectionMatrix(orthoMatrix);
            GL.PushMatrix();
            _touchReactMaterial.SetPass(0);
        }

        void CompleteRenderTexture()
        {
            GL.PopMatrix();
            GL.PopMatrix();
            Graphics.ClearRandomWriteTargets();
            Camera.SetupCurrent(_currentCamera);
        }

        private void UpdateShaders()
        {
            Shader.SetGlobalTexture("_TouchReact_Buffer", TouchReactCamera.targetTexture);
            Vector4 pos = TouchReactCamera.transform.position;
            pos.z = -pos.z;
            pos.w = TouchReactCamera.orthographicSize * 2;
            pos.x -= TouchReactCamera.orthographicSize;
            pos.z -= TouchReactCamera.orthographicSize;
            Shader.SetGlobalVector("_TouchReact_Pos", pos);
        }

        private float SnapToPixel(float v, int textureSize, float orthoSize)
        {
            float worldPixel = orthoSize * 2 / textureSize;

            v = (int) (v / worldPixel);
            v *= worldPixel;

            return v;
        }

        private void DrawColliders()
        {
                for (int i = 0; i <= ColliderList.Count - 1; i++)
                {
                    Collider tempCollider = ColliderList[i].Collider;
                    if (tempCollider is MeshCollider)
                        DrawMeshCollider(tempCollider as MeshCollider);
                    else if (tempCollider is SphereCollider)
                        DrawSphereCollider(tempCollider as SphereCollider, ColliderList[i].Scale);
                    else if (tempCollider is BoxCollider)
                        DrawBoxCollider(tempCollider as BoxCollider, ColliderList[i].Scale);
                    else if (tempCollider is CapsuleCollider)
                        DrawCapsuleCollider(tempCollider as CapsuleCollider, ColliderList[i].Scale);
                }
        }

        private void DrawMeshfilters()
        {
                for (int i = 0; i <= MeshFilterList.Count - 1; i++)
                    DrawMeshfilter(MeshFilterList[i]);
        }

        private void DrawBoxCollider(BoxCollider boxCollider, float scale)
        {
            Vector3 boxScale = new Vector3(boxCollider.size.x * boxCollider.transform.lossyScale.x,
                boxCollider.size.y * boxCollider.transform.lossyScale.y,
                boxCollider.size.z * boxCollider.transform.lossyScale.z);

            Matrix4x4 positionMatrix = Matrix4x4.TRS(boxCollider.bounds.center, boxCollider.transform.rotation,
                boxScale * scale);
            if (ShowDebugColliders)
                Graphics.DrawMesh(_boxColliderMesh, positionMatrix, _touchReactMaterial, 0, null);
            //Graphics.DrawMesh(_boxColliderMesh, positionMatrix, _touchReactMaterial, InvisibleLayer, TouchReactCamera);
            Graphics.DrawMeshNow(_boxColliderMesh,positionMatrix,0);
        }

        private void DrawCapsuleCollider(CapsuleCollider capsuleCollider, float scale)
        {
            Mesh capsuleMesh = GetCapsuleColliderMesh(capsuleCollider.radius, capsuleCollider.height);

            Vector3 axisRotation = Vector3.zero;
            if (capsuleCollider.direction == 0) axisRotation = new Vector3(0, 0, 90);
            if (capsuleCollider.direction == 2) axisRotation = new Vector3(90, 0, 0);

            Matrix4x4 positionMatrix = Matrix4x4.TRS(capsuleCollider.bounds.center,
                capsuleCollider.transform.rotation * Quaternion.Euler(axisRotation),
                capsuleCollider.transform.lossyScale * scale);

            if (ShowDebugColliders)
                Graphics.DrawMesh(capsuleMesh, positionMatrix, _touchReactMaterial, 0, null);

            //Graphics.DrawMesh(capsuleMesh, positionMatrix, _touchReactMaterial, InvisibleLayer, TouchReactCamera);
            Graphics.DrawMeshNow(capsuleMesh,positionMatrix,0);
        }

        private void DrawSphereCollider(SphereCollider sphereCollider, float scale)
        {
            float sphereScale = GetMaxValue(sphereCollider.transform.lossyScale);
            Vector3 sphereScaleVector = new Vector3(sphereScale, sphereScale, sphereScale);

            Matrix4x4 positionMatrix = Matrix4x4.TRS(sphereCollider.bounds.center, sphereCollider.transform.rotation,
                sphereScaleVector * sphereCollider.radius * scale);

            if (ShowDebugColliders)
                Graphics.DrawMesh(_sphereColliderMesh, positionMatrix, _touchReactMaterial, 0, null);
            //Graphics.DrawMesh(_sphereColliderMesh, positionMatrix, _touchReactMaterial, InvisibleLayer, TouchReactCamera);
            Graphics.DrawMeshNow(_sphereColliderMesh,positionMatrix,0);
        }

        private void DrawMeshCollider(MeshCollider meshCollider)
        {
            Matrix4x4 positionMatrix = Matrix4x4.TRS(meshCollider.transform.position, meshCollider.transform.rotation,
                meshCollider.transform.lossyScale);

            Mesh mesh = meshCollider.sharedMesh;
            if (mesh)
            {
                if (ShowDebugColliders)
                    Graphics.DrawMesh(mesh, positionMatrix, _touchReactMaterial, 0, null);
                
                //Graphics.DrawMesh(mesh, positionMatrix, _touchReactMaterial, InvisibleLayer, TouchReactCamera);
                Graphics.DrawMeshNow(mesh,positionMatrix,0);
            }
        }

        private float GetMaxValue(Vector3 vector)
        {
            float returnValue = float.MinValue;
            if (returnValue < vector.x) returnValue = vector.x;
            if (returnValue < vector.y) returnValue = vector.y;
            if (returnValue < vector.z) returnValue = vector.z;
            return returnValue;
        }

        private void DrawMeshfilter(MeshFilter meshfilter)
        {
            Matrix4x4 positionMatrix = Matrix4x4.TRS(meshfilter.transform.position, meshfilter.transform.rotation,
                meshfilter.transform.lossyScale);

            Mesh mesh = meshfilter.sharedMesh;
            if (mesh)
            {
                if (ShowDebugColliders)
                    Graphics.DrawMesh(mesh, positionMatrix, _touchReactMaterial, 0, null);

                //Graphics.DrawMesh(mesh, positionMatrix, _touchReactMaterial, InvisibleLayer, TouchReactCamera);
                Graphics.DrawMeshNow(mesh,positionMatrix,0);
            }
        }

        public void Init()
        {
            if (TouchReactCamera) TouchReactCamera.enabled = false;

            if (!TouchReactCamera) CreateTouchReactCamera();

            UpdateTouchReactCamera();

            FindCamera();

            SetupMaterial();
            UpdateCamera();
        }


        void FindCamera()
        {
            if (AutoselectCamera) SelectedCamera = Camera.main;
            if (SelectedCamera == TouchReactCamera) SelectedCamera = null;
            
            if (SelectedCamera == null)
            {
                Camera[] cameras = FindObjectsOfType<Camera>();
                for (int i = 0; i <= cameras.Length - 1; i++)
                {
                    if (cameras[i].gameObject.name.Contains("Main Camera") ||
                        cameras[i].gameObject.name.Contains("MainCamera"))
                    {
                        SelectedCamera = cameras[i];
                        break;
                    }
                }
                
                if (SelectedCamera == null)
                {
                    for (int i = 0; i <= cameras.Length - 1; i++)
                    {
                        if (cameras[i] != TouchReactCamera)
                        {
                            SelectedCamera = cameras[i];
                            break;
                        }
                    }
                }
            }
        }
        public void UpdateTouchReactCamera()
        {
            if (TouchReactCamera && HideTouchReactCamera)
            {
                TouchReactCamera.gameObject.hideFlags = HideFlags.HideInHierarchy;
            }
            else
            {
                TouchReactCamera.gameObject.hideFlags = HideFlags.None;
            }
        }

        private void SetupMaterial()
        {
            _touchReactMaterial =
                new Material(Shader.Find("AwesomeTechnologies/TouchReact/RenderTouchBuffer"))
                {
#if UNITY_5_6_OR_NEWER
                    enableInstancing = true
#endif
                };

            _touchreactMaterialInstanced =
                new Material(Shader.Find("AwesomeTechnologies/TouchReact/RenderTouchBufferInstanced"))
                {
#if UNITY_5_6_OR_NEWER
                    enableInstancing = true
#endif
                };


            _touchreactMaterialPropertyBlock = new MaterialPropertyBlock();
        }

        private void CreateTouchReactCamera()
        {
            Transform tourchBendCameraTransform = transform.Find("TouchReactCamera");

            if (!tourchBendCameraTransform)
            {
                GameObject touchReactCameraObject = new GameObject("TouchReactCamera");
                touchReactCameraObject.transform.SetParent(transform, false);
                touchReactCameraObject.transform.position = Vector3.zero;
                touchReactCameraObject.transform.rotation = Quaternion.LookRotation(Vector3.up);

                Camera tempEditorCamera = touchReactCameraObject.AddComponent<Camera>();
                tempEditorCamera.farClipPlane = 10000;
                tempEditorCamera.nearClipPlane = -10000;
                tempEditorCamera.depth = -100;
                tempEditorCamera.clearFlags = CameraClearFlags.Color;
                tempEditorCamera.backgroundColor = Color.black;
                tempEditorCamera.renderingPath = RenderingPath.Forward;
                tempEditorCamera.useOcclusionCulling = true;
                tempEditorCamera.orthographic = true;
                tempEditorCamera.orthographicSize = 50f;
               // tempEditorCamera.cullingMask = 1 << InvisibleLayer;
#if UNITY_5_6_OR_NEWER
                tempEditorCamera.allowMSAA = false;
                tempEditorCamera.allowHDR = false;
#endif
                tempEditorCamera.stereoTargetEye = StereoTargetEyeMask.None;
                TouchReactCamera = tempEditorCamera;                
            }
            else
            {
                TouchReactCamera = tourchBendCameraTransform.gameObject.GetComponent<Camera>();
            }

            TouchReactCamera.enabled = false;
        }

        public Vector3 GetCameraPosition()
        {
            if (Application.isPlaying)
                if (SelectedCamera)
                    return SelectedCamera.transform.position;
                else
                    return Vector3.zero;
#if UNITY_EDITOR
            if (TrSceneViewDetector.GetCurrentSceneViewCamera())
            {
                return TrSceneViewDetector.GetCurrentSceneViewCamera().transform.position;
            }
            //Camera[] sceneviewCameras = SceneView.GetAllSceneCameras();
            //if (sceneviewCameras.Length > 0)
            //    return sceneviewCameras[0].transform.position;
            return Vector3.zero;

#else
  return Vector3.zero;
#endif
        }

        public void InstanceAddCollider(TouchColliderInfo touchColliderInfo)
        {
            if (!ColliderList.Contains(touchColliderInfo)) ColliderList.Add(touchColliderInfo);
        }

        public void InstanceRemoveCollider(TouchColliderInfo touchColliderInfo)
        {
            ColliderList.Remove(touchColliderInfo);
        }

        public void InstanceAddMeshFilter(MeshFilter meshFilter)
        {
            if (!MeshFilterList.Contains(meshFilter)) MeshFilterList.Add(meshFilter);
        }

        public void InstanceDrawMeshInstanced(Mesh mesh, List<Matrix4x4> instanceList, int subMeshIndex)
        {
            if (_touchreactMaterialInstanced)
            {
                if (ShowDebugColliders)
                {
                    Graphics.DrawMeshInstanced(mesh, subMeshIndex, _touchreactMaterialInstanced, instanceList,
                        _touchreactMaterialPropertyBlock, ShadowCastingMode.Off, false, 0, null);
                }

                for (int i = 0; i < instanceList.Count; i++)
                {
                   // Graphics.DrawMeshInstanced(mesh, subMeshIndex, _touchreactMaterialInstanced, instanceList,
                   //     _touchreactMaterialPropertyBlock, ShadowCastingMode.Off, false,InvisibleLayer,TouchReactCamera);
                   Graphics.DrawMeshNow(mesh,instanceList[i],subMeshIndex);
                }
              
            }
        }

        public void InstanceRemoveMeshFilter(MeshFilter meshFilter)
        {
            MeshFilterList.Remove(meshFilter);
        }

        public static void FindInstance()
        {
            Instance = FindObjectOfType<TouchReactSystem>();
        }

        public static void AddCollider(TouchColliderInfo touchColliderInfo)
        {
            if (!Instance) FindInstance();
            if (Instance) Instance.InstanceAddCollider(touchColliderInfo);
        }

        public static void RemoveCollider(TouchColliderInfo touchColliderInfo)
        {
            if (!Instance) FindInstance();
            if (Instance) Instance.InstanceRemoveCollider(touchColliderInfo);
        }

        public static void AddMeshFilter(MeshFilter mesh)
        {
            if (!Instance) FindInstance();
            if (Instance) Instance.InstanceAddMeshFilter(mesh);
        }

        public static void RemoveMeshFilter(MeshFilter mesh)
        {
            if (!Instance) FindInstance();
            if (Instance) Instance.InstanceRemoveMeshFilter(mesh);
        }


        public static bool TouchReactEnabled()
        {
            if (Instance)
            {
                if (Instance.isActiveAndEnabled) return true;                                  
            }

            return false;
        }

        public static void DrawMeshInstanced(Mesh mesh, List<Matrix4x4> instanceList, int subMeshIndex)
        {
            if (Instance)
            {
                if (Instance.isActiveAndEnabled)
                {
                    Instance.InstanceDrawMeshInstanced(mesh, instanceList, subMeshIndex);
                }
            }
        }
    }
}