using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace Anima2D
{
	public class OnionLayer
	{
		GameObject m_PreviewInstance;
		Renderer[] m_Renderers;
		MaterialCache[] m_MaterialCache;
		
		public Renderer[] renderers { get { return m_Renderers; } private set { m_Renderers = value; } }
		public MaterialCache[] materialCache { get { return m_MaterialCache; } private set { m_MaterialCache = value; } }

		public GameObject previewInstance
		{
			get {
				return m_PreviewInstance;
			}

			set {
				if(m_PreviewInstance != value)
				{
					Destroy();
					
					m_PreviewInstance = value;

					if(m_PreviewInstance)
					{
						InitializeRenderers();
					}
				}
			}
		}
		
		public void Destroy()
		{
			if(m_MaterialCache != null)
			{
				foreach(MaterialCache materialCache in m_MaterialCache)
				{
					if(materialCache != null)
					{
						materialCache.Destroy();
					}
				}
			}

			m_PreviewInstance = null;
		}
		
		void InitializeRenderers()
		{
			Renderer[] l_renderers = m_PreviewInstance.GetComponentsInChildren<Renderer>();
			
			List<string> l_sortingLayerNames = new List<string>(l_renderers.Length);
			
			foreach(Renderer l_renderer in l_renderers)
			{
				l_sortingLayerNames.Add(l_renderer.sortingLayerName);
			}
			
			//Find the deepest used layer
			List<string> editorSortingLayers = EditorExtra.GetSortingLayerNames();
			
			int deepestLayerIndex = int.MaxValue;
			
			foreach(string layerName in l_sortingLayerNames)
			{
				int index = editorSortingLayers.IndexOf(layerName);
				
				if(index < deepestLayerIndex)
				{
					deepestLayerIndex = index;
				}
			}
			
			string deepestLayer = "Default";
			
			if(deepestLayerIndex >= 0 && deepestLayerIndex < editorSortingLayers.Count)
			{
				deepestLayer = editorSortingLayers[deepestLayerIndex];
			}
			
			//Sort renderers front to back taking sorting layer and sorting order into account
			//Set deepest layer to all renderers and calculate the sorting order
			List< KeyValuePair<Renderer, double> > l_renderersOrder = new List< KeyValuePair<Renderer, double> >();
			
			for(int i = 0; i < l_renderers.Length; ++i)
			{
				Renderer l_renderer = l_renderers[i];
				int l_sortingOrder = l_renderer.sortingOrder;
				int l_layerIndex = editorSortingLayers.IndexOf(l_renderer.sortingLayerName);
				
				l_renderer.sortingLayerName = deepestLayer;
				l_renderersOrder.Add(new KeyValuePair<Renderer, double>(l_renderer,(l_layerIndex * 2.0) + (l_sortingOrder / (double)32767)));
			}
			
			l_renderersOrder = l_renderersOrder.OrderByDescending( (s) => s.Value ).ToList();
			
			//Store renderers in order
			renderers = l_renderersOrder.ConvertAll( s => s.Key ).ToArray();
			
			//Create temp materials for non sprite renderers
			List<MaterialCache> l_materialCacheList = new List<MaterialCache>();
			
			foreach(Renderer renderer in renderers)
			{
				l_materialCacheList.Add(new MaterialCache(renderer));
			}
			
			materialCache = l_materialCacheList.ToArray();
		}
		
		public void SetOnionLayerDepth(int depth)
		{
			int l_order = 0;
			
			if(renderers != null)
			{
				foreach(Renderer renderer in renderers)
				{
					renderer.sortingOrder = -(depth * renderers.Length * 2) - l_order;
					l_order++;
				}
			}
		}
		
		public void SetFrame(int frame, AnimationClip clip)
		{
			if(m_PreviewInstance && clip)
			{
				AnimationMode.BeginSampling ();
				AnimationMode.SampleAnimationClip(m_PreviewInstance, clip, AnimationWindowExtra.FrameToTime(frame));
				AnimationMode.EndSampling ();
				
				IkUtils.UpdateIK(m_PreviewInstance,"",false);
			}
		}
		
		public void SetColor(Color color)
		{
			if(renderers != null)
			{
				foreach(Renderer renderer in renderers)
				{
					SpriteRenderer spriteRenderer = renderer as SpriteRenderer;
					
					if(spriteRenderer)
					{
						color.a = spriteRenderer.color.a;
						spriteRenderer.color = color;
					}
				}
			}
			
			if(materialCache != null)
			{
				foreach(MaterialCache l_materialCache in materialCache)
				{
					l_materialCache.SetColor(color);
				}
			}
		}
		
		public void SetAlpha(float alpha)
		{
			if(renderers != null)
			{
				foreach(Renderer renderer in renderers)
				{
					SpriteRenderer spriteRenderer = renderer as SpriteRenderer;
					
					if(spriteRenderer)
					{
						Color c = spriteRenderer.color;
						c.a = alpha;
						spriteRenderer.color = c;
					}
				}
			}
			
			if(materialCache != null)
			{
				foreach(MaterialCache l_materialCache in materialCache)
				{
					l_materialCache.SetAlpha(alpha);
				}
			}
		}
	}
}